Issue 18: added Options for commands that support it

git-svn-id: http://jclouds.googlecode.com/svn/trunk@479 3d8758e0-26b5-11de-8745-db77d3ebf521
This commit is contained in:
adrian.f.cole 2009-05-07 16:18:56 +00:00
parent 412fb3a816
commit aeba634379
20 changed files with 1768 additions and 204 deletions

View File

@ -31,6 +31,7 @@ package org.jclouds.http;
public interface HttpConstants {
public static final String CONTENT_LENGTH = "Content-Length";
public static final String CONTENT_TYPE = "Content-Type";
public static final String CONTENT_MD5 = "Content-MD5";
public static final String HOST = "Host";
public static final String DATE = "Date";
public static final String BINARY = "application/octet-stream";

View File

@ -0,0 +1,178 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adrian@jclouds.org>
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
package com.amazon.s3;
import com.google.inject.Guice;
import com.google.inject.Injector;
import org.jclouds.aws.PerformanceTest;
import org.jclouds.aws.s3.DateService;
import org.joda.time.DateTime;
import org.testng.annotations.Test;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.SimpleTimeZone;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
@Test(sequential = true, timeOut = 2 * 60 * 1000, testName = "s3.DateTest", groups = "performance")
public class DateServiceTest extends PerformanceTest {
Injector i = Guice.createInjector();
DateService utils = i.getInstance(DateService.class);
SimpleDateFormat dateParser;
public DateServiceTest() {
this.dateParser = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
this.dateParser.setTimeZone(new SimpleTimeZone(0, "GMT"));
}
Date amazonDateFromString(String toParse) throws ParseException {
return this.dateParser.parse(toParse);
}
private static String toParse = "2009-03-12T02:00:07.000Z";
@Test
public void testParseDateSameAsAmazon() throws ParseException,
ExecutionException, InterruptedException {
Date java = dateParser.parse(toParse);
DateTime joda = utils.dateTimeFromXMLFormat(toParse);
assert java.equals(joda.toDate());
}
@Test
public void testTimeStampDateSameAsAmazon() throws ExecutionException,
InterruptedException {
String java = AWSAuthConnection.httpDate();
String joda = utils.timestampAsHeaderString();
assert java.equals(joda);
}
@Test
public void testToHeaderString() throws ExecutionException,
InterruptedException {
String joda1 = utils.toHeaderString(new DateTime());
String joda = utils.timestampAsHeaderString();
assert joda1.equals(joda);
}
@Test
void testTimeStampSerialResponseTime() throws ExecutionException,
InterruptedException {
for (int i = 0; i < LOOP_COUNT; i++)
utils.timestampAsHeaderString();
}
@Test
void testAmazonTimeStampSerialResponseTime() {
for (int i = 0; i < LOOP_COUNT; i++)
AWSAuthConnection.httpDate();
}
@Test
void testTimeStampParallelResponseTime() throws InterruptedException,
ExecutionException {
CompletionService<Boolean> completer = new ExecutorCompletionService<Boolean>(
exec);
for (int i = 0; i < LOOP_COUNT; i++)
completer.submit(new Callable<Boolean>() {
public Boolean call() throws ExecutionException,
InterruptedException {
utils.timestampAsHeaderString();
return true;
}
});
for (int i = 0; i < LOOP_COUNT; i++)
assert completer.take().get();
}
@Test
void testAmazonTimeStampParallelResponseTime() throws InterruptedException,
ExecutionException {
CompletionService<Boolean> completer = new ExecutorCompletionService<Boolean>(
exec);
for (int i = 0; i < LOOP_COUNT; i++)
completer.submit(new Callable<Boolean>() {
public Boolean call() {
AWSAuthConnection.httpDate();
return true;
}
});
for (int i = 0; i < LOOP_COUNT; i++)
assert completer.take().get();
}
@Test
void testParseDateSerialResponseTime() throws ExecutionException,
InterruptedException {
for (int i = 0; i < LOOP_COUNT; i++)
utils.dateTimeFromXMLFormat(toParse);
}
@Test
void testAmazonParseDateSerialResponseTime() {
for (int i = 0; i < LOOP_COUNT; i++)
AWSAuthConnection.httpDate();
}
@Test
void testParseDateParallelResponseTime() throws InterruptedException,
ExecutionException {
CompletionService<Boolean> completer = new ExecutorCompletionService<Boolean>(
exec);
for (int i = 0; i < LOOP_COUNT; i++)
completer.submit(new Callable<Boolean>() {
public Boolean call() throws ExecutionException,
InterruptedException {
utils.dateTimeFromXMLFormat(toParse);
return true;
}
});
for (int i = 0; i < LOOP_COUNT; i++)
assert completer.take().get();
}
@Test
void testAmazonParseDateParallelResponseTime() throws InterruptedException,
ExecutionException {
CompletionService<Boolean> completer = new ExecutorCompletionService<Boolean>(
exec);
for (int i = 0; i < LOOP_COUNT; i++)
completer.submit(new Callable<Boolean>() {
public Boolean call() {
AWSAuthConnection.httpDate();
return true;
}
});
for (int i = 0; i < LOOP_COUNT; i++)
assert completer.take().get();
}
}

View File

@ -1,155 +0,0 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adrian@jclouds.org>
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
package com.amazon.s3;
import com.google.inject.Guice;
import com.google.inject.Injector;
import org.jclouds.aws.PerformanceTest;
import org.jclouds.aws.s3.DateService;
import org.joda.time.DateTime;
import org.testng.annotations.Test;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.SimpleTimeZone;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
@Test(sequential = true, timeOut = 2 * 60 * 1000, testName = "s3.DateTest", groups = "performance")
public class DateTest extends PerformanceTest {
Injector i = Guice.createInjector();
DateService utils = i.getInstance(DateService.class);
SimpleDateFormat dateParser;
public DateTest() {
this.dateParser = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
this.dateParser.setTimeZone(new SimpleTimeZone(0, "GMT"));
}
Date amazonDateFromString(String toParse) throws ParseException {
return this.dateParser.parse(toParse);
}
private static String toParse = "2009-03-12T02:00:07.000Z";
@Test
public void testParseDateSameAsAmazon() throws ParseException, ExecutionException, InterruptedException {
Date java = dateParser.parse(toParse);
DateTime joda = utils.dateTimeFromXMLFormat(toParse);
assert java.equals(joda.toDate());
}
@Test
public void testTimeStampDateSameAsAmazon() throws ExecutionException, InterruptedException {
String java = AWSAuthConnection.httpDate();
String joda = utils.timestampAsHeaderString();
assert java.equals(joda);
}
@Test
void testTimeStampSerialResponseTime() throws ExecutionException, InterruptedException {
for (int i = 0; i < LOOP_COUNT; i++)
utils.timestampAsHeaderString();
}
@Test
void testAmazonTimeStampSerialResponseTime() {
for (int i = 0; i < LOOP_COUNT; i++)
AWSAuthConnection.httpDate();
}
@Test
void testTimeStampParallelResponseTime() throws InterruptedException, ExecutionException {
CompletionService<Boolean> completer = new ExecutorCompletionService<Boolean>(exec);
for (int i = 0; i < LOOP_COUNT; i++)
completer.submit(new Callable<Boolean>() {
public Boolean call() throws ExecutionException, InterruptedException {
utils.timestampAsHeaderString();
return true;
}
});
for (int i = 0; i < LOOP_COUNT; i++) assert completer.take().get();
}
@Test
void testAmazonTimeStampParallelResponseTime() throws InterruptedException, ExecutionException {
CompletionService<Boolean> completer = new ExecutorCompletionService<Boolean>(exec);
for (int i = 0; i < LOOP_COUNT; i++)
completer.submit(new Callable<Boolean>() {
public Boolean call() {
AWSAuthConnection.httpDate();
return true;
}
});
for (int i = 0; i < LOOP_COUNT; i++) assert completer.take().get();
}
@Test
void testParseDateSerialResponseTime() throws ExecutionException, InterruptedException {
for (int i = 0; i < LOOP_COUNT; i++)
utils.dateTimeFromXMLFormat(toParse);
}
@Test
void testAmazonParseDateSerialResponseTime() {
for (int i = 0; i < LOOP_COUNT; i++)
AWSAuthConnection.httpDate();
}
@Test
void testParseDateParallelResponseTime() throws InterruptedException, ExecutionException {
CompletionService<Boolean> completer = new ExecutorCompletionService<Boolean>(exec);
for (int i = 0; i < LOOP_COUNT; i++)
completer.submit(new Callable<Boolean>() {
public Boolean call() throws ExecutionException, InterruptedException {
utils.dateTimeFromXMLFormat(toParse);
return true;
}
});
for (int i = 0; i < LOOP_COUNT; i++) assert completer.take().get();
}
@Test
void testAmazonParseDateParallelResponseTime() throws InterruptedException, ExecutionException {
CompletionService<Boolean> completer = new ExecutorCompletionService<Boolean>(exec);
for (int i = 0; i < LOOP_COUNT; i++)
completer.submit(new Callable<Boolean>() {
public Boolean call() {
AWSAuthConnection.httpDate();
return true;
}
});
for (int i = 0; i < LOOP_COUNT; i++) assert completer.take().get();
}
}

View File

@ -42,7 +42,10 @@ public class DateService {
}
public String timestampAsHeaderString() {
return headerDateFormat.print(new DateTime(DateTimeZone.UTC));
return toHeaderString(new DateTime());
}
public String toHeaderString(DateTime date) {
return headerDateFormat.print(date.withZone(DateTimeZone.UTC));
}
}

View File

@ -26,7 +26,7 @@ package org.jclouds.aws.s3;
import java.util.List;
import java.util.concurrent.Future;
import org.jclouds.aws.s3.commands.options.CreateBucketOptions;
import org.jclouds.aws.s3.commands.options.PutBucketOptions;
import org.jclouds.aws.s3.domain.S3Bucket;
import org.jclouds.aws.s3.domain.S3Object;
@ -100,10 +100,10 @@ public interface S3Connection {
* @param options
* for creating your bucket
* @return true, if the bucket was created
* @see CreateBucketOptions
* @see PutBucketOptions
*/
Future<Boolean> createBucketIfNotExists(String name,
CreateBucketOptions options);
PutBucketOptions options);
/**
* Deletes the bucket, if it is empty.

View File

@ -23,6 +23,7 @@
*/
package org.jclouds.aws.s3.commands;
import static org.jclouds.aws.s3.commands.options.GetBucketOptions.Builder.maxKeys;
import org.jclouds.http.commands.callables.ReturnTrueIf200;
import com.google.inject.Inject;
@ -34,6 +35,7 @@ public class BucketExists extends S3FutureCommand<Boolean> {
@Inject
public BucketExists(@Named("jclouds.http.address") String amazonHost,
ReturnTrueIf200 callable, @Assisted String s3Bucket) {
super("HEAD", "/", callable, amazonHost, s3Bucket);
super("HEAD", "/" + maxKeys(0).toQueryString(), callable, amazonHost,
s3Bucket);
}
}

View File

@ -23,7 +23,7 @@
*/
package org.jclouds.aws.s3.commands;
import org.jclouds.aws.s3.commands.options.CreateBucketOptions;
import org.jclouds.aws.s3.commands.options.PutBucketOptions;
import org.jclouds.aws.s3.domain.S3Object;
import org.jclouds.aws.s3.xml.S3ParserFactory;
@ -89,10 +89,10 @@ public class S3CommandFactory {
private PutBucketFactoryOptions putBucketFactoryOptions;
public static interface PutBucketFactoryOptions {
PutBucket create(String bucket, CreateBucketOptions options);
PutBucket create(String bucket, PutBucketOptions options);
}
public PutBucket createPutBucket(String bucket, CreateBucketOptions options) {
public PutBucket createPutBucket(String bucket, PutBucketOptions options) {
return putBucketFactoryOptions.create(bucket, options);
}

View File

@ -0,0 +1,293 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adrian@jclouds.org>
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
package org.jclouds.aws.s3.commands.options;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import java.io.UnsupportedEncodingException;
import org.jclouds.aws.s3.DateService;
import org.jclouds.aws.s3.S3Utils;
import org.joda.time.DateTime;
import com.google.common.base.Preconditions;
import com.google.common.collect.Multimap;
/**
* Contains options supported in the REST API for the COPY object operation.
* <p/>
* <h2>Usage</h2> The recommended way to instantiate a CopyObjectOptions object
* is to statically import CopyObjectOptions.Builder.* and invoke a static
* creation method followed by an instance mutator (if needed):
* <p/>
* <code>
* import static org.jclouds.aws.s3.commands.options.CopyObjectOptions.Builder.*
*
* S3Connection connection = // get connection
*
* Multimap<String,String> metadata = HashMultimap.create();
* metadata.put("x-amz-meta-adrian", "foo");
*
* // this will copy the object, provided it wasn't modified since yesterday.
* // it will not use metadata from the source, and instead use what we pass in.
* Future<S3Object.MetaData> object = connection.copyObject("sourceBucket", "objectName",
* "destinationBucket", "destinationName",
* overrideMetadataWith(meta).
* ifUnmodifiedSince(new DateTime().minusDays(1))
* );
* <code>
*
* Description of parameters taken from {@link http://docs.amazonwebservices.com/AmazonS3/2006-03-01/index.html?RESTObjectCOPY.html}
*
* @author Adrian Cole
*
*
*/
public class CopyObjectOptions {
private final static DateService dateService = new DateService();
private String ifModifiedSince;
private String ifUnmodifiedSince;
private String ifMatch;
private String ifNoneMatch;
private Multimap<String, String> metadata;
/**
* For use in the header x-amz-copy-source-if-unmodified-since
* <p />
* Copies the object if it hasn't been modified since the specified time;
* otherwise returns a 412 (precondition failed).
* <p />
* This header can be used with x-amz-copy-source-if-match, but cannot be
* used with other conditional copy headers.
*
* @return valid HTTP date (go to http://rfc.net/rfc2616.html#s3.3).
* @see CopyObjectOptions#ifSourceModifiedSince(DateTime)
*/
public String getIfModifiedSince() {
return ifModifiedSince;
}
/**
* For use in the header x-amz-copy-source-if-modified-since
* <p />
* Copies the object if it has been modified since the specified time;
* otherwise returns a 412 (failed condition).
* <p/>
* This header can be used with x-amz-copy-source-if-none-match, but cannot
* be used with other conditional copy headers.
*
* @return valid HTTP date (go to http://rfc.net/rfc2616.html#s3.3).
*
* @see CopyObjectOptions#ifSourceUnmodifiedSince(DateTime)
*/
public String getIfUnmodifiedSince() {
return ifUnmodifiedSince;
}
/**
* For use in the request header: x-amz-copy-source-if-match
* <p />
* Copies the object if its entity tag (ETag) matches the specified tag;
* otherwise return a 412 (precondition failed).
* <p/>
* This header can be used with x-amz-copy-source-if-unmodified-since, but
* cannot be used with other conditional copy headers.
*
* @see CopyObjectOptions#ifSourceMd5Matches(String)
*/
public String getIfMatch() {
return ifMatch;
}
/**
* For use in the request header: x-amz-copy-source-if-none-match
* <p />
* Copies the object if its entity tag (ETag) is different than the
* specified Etag; otherwise returns a 412 (failed condition).
* <p/>
* This header can be used with x-amz-copy-source-if-modified-since, but
* cannot be used with other conditional copy headers.
*
* @see CopyObjectOptions#ifSourceMd5DoesntMatch(String)
*/
public String getIfNoneMatch() {
return ifNoneMatch;
}
/**
* When not null, contains the header
* [x-amz-copy-source-if-unmodified-since] -> [REPLACE] and metadata headers
* passed in from the users.
*
* @see #overrideMetadataWith(Multimap)
*/
public Multimap<String, String> getMetadata() {
return metadata;
}
/**
* Only return the object if it has changed since this time.
* <p />
* Not compatible with {@link #ifSourceMd5Matches(byte[])} or
* {@link #ifSourceUnmodifiedSince(DateTime)}
*/
public CopyObjectOptions ifSourceModifiedSince(DateTime ifModifiedSince) {
checkState(ifMatch == null,
"ifMd5Matches() is not compatible with ifModifiedSince()");
checkState(ifUnmodifiedSince == null,
"ifUnmodifiedSince() is not compatible with ifModifiedSince()");
this.ifModifiedSince = dateService.toHeaderString(checkNotNull(
ifModifiedSince, "ifModifiedSince"));
return this;
}
/**
* Only return the object if it hasn't changed since this time.
* <p />
* Not compatible with {@link #ifSourceMd5DoesntMatch(byte[])} or
* {@link #ifSourceModifiedSince(DateTime)}
*/
public CopyObjectOptions ifSourceUnmodifiedSince(DateTime ifUnmodifiedSince) {
checkState(ifNoneMatch == null,
"ifMd5DoesntMatch() is not compatible with ifUnmodifiedSince()");
checkState(ifModifiedSince == null,
"ifModifiedSince() is not compatible with ifUnmodifiedSince()");
this.ifUnmodifiedSince = dateService.toHeaderString(checkNotNull(
ifUnmodifiedSince, "ifUnmodifiedSince"));
return this;
}
/**
* The object's md5 hash should match the parameter <code>md5</code>.
*
* <p />
* Not compatible with {@link #ifSourceMd5DoesntMatch(byte[])} or
* {@link #ifSourceModifiedSince(DateTime)}
*
* @param md5
* hash representing the entity
* @throws UnsupportedEncodingException
* if there was a problem converting this into an S3 eTag string
*/
public CopyObjectOptions ifSourceMd5Matches(byte[] md5)
throws UnsupportedEncodingException {
checkState(ifNoneMatch == null,
"ifMd5DoesntMatch() is not compatible with ifMd5Matches()");
checkState(ifModifiedSince == null,
"ifModifiedSince() is not compatible with ifMd5Matches()");
this.ifMatch = String.format("\"%1s\"", S3Utils
.toHexString(checkNotNull(md5, "md5")));
return this;
}
/**
* The object should not have a md5 hash corresponding with the parameter
* <code>md5</code>.
* <p />
* Not compatible with {@link #ifSourceMd5Matches(byte[])} or
* {@link #ifSourceUnmodifiedSince(DateTime)}
*
* @param md5
* hash representing the entity
* @throws UnsupportedEncodingException
* if there was a problem converting this into an S3 eTag string
*/
public CopyObjectOptions ifSourceMd5DoesntMatch(byte[] md5)
throws UnsupportedEncodingException {
checkState(ifMatch == null,
"ifMd5Matches() is not compatible with ifMd5DoesntMatch()");
Preconditions
.checkState(ifUnmodifiedSince == null,
"ifUnmodifiedSince() is not compatible with ifMd5DoesntMatch()");
this.ifNoneMatch = String.format("\"%1s\"", S3Utils
.toHexString(checkNotNull(md5, "ifMd5DoesntMatch")));
return this;
}
/**
* Use the provided metadata instead of what is on the source object.
*/
public CopyObjectOptions overrideMetadataWith(
Multimap<String, String> metadata) {
checkNotNull(metadata, "metadata");
for (String header : metadata.keySet()) {
checkArgument(header.startsWith("x-amz-meta-"),
"Metadata keys must start with x-amz-meta-");
}
metadata.put("x-amz-copy-source-if-unmodified-since", "REPLACE");
this.metadata = metadata;
return this;
}
public static class Builder {
/**
* @see CopyObjectOptions#getIfModifiedSince()
*/
public static CopyObjectOptions ifSourceModifiedSince(
DateTime ifModifiedSince) {
CopyObjectOptions options = new CopyObjectOptions();
return options.ifSourceModifiedSince(ifModifiedSince);
}
/**
* @see CopyObjectOptions#ifSourceUnmodifiedSince(DateTime)
*/
public static CopyObjectOptions ifSourceUnmodifiedSince(
DateTime ifUnmodifiedSince) {
CopyObjectOptions options = new CopyObjectOptions();
return options.ifSourceUnmodifiedSince(ifUnmodifiedSince);
}
/**
* @see CopyObjectOptions#ifSourceMd5Matches(byte[])
*/
public static CopyObjectOptions ifSourceMd5Matches(byte[] md5)
throws UnsupportedEncodingException {
CopyObjectOptions options = new CopyObjectOptions();
return options.ifSourceMd5Matches(md5);
}
/**
* @see CopyObjectOptions#ifSourceMd5DoesntMatch(byte[])
*/
public static CopyObjectOptions ifSourceMd5DoesntMatch(byte[] md5)
throws UnsupportedEncodingException {
CopyObjectOptions options = new CopyObjectOptions();
return options.ifSourceMd5DoesntMatch(md5);
}
/**
* @see #overrideMetadataWith(Multimap)
*/
public static CopyObjectOptions overrideMetadataWith(
Multimap<String, String> metadata) {
CopyObjectOptions options = new CopyObjectOptions();
return options.overrideMetadataWith(metadata);
}
}
}

View File

@ -0,0 +1,183 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adrian@jclouds.org>
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
package org.jclouds.aws.s3.commands.options;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
/**
* Contains options supported in the REST API for the GET bucket operation. <h2>
* Usage</h2> The recommended way to instantiate a GetBucketOptions object is to
* statically import GetBucketOptions.Builder.* and invoke a static creation
* method followed by an instance mutator (if needed):
* <p/>
* <code>
* import static org.jclouds.aws.s3.commands.options.GetBucketOptions.Builder.*
*
* S3Connection connection = // get connection
* Future<S3Bucket> bucket = connection.getBucket("bucketName",prefix("/home/users").maxKeys(1000));
* <code>
*
* Description of parameters taken from {@link http://docs.amazonwebservices.com/AmazonS3/2006-03-01/index.html?RESTBucketGET.html}
*
* @author Adrian Cole
*
*
*/
public class GetBucketOptions {
private Map<String, String> options = new HashMap<String, String>();
/**
* Builds a query string, ex. ?marker=toast
*
* @return an http query string representing these options, or empty string
* if none are present.
*/
public String toQueryString() {
StringBuilder builder = new StringBuilder("");
if (options.size() > 0) {
builder.append("?");
for (Iterator<Entry<String, String>> i = options.entrySet()
.iterator(); i.hasNext();) {
Entry<String, String> entry = i.next();
builder.append(entry.getKey()).append("=").append(
entry.getValue());
if (i.hasNext())
builder.append("&");
}
}
return builder.toString();
}
/**
* Limits the response to keys which begin with the indicated prefix. You
* can use prefixes to separate a bucket into different sets of keys in a
* way similar to how a file system uses folders.
*/
public GetBucketOptions prefix(String prefix) {
options.put("prefix", checkNotNull(prefix, "prefix"));
return this;
}
/**
* @see GetBucketOptions#prefix(String)
*/
public String getPrefix() {
return options.get("prefix");
}
/**
* Indicates where in the bucket to begin listing. The list will only
* include keys that occur lexicographically after marker. This is
* convenient for pagination: To get the next page of results use the last
* key of the current page as the marker.
*/
public GetBucketOptions marker(String marker) {
options.put("marker", checkNotNull(marker, "marker"));
return this;
}
/**
* @see GetBucketOptions#marker(String)
*/
public String getMarker() {
return options.get("marker");
}
/**
* The maximum number of keys you'd like to see in the response body. The
* server might return fewer than this many keys, but will not return more.
*/
public GetBucketOptions maxKeys(long maxKeys) {
checkState(maxKeys >= 0, "maxKeys must be >= 0");
options.put("max-keys", Long.toString(maxKeys));
return this;
}
/**
* @see GetBucketOptions#maxKeys(String)
*/
public String getMaxKeys() {
return options.get("max-keys");
}
/**
* Causes keys that contain the same string between the prefix and the first
* occurrence of the delimiter to be rolled up into a single result element
* in the CommonPrefixes collection. These rolled-up keys are not returned
* elsewhere in the response.
*/
public GetBucketOptions delimiter(String delimiter) {
options.put("delimiter", checkNotNull(delimiter, "delimiter"));
return this;
}
/**
* @see GetBucketOptions#delimiter(String)
*/
public String getDelimiter() {
return options.get("delimiter");
}
public static class Builder {
/**
* @see GetBucketOptions#prefix
*/
public static GetBucketOptions prefix(String prefix) {
GetBucketOptions options = new GetBucketOptions();
return options.prefix(prefix);
}
/**
* @see GetBucketOptions#marker
*/
public static GetBucketOptions marker(String marker) {
GetBucketOptions options = new GetBucketOptions();
return options.marker(marker);
}
/**
* @see GetBucketOptions#maxKeys
*/
public static GetBucketOptions maxKeys(long maxKeys) {
GetBucketOptions options = new GetBucketOptions();
return options.maxKeys(maxKeys);
}
/**
* @see GetBucketOptions#delimiter
*/
public static GetBucketOptions delimiter(String delimiter) {
GetBucketOptions options = new GetBucketOptions();
return options.delimiter(delimiter);
}
}
}

View File

@ -0,0 +1,257 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adrian@jclouds.org>
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
package org.jclouds.aws.s3.commands.options;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import java.io.UnsupportedEncodingException;
import org.jclouds.aws.s3.DateService;
import org.jclouds.aws.s3.S3Utils;
import org.joda.time.DateTime;
/**
* Contains options supported in the REST API for the GET object operation. <h2>
* Usage</h2> The recommended way to instantiate a GetObjectOptions object is to
* statically import GetObjectOptions.Builder.* and invoke a static creation
* method followed by an instance mutator (if needed):
* <p/>
* <code>
* import static org.jclouds.aws.s3.commands.options.GetObjectOptions.Builder.*
*
* S3Connection connection = // get connection
*
* // this will get the first megabyte of an object, provided it wasn't modified since yesterday
* Future<S3Object> object = connection.getObject("bucket","objectName",range(0,1024).ifUnmodifiedSince(new DateTime().minusDays(1)));
* <code>
*
* Description of parameters taken from {@link http://docs.amazonwebservices.com/AmazonS3/2006-03-01/index.html?RESTObjectGET.html}
*
* @author Adrian Cole
*
*
*/
public class GetObjectOptions {
private final static DateService dateService = new DateService();
private String range;
private String ifModifiedSince;
private String ifUnmodifiedSince;
private String ifMatch;
private String ifNoneMatch;
/**
* Only download the specified range of the object.
*/
public GetObjectOptions range(long start, long end) {
checkState(start >= 0, "start must be >= 0");
checkState(end >= 0, "end must be >= 0");
this.range = String.format("bytes=%1d-%2d", start, end);
return this;
}
/**
* For use in the header Range
* <p />
*
* @see GetObjectOptions#range(long, long)
*/
public String getRange() {
return range;
}
/**
* Only return the object if it has changed since this time.
* <p />
* Not compatible with {@link #ifMd5Matches(byte[])} or
* {@link #ifUnmodifiedSince(DateTime)}
*/
public GetObjectOptions ifModifiedSince(DateTime ifModifiedSince) {
checkState(ifMatch == null,
"ifMd5Matches() is not compatible with ifModifiedSince()");
checkState(ifUnmodifiedSince == null,
"ifUnmodifiedSince() is not compatible with ifModifiedSince()");
this.ifModifiedSince = dateService.toHeaderString(checkNotNull(
ifModifiedSince, "ifModifiedSince"));
return this;
}
/**
* For use in the header If-Modified-Since
* <p />
* Return the object only if it has been modified since the specified time,
* otherwise return a 304 (not modified).
*
* @see GetObjectOptions#ifModifiedSince(DateTime)
*/
public String getIfModifiedSince() {
return ifModifiedSince;
}
/**
* Only return the object if it hasn't changed since this time.
* <p />
* Not compatible with {@link #ifMd5DoesntMatch(byte[])} or
* {@link #ifModifiedSince(DateTime)}
*/
public GetObjectOptions ifUnmodifiedSince(DateTime ifUnmodifiedSince) {
checkState(ifNoneMatch == null,
"ifMd5DoesntMatch() is not compatible with ifUnmodifiedSince()");
checkState(ifModifiedSince == null,
"ifModifiedSince() is not compatible with ifUnmodifiedSince()");
this.ifUnmodifiedSince = dateService.toHeaderString(checkNotNull(
ifUnmodifiedSince, "ifUnmodifiedSince"));
return this;
}
/**
* For use in the header If-Unmodified-Since
* <p />
* Return the object only if it has not been modified since the specified
* time, otherwise return a 412 (precondition failed).
*
* @see GetObjectOptions#ifUnmodifiedSince(DateTime)
*/
public String getIfUnmodifiedSince() {
return ifUnmodifiedSince;
}
/**
* The object's md5 hash should match the parameter <code>md5</code>.
*
* <p />
* Not compatible with {@link #ifMd5DoesntMatch(byte[])} or
* {@link #ifModifiedSince(DateTime)}
*
* @param md5
* hash representing the entity
* @throws UnsupportedEncodingException
* if there was a problem converting this into an S3 eTag string
*/
public GetObjectOptions ifMd5Matches(byte[] md5)
throws UnsupportedEncodingException {
checkState(ifNoneMatch == null,
"ifMd5DoesntMatch() is not compatible with ifMd5Matches()");
checkState(ifModifiedSince == null,
"ifModifiedSince() is not compatible with ifMd5Matches()");
this.ifMatch = String.format("\"%1s\"", S3Utils
.toHexString(checkNotNull(md5, "md5")));
return this;
}
/**
* For use in the request header: If-Match
* <p />
* Return the object only if its entity tag (ETag) is the same as the md5
* specified, otherwise return a 412 (precondition failed).
*
* @see GetObjectOptions#ifMd5Matches(String)
*/
public String getIfMatch() {
return ifMatch;
}
/**
* The object should not have a md5 hash corresponding with the parameter
* <code>md5</code>.
* <p />
* Not compatible with {@link #ifMd5Matches(byte[])} or
* {@link #ifUnmodifiedSince(DateTime)}
*
* @param md5
* hash representing the entity
* @throws UnsupportedEncodingException
* if there was a problem converting this into an S3 eTag string
*/
public GetObjectOptions ifMd5DoesntMatch(byte[] md5)
throws UnsupportedEncodingException {
checkState(ifMatch == null,
"ifMd5Matches() is not compatible with ifMd5DoesntMatch()");
checkState(ifUnmodifiedSince == null,
"ifUnmodifiedSince() is not compatible with ifMd5DoesntMatch()");
this.ifNoneMatch = String.format("\"%1s\"", S3Utils
.toHexString(checkNotNull(md5, "ifMd5DoesntMatch")));
return this;
}
/**
* For use in the request header: If-None-Match
* <p />
* Return the object only if its entity tag (ETag) is different from the one
* specified, otherwise return a 304 (not modified).
*
* @see GetObjectOptions#ifMd5DoesntMatch(String)
*/
public String getIfNoneMatch() {
return ifNoneMatch;
}
public static class Builder {
/**
* @see GetObjectOptions#range(long, long)
*/
public static GetObjectOptions range(long start, long end) {
GetObjectOptions options = new GetObjectOptions();
return options.range(start, end);
}
/**
* @see GetObjectOptions#getIfModifiedSince()
*/
public static GetObjectOptions ifModifiedSince(DateTime ifModifiedSince) {
GetObjectOptions options = new GetObjectOptions();
return options.ifModifiedSince(ifModifiedSince);
}
/**
* @see GetObjectOptions#ifUnmodifiedSince(DateTime)
*/
public static GetObjectOptions ifUnmodifiedSince(
DateTime ifUnmodifiedSince) {
GetObjectOptions options = new GetObjectOptions();
return options.ifUnmodifiedSince(ifUnmodifiedSince);
}
/**
* @see GetObjectOptions#ifMd5Matches(byte[])
*/
public static GetObjectOptions ifMd5Matches(byte[] md5)
throws UnsupportedEncodingException {
GetObjectOptions options = new GetObjectOptions();
return options.ifMd5Matches(md5);
}
/**
* @see GetObjectOptions#ifMd5DoesntMatch(byte[])
*/
public static GetObjectOptions ifMd5DoesntMatch(byte[] md5)
throws UnsupportedEncodingException {
GetObjectOptions options = new GetObjectOptions();
return options.ifMd5DoesntMatch(md5);
}
}
}

View File

@ -29,11 +29,11 @@ import static com.google.common.base.Preconditions.checkNotNull;
/**
* Contains options supported in the REST API for the PUT bucket operation.
* <p/>
* Example usage:
* <h2>Usage</h2>
* The recommended way to instantiate a PutBucketOptions object is to statically import PutBucketOptions.Builder.* and invoke a static creation method followed by an instance mutator (if needed):
* <p/>
* <code>
* import static org.jclouds.aws.s3.commands.options.CreateBucketOptions.Builder.*
* import static org.jclouds.aws.s3.commands.options.PutBucketOptions.Builder.*
* import static org.jclouds.aws.s3.domain.S3Bucket.MetaData.LocationConstraint.*;
* import org.jclouds.aws.s3.S3Connection;
*
@ -41,25 +41,38 @@ import static com.google.common.base.Preconditions.checkNotNull;
* Future<Boolean> createdInEu = connection.createBucketIfNotExists("bucketName",locationConstraint(EU));
* <code>
*
* Description of parameters taken from {@link http://docs.amazonwebservices.com/AmazonS3/2006-03-01/index.html?RESTBucketPUT.html}
*
* @author Adrian Cole
*
*/
public class CreateBucketOptions {
public class PutBucketOptions {
private LocationConstraint constraint;
public CreateBucketOptions locationConstraint(LocationConstraint constraint) {
/**
* Depending on your latency and legal requirements, you can specify a
* location constraint that will affect where your data physically resides.
* You can currently specify a Europe (EU) location constraint.
*/
public PutBucketOptions locationConstraint(LocationConstraint constraint) {
this.constraint = checkNotNull(constraint, "constraint");
return this;
}
/**
* @see PutBucketOptions#locationConstraint(LocationConstraint)
*/
public LocationConstraint getLocationConstraint() {
return constraint;
}
public static class Builder {
public static CreateBucketOptions locationConstraint(
/**
* @see PutBucketOptions#locationConstraint(LocationConstraint)
*/
public static PutBucketOptions locationConstraint(
LocationConstraint constraint) {
CreateBucketOptions options = new CreateBucketOptions();
PutBucketOptions options = new PutBucketOptions();
return options.locationConstraint(constraint);
}
}

View File

@ -43,7 +43,8 @@ import com.google.inject.name.Named;
public class RequestAuthorizeSignature implements HttpRequestFilter {
private static final String[] firstHeadersToSign = new String[] {
"Content-MD5", "Content-Type", "Date" };
HttpConstants.CONTENT_MD5, HttpConstants.CONTENT_TYPE,
HttpConstants.DATE };
private final String accessKey;
private final String secretKey;
@ -110,20 +111,57 @@ public class RequestAuthorizeSignature implements HttpRequestFilter {
public void filter(HttpRequest request) throws HttpException {
// re-sign the request
request.getHeaders().removeAll(S3Constants.AUTH);
request.getHeaders().removeAll(HttpConstants.CONTENT_TYPE);
removeOldHeaders(request);
addContentTypeHeader(request);
addDateHeader(request);
StringBuilder toSign = new StringBuilder();
appendMethod(request, toSign);
appendHttpHeaders(request, toSign);
appendAmzHeaders(request, toSign);
appendBucketName(request, toSign);
appendUriPath(request, toSign);
addAuthHeader(request, toSign);
}
private void removeOldHeaders(HttpRequest request) {
request.getHeaders().removeAll(S3Constants.AUTH);
request.getHeaders().removeAll(HttpConstants.CONTENT_TYPE);
request.getHeaders().removeAll(HttpConstants.DATE);
}
private void addAuthHeader(HttpRequest request, StringBuilder toSign)
throws HttpException {
String signature;
try {
signature = S3Utils.hmacSha1Base64(toSign.toString(), secretKey
.getBytes());
} catch (Exception e) {
throw new HttpException("error signing request", e);
}
request.getHeaders().put(S3Constants.AUTH,
"AWS " + accessKey + ":" + signature);
}
private void addContentTypeHeader(HttpRequest request) {
if (request.getContent() != null && request.getContentType() != null) {
request.getHeaders().put(HttpConstants.CONTENT_TYPE,
request.getContentType());
}
}
private void appendMethod(HttpRequest request, StringBuilder toSign) {
toSign.append(request.getMethod()).append("\n");
}
private void addDateHeader(HttpRequest request) {
request.getHeaders().put(HttpConstants.DATE,
dateService.timestampAsHeaderString());
for (String header : firstHeadersToSign)
toSign.append(
valueOrEmpty(request.getHeaders().get(header))).append("\n");
}
private void appendAmzHeaders(HttpRequest request, StringBuilder toSign) {
for (String header : request.getHeaders().keySet()) {
if (header.startsWith("x-amz-")) {
toSign.append(header).append(":");
@ -133,21 +171,28 @@ public class RequestAuthorizeSignature implements HttpRequestFilter {
toSign.append("\n");
}
}
// to do amazon headers
}
private void appendHttpHeaders(HttpRequest request, StringBuilder toSign) {
for (String header : firstHeadersToSign)
toSign.append(valueOrEmpty(request.getHeaders().get(header)))
.append("\n");
}
private void appendBucketName(HttpRequest request, StringBuilder toSign) {
String hostHeader = request.getHeaders().get(HttpConstants.HOST)
.iterator().next();
if (hostHeader.endsWith(".s3.amazonaws.com"))
toSign.append("/").append(
hostHeader.substring(0, hostHeader.length() - 17));
toSign.append(request.getUri());
String signature;
try {
signature = S3Utils.hmacSha1Base64(toSign.toString(), secretKey.getBytes());
} catch (Exception e) {
throw new HttpException("error signing request", e);
}
request.getHeaders().put("Authorization",
"AWS " + accessKey + ":" + signature);
}
private void appendUriPath(HttpRequest request, StringBuilder toSign) {
int queryIndex = request.getUri().indexOf('?');
if (queryIndex >= 0)
toSign.append(request.getUri().substring(0, queryIndex));
else
toSign.append(request.getUri());
}
private String valueOrEmpty(Collection<String> collection) {

View File

@ -38,7 +38,7 @@ import org.jclouds.aws.s3.commands.HeadMetaData;
import org.jclouds.aws.s3.commands.PutBucket;
import org.jclouds.aws.s3.commands.PutObject;
import org.jclouds.aws.s3.commands.S3CommandFactory;
import org.jclouds.aws.s3.commands.options.CreateBucketOptions;
import org.jclouds.aws.s3.commands.options.PutBucketOptions;
import org.jclouds.aws.s3.domain.S3Bucket;
import org.jclouds.aws.s3.domain.S3Object;
import org.jclouds.aws.s3.domain.S3Bucket.MetaData;
@ -130,7 +130,7 @@ public class LiveS3Connection implements S3Connection {
* @see PutBucket
*/
public Future<Boolean> createBucketIfNotExists(String s3Bucket,
CreateBucketOptions options) {
PutBucketOptions options) {
PutBucket putBucket = factory.createPutBucket(s3Bucket, options);
client.submit(putBucket);
return putBucket;

View File

@ -141,15 +141,19 @@ public class AmazonS3Test extends S3IntegrationTest {
TimeUnit.SECONDS);
}
Boolean bucketExists() throws Exception {
String s3Bucket = bucketPrefix + "adrianjbosstest";
return client.bucketExists(s3Bucket).get(10, TimeUnit.SECONDS);
@Test
void bucketExists() throws Exception {
String s3Bucket = bucketPrefix + "needstoexist";
assert !client.bucketExists(s3Bucket).get(10, TimeUnit.SECONDS);
assert client.createBucketIfNotExists(s3Bucket).get(10,
TimeUnit.SECONDS);
assert client.bucketExists(s3Bucket).get(10, TimeUnit.SECONDS);
}
Boolean deleteBucket() throws Exception {
String s3Bucket = bucketPrefix + "adrianjbosstest";
return client.deleteBucketIfEmpty(s3Bucket)
.get(10, TimeUnit.SECONDS);
return client.deleteBucketIfEmpty(s3Bucket).get(10, TimeUnit.SECONDS);
}
Boolean deleteObject() throws Exception {

View File

@ -34,7 +34,7 @@ import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.jclouds.aws.s3.commands.options.CreateBucketOptions;
import org.jclouds.aws.s3.commands.options.PutBucketOptions;
import org.jclouds.aws.s3.domain.S3Bucket;
import org.jclouds.aws.s3.domain.S3Object;
import org.jclouds.aws.s3.domain.S3Bucket.MetaData;
@ -214,7 +214,7 @@ public class StubS3Connection implements S3Connection {
}
public Future<Boolean> createBucketIfNotExists(String name,
CreateBucketOptions options) {
PutBucketOptions options) {
throw new UnsupportedOperationException("todo");
}

View File

@ -28,7 +28,7 @@ import static org.easymock.classextension.EasyMock.createMock;
import static org.easymock.classextension.EasyMock.replay;
import org.jclouds.aws.s3.commands.config.S3CommandsModule;
import org.jclouds.aws.s3.commands.options.CreateBucketOptions;
import org.jclouds.aws.s3.commands.options.PutBucketOptions;
import org.jclouds.aws.s3.domain.S3Object;
import org.jclouds.aws.s3.domain.S3Bucket.MetaData.LocationConstraint;
import org.testng.annotations.AfterMethod;
@ -98,9 +98,8 @@ public class S3CommandFactoryTest {
@Test
void testCreatePutBucketOptions() {
assert commandFactory.createPutBucket("test",
CreateBucketOptions.Builder
.locationConstraint(LocationConstraint.EU)) != null;
assert commandFactory.createPutBucket("test", PutBucketOptions.Builder
.locationConstraint(LocationConstraint.EU)) != null;
}
@Test

View File

@ -0,0 +1,288 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adrian@jclouds.org>
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specifSourceic language governing permissions and limitations
* under the License.
* ====================================================================
*/
package org.jclouds.aws.s3.commands.options;
import static org.jclouds.aws.s3.commands.options.CopyObjectOptions.Builder.ifSourceMd5DoesntMatch;
import static org.jclouds.aws.s3.commands.options.CopyObjectOptions.Builder.ifSourceMd5Matches;
import static org.jclouds.aws.s3.commands.options.CopyObjectOptions.Builder.ifSourceModifiedSince;
import static org.jclouds.aws.s3.commands.options.CopyObjectOptions.Builder.ifSourceUnmodifiedSince;
import static org.jclouds.aws.s3.commands.options.CopyObjectOptions.Builder.overrideMetadataWith;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNull;
import java.io.UnsupportedEncodingException;
import org.jclouds.aws.s3.DateService;
import org.jclouds.aws.s3.S3Utils;
import org.joda.time.DateTime;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
/**
* Tests possible uses of CopyObjectOptions and CopyObjectOptions.Builder.*
*
* @author Adrian Cole
*/
public class CopyObjectOptionsTest {
private byte[] testBytes;
private DateTime now;
private String nowExpected;
private Multimap<String, String> goodMeta;
private Multimap<String, String> badMeta;
@BeforeMethod
void setUp() {
goodMeta = HashMultimap.create();
goodMeta.put("x-amz-meta-adrian", "foo");
badMeta = HashMultimap.create();
badMeta.put("x-google-meta-adrian", "foo");
now = new DateTime();
nowExpected = new DateService().toHeaderString(now);
testBytes = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7 };
}
@Test
void testGoodMetaStatic() {
CopyObjectOptions options = overrideMetadataWith(goodMeta);
assertGoodMeta(options);
}
@Test(expectedExceptions = NullPointerException.class)
public void testMetaNPE() {
overrideMetadataWith(null);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadMeta() {
overrideMetadataWith(badMeta);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadMetaStatic() {
overrideMetadataWith(badMeta);
}
private void assertGoodMeta(CopyObjectOptions options) {
assert options != null;
assert options.getMetadata() != null;
assertEquals(options.getMetadata().size(), 2);
assertEquals(options.getMetadata().get(
"x-amz-copy-source-if-unmodified-since").iterator().next(),
"REPLACE");
assertEquals(options.getMetadata().get("x-amz-meta-adrian").iterator()
.next(), "foo");
}
@Test
void testGoodMeta() {
CopyObjectOptions options = new CopyObjectOptions();
options.overrideMetadataWith(goodMeta);
assertGoodMeta(options);
}
@Test
public void testIfModifiedSince() {
CopyObjectOptions options = new CopyObjectOptions();
options.ifSourceModifiedSince(now);
assertEquals(options.getIfModifiedSince(), nowExpected);
}
@Test
public void testNullIfModifiedSince() {
CopyObjectOptions options = new CopyObjectOptions();
assertNull(options.getIfModifiedSince());
}
@Test
public void testIfModifiedSinceStatic() {
CopyObjectOptions options = ifSourceModifiedSince(now);
assertEquals(options.getIfModifiedSince(), nowExpected);
}
@Test(expectedExceptions = NullPointerException.class)
public void testIfModifiedSinceNPE() {
ifSourceModifiedSince(null);
}
@Test
public void testIfUnmodifiedSince() {
CopyObjectOptions options = new CopyObjectOptions();
options.ifSourceUnmodifiedSince(now);
isNowExpected(options);
}
@Test
public void testNullIfUnmodifiedSince() {
CopyObjectOptions options = new CopyObjectOptions();
assertNull(options.getIfUnmodifiedSince());
}
@Test
public void testIfUnmodifiedSinceStatic() {
CopyObjectOptions options = ifSourceUnmodifiedSince(now);
isNowExpected(options);
}
private void isNowExpected(CopyObjectOptions options) {
assertEquals(options.getIfUnmodifiedSince(), nowExpected);
}
@Test(expectedExceptions = NullPointerException.class)
public void testIfUnmodifiedSinceNPE() {
ifSourceUnmodifiedSince(null);
}
@Test
public void testIfMd5Matches() throws UnsupportedEncodingException {
CopyObjectOptions options = new CopyObjectOptions();
options.ifSourceMd5Matches(testBytes);
matchesHex(options.getIfMatch());
}
@Test
public void testNullIfMd5Matches() {
CopyObjectOptions options = new CopyObjectOptions();
assertNull(options.getIfMatch());
}
@Test
public void testIfMd5MatchesStatic() throws UnsupportedEncodingException {
CopyObjectOptions options = ifSourceMd5Matches(testBytes);
matchesHex(options.getIfMatch());
}
@Test(expectedExceptions = NullPointerException.class)
public void testIfMd5MatchesNPE() throws UnsupportedEncodingException {
ifSourceMd5Matches(null);
}
@Test
public void testIfMd5DoesntMatch() throws UnsupportedEncodingException {
CopyObjectOptions options = new CopyObjectOptions();
options.ifSourceMd5DoesntMatch(testBytes);
matchesHex(options.getIfNoneMatch());
}
@Test
public void testNullIfMd5DoesntMatch() {
CopyObjectOptions options = new CopyObjectOptions();
assertNull(options.getIfNoneMatch());
}
@Test
public void testIfMd5DoesntMatchStatic()
throws UnsupportedEncodingException {
CopyObjectOptions options = ifSourceMd5DoesntMatch(testBytes);
matchesHex(options.getIfNoneMatch());
}
@Test(expectedExceptions = NullPointerException.class)
public void testIfMd5DoesntMatchNPE() throws UnsupportedEncodingException {
ifSourceMd5DoesntMatch(null);
}
private void matchesHex(String match) throws UnsupportedEncodingException {
String expected = "\"" + S3Utils.toHexString(testBytes) + "\"";
assertEquals(match, expected);
}
@Test(expectedExceptions = IllegalStateException.class)
public void testIfUnmodifiedAfterModified() {
ifSourceModifiedSince(now).ifSourceUnmodifiedSince(now);
}
public void testIfUnmodifiedAfterMd5Matches()
throws UnsupportedEncodingException {
ifSourceMd5Matches(testBytes).ifSourceUnmodifiedSince(now);
}
@Test(expectedExceptions = IllegalStateException.class)
public void testIfUnmodifiedAfterMd5DoesntMatch()
throws UnsupportedEncodingException {
ifSourceMd5DoesntMatch(testBytes).ifSourceUnmodifiedSince(now);
}
@Test(expectedExceptions = IllegalStateException.class)
public void testIfModifiedAfterUnmodified() {
ifSourceUnmodifiedSince(now).ifSourceModifiedSince(now);
}
@Test(expectedExceptions = IllegalStateException.class)
public void testIfModifiedAfterMd5Matches()
throws UnsupportedEncodingException {
ifSourceMd5Matches(testBytes).ifSourceModifiedSince(now);
}
public void testIfModifiedAfterMd5DoesntMatch()
throws UnsupportedEncodingException {
ifSourceMd5DoesntMatch(testBytes).ifSourceModifiedSince(now);
}
@Test(expectedExceptions = IllegalStateException.class)
public void testMd5MatchesAfterIfModified()
throws UnsupportedEncodingException {
ifSourceModifiedSince(now).ifSourceMd5Matches(testBytes);
}
public void testMd5MatchesAfterIfUnmodified()
throws UnsupportedEncodingException {
ifSourceUnmodifiedSince(now).ifSourceMd5Matches(testBytes);
}
@Test(expectedExceptions = IllegalStateException.class)
public void testMd5MatchesAfterMd5DoesntMatch()
throws UnsupportedEncodingException {
ifSourceMd5DoesntMatch(testBytes).ifSourceMd5Matches(testBytes);
}
public void testMd5DoesntMatchAfterIfModified()
throws UnsupportedEncodingException {
ifSourceModifiedSince(now).ifSourceMd5DoesntMatch(testBytes);
}
@Test(expectedExceptions = IllegalStateException.class)
public void testMd5DoesntMatchAfterIfUnmodified()
throws UnsupportedEncodingException {
ifSourceUnmodifiedSince(now).ifSourceMd5DoesntMatch(testBytes);
}
@Test(expectedExceptions = IllegalStateException.class)
public void testMd5DoesntMatchAfterMd5Matches()
throws UnsupportedEncodingException {
ifSourceMd5Matches(testBytes).ifSourceMd5DoesntMatch(testBytes);
}
}

View File

@ -0,0 +1,162 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adrian@jclouds.org>
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
package org.jclouds.aws.s3.commands.options;
import static org.jclouds.aws.s3.commands.options.GetBucketOptions.Builder.delimiter;
import static org.jclouds.aws.s3.commands.options.GetBucketOptions.Builder.marker;
import static org.jclouds.aws.s3.commands.options.GetBucketOptions.Builder.maxKeys;
import static org.jclouds.aws.s3.commands.options.GetBucketOptions.Builder.prefix;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNull;
import org.testng.annotations.Test;
/**
* Tests possible uses of GetBucketOptions and GetBucketOptions.Builder.*
*
* @author Adrian Cole
*/
public class GetBucketOptionsTest {
@Test
public void testPrefix() {
GetBucketOptions options = new GetBucketOptions();
options.prefix("test");
assertEquals(options.getPrefix(), "test");
}
@Test
public void testNoOptionsQueryString() {
GetBucketOptions options = new GetBucketOptions();
assertEquals(options.toQueryString(), "");
}
@Test
public void testOneOptionQueryString() {
GetBucketOptions options = new GetBucketOptions();
options.prefix("test");
assertEquals(options.toQueryString(), "?prefix=test");
}
@Test
public void testTwoOptionQueryString() {
GetBucketOptions options = new GetBucketOptions();
options.prefix("test").maxKeys(1);
try {
assertEquals(options.toQueryString(), "?prefix=test&max-keys=1");
} catch (AssertionError e) {
assertEquals(options.toQueryString(), "?max-keys=1&prefix=test");
}
}
@Test
public void testNullPrefix() {
GetBucketOptions options = new GetBucketOptions();
assertNull(options.getPrefix());
}
@Test
public void testPrefixStatic() {
GetBucketOptions options = prefix("test");
assertEquals(options.getPrefix(), "test");
}
@Test(expectedExceptions = NullPointerException.class)
public void testPrefixNPE() {
prefix(null);
}
@Test
public void testMarker() {
GetBucketOptions options = new GetBucketOptions();
options.marker("test");
assertEquals(options.getMarker(), "test");
}
@Test
public void testNullMarker() {
GetBucketOptions options = new GetBucketOptions();
assertNull(options.getMarker());
}
@Test
public void testMarkerStatic() {
GetBucketOptions options = marker("test");
assertEquals(options.getMarker(), "test");
}
@Test(expectedExceptions = NullPointerException.class)
public void testMarkerNPE() {
marker(null);
}
@Test
public void testMaxKeys() {
GetBucketOptions options = new GetBucketOptions();
options.maxKeys(1000);
assertEquals(options.getMaxKeys(), "1000");
}
@Test
public void testNullMaxKeys() {
GetBucketOptions options = new GetBucketOptions();
assertNull(options.getMaxKeys());
}
@Test
public void testMaxKeysStatic() {
GetBucketOptions options = maxKeys(1000);
assertEquals(options.getMaxKeys(), "1000");
}
@Test(expectedExceptions = IllegalStateException.class)
public void testMaxKeysNegative() {
maxKeys(-1);
}
@Test
public void testDelimiter() {
GetBucketOptions options = new GetBucketOptions();
options.delimiter("test");
assertEquals(options.getDelimiter(), "test");
}
@Test
public void testNullDelimiter() {
GetBucketOptions options = new GetBucketOptions();
assertNull(options.getDelimiter());
}
@Test
public void testDelimiterStatic() {
GetBucketOptions options = delimiter("test");
assertEquals(options.getDelimiter(), "test");
}
@Test(expectedExceptions = NullPointerException.class)
public void testDelimiterNPE() {
delimiter(null);
}
}

View File

@ -0,0 +1,286 @@
/**
*
* Copyright (C) 2009 Adrian Cole <adrian@jclouds.org>
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
package org.jclouds.aws.s3.commands.options;
import static org.jclouds.aws.s3.commands.options.GetObjectOptions.Builder.ifMd5DoesntMatch;
import static org.jclouds.aws.s3.commands.options.GetObjectOptions.Builder.ifMd5Matches;
import static org.jclouds.aws.s3.commands.options.GetObjectOptions.Builder.ifModifiedSince;
import static org.jclouds.aws.s3.commands.options.GetObjectOptions.Builder.ifUnmodifiedSince;
import static org.jclouds.aws.s3.commands.options.GetObjectOptions.Builder.range;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNull;
import java.io.UnsupportedEncodingException;
import org.jclouds.aws.s3.DateService;
import org.jclouds.aws.s3.S3Utils;
import org.joda.time.DateTime;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
/**
* Tests possible uses of GetObjectOptions and GetObjectOptions.Builder.*
*
* @author Adrian Cole
*/
public class GetObjectOptionsTest {
private byte[] testBytes;
private DateTime now;
private String nowExpected;
@BeforeTest
void setUp() {
now = new DateTime();
nowExpected = new DateService().toHeaderString(now);
testBytes = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7 };
}
@Test
public void testIfModifiedSince() {
GetObjectOptions options = new GetObjectOptions();
options.ifModifiedSince(now);
assertEquals(options.getIfModifiedSince(), nowExpected);
}
@Test
public void testNullIfModifiedSince() {
GetObjectOptions options = new GetObjectOptions();
assertNull(options.getIfModifiedSince());
}
@Test
public void testIfModifiedSinceStatic() {
GetObjectOptions options = ifModifiedSince(now);
assertEquals(options.getIfModifiedSince(), nowExpected);
}
@Test(expectedExceptions = NullPointerException.class)
public void testIfModifiedSinceNPE() {
ifModifiedSince(null);
}
@Test
public void testIfUnmodifiedSince() {
GetObjectOptions options = new GetObjectOptions();
options.ifUnmodifiedSince(now);
isNowExpected(options);
}
@Test
public void testNullIfUnmodifiedSince() {
GetObjectOptions options = new GetObjectOptions();
assertNull(options.getIfUnmodifiedSince());
}
@Test
public void testIfUnmodifiedSinceStatic() {
GetObjectOptions options = ifUnmodifiedSince(now);
isNowExpected(options);
}
private void isNowExpected(GetObjectOptions options) {
assertEquals(options.getIfUnmodifiedSince(), nowExpected);
}
@Test(expectedExceptions = NullPointerException.class)
public void testIfUnmodifiedSinceNPE() {
ifUnmodifiedSince(null);
}
public void testModifiedSinceAndRange() {
GetObjectOptions options = new GetObjectOptions();
options.ifModifiedSince(now);
options.range(0, 1024);
isNowExpected(options);
bytes1to1024(options);
}
@Test
public void testRange() {
GetObjectOptions options = new GetObjectOptions();
options.range(0, 1024);
bytes1to1024(options);
}
private void bytes1to1024(GetObjectOptions options) {
assertEquals(options.getRange(), "bytes=0-1024");
}
@Test
public void testNullRange() {
GetObjectOptions options = new GetObjectOptions();
assertNull(options.getRange());
}
@Test
public void testRangeStatic() {
GetObjectOptions options = range(0, 1024);
bytes1to1024(options);
}
@Test(expectedExceptions = IllegalStateException.class)
public void testRangeNegative1() {
range(-1, 0);
}
@Test(expectedExceptions = IllegalStateException.class)
public void testRangeNegative2() {
range(0, -1);
}
@Test(expectedExceptions = IllegalStateException.class)
public void testRangeNegative() {
range(-1, -1);
}
@Test
public void testIfMd5Matches() throws UnsupportedEncodingException {
GetObjectOptions options = new GetObjectOptions();
options.ifMd5Matches(testBytes);
matchesHex(options.getIfMatch());
}
@Test
public void testNullIfMd5Matches() {
GetObjectOptions options = new GetObjectOptions();
assertNull(options.getIfMatch());
}
@Test
public void testIfMd5MatchesStatic() throws UnsupportedEncodingException {
GetObjectOptions options = ifMd5Matches(testBytes);
matchesHex(options.getIfMatch());
}
@Test(expectedExceptions = NullPointerException.class)
public void testIfMd5MatchesNPE() throws UnsupportedEncodingException {
ifMd5Matches(null);
}
@Test
public void testIfMd5DoesntMatch() throws UnsupportedEncodingException {
GetObjectOptions options = new GetObjectOptions();
options.ifMd5DoesntMatch(testBytes);
matchesHex(options.getIfNoneMatch());
}
@Test
public void testNullIfMd5DoesntMatch() {
GetObjectOptions options = new GetObjectOptions();
assertNull(options.getIfNoneMatch());
}
@Test
public void testIfMd5DoesntMatchStatic()
throws UnsupportedEncodingException {
GetObjectOptions options = ifMd5DoesntMatch(testBytes);
matchesHex(options.getIfNoneMatch());
}
@Test(expectedExceptions = NullPointerException.class)
public void testIfMd5DoesntMatchNPE() throws UnsupportedEncodingException {
ifMd5DoesntMatch(null);
}
private void matchesHex(String match) throws UnsupportedEncodingException {
String expected = "\"" + S3Utils.toHexString(testBytes) + "\"";
assertEquals(match, expected);
}
@Test(expectedExceptions = IllegalStateException.class)
public void testIfUnmodifiedAfterModified() {
ifModifiedSince(now).ifUnmodifiedSince(now);
}
public void testIfUnmodifiedAfterMd5Matches()
throws UnsupportedEncodingException {
ifMd5Matches(testBytes).ifUnmodifiedSince(now);
}
@Test(expectedExceptions = IllegalStateException.class)
public void testIfUnmodifiedAfterMd5DoesntMatch()
throws UnsupportedEncodingException {
ifMd5DoesntMatch(testBytes).ifUnmodifiedSince(now);
}
@Test(expectedExceptions = IllegalStateException.class)
public void testIfModifiedAfterUnmodified() {
ifUnmodifiedSince(now).ifModifiedSince(now);
}
@Test(expectedExceptions = IllegalStateException.class)
public void testIfModifiedAfterMd5Matches()
throws UnsupportedEncodingException {
ifMd5Matches(testBytes).ifModifiedSince(now);
}
public void testIfModifiedAfterMd5DoesntMatch()
throws UnsupportedEncodingException {
ifMd5DoesntMatch(testBytes).ifModifiedSince(now);
}
@Test(expectedExceptions = IllegalStateException.class)
public void testMd5MatchesAfterIfModified()
throws UnsupportedEncodingException {
ifModifiedSince(now).ifMd5Matches(testBytes);
}
public void testMd5MatchesAfterIfUnmodified()
throws UnsupportedEncodingException {
ifUnmodifiedSince(now).ifMd5Matches(testBytes);
}
@Test(expectedExceptions = IllegalStateException.class)
public void testMd5MatchesAfterMd5DoesntMatch()
throws UnsupportedEncodingException {
ifMd5DoesntMatch(testBytes).ifMd5Matches(testBytes);
}
public void testMd5DoesntMatchAfterIfModified()
throws UnsupportedEncodingException {
ifModifiedSince(now).ifMd5DoesntMatch(testBytes);
}
@Test(expectedExceptions = IllegalStateException.class)
public void testMd5DoesntMatchAfterIfUnmodified()
throws UnsupportedEncodingException {
ifUnmodifiedSince(now).ifMd5DoesntMatch(testBytes);
}
@Test(expectedExceptions = IllegalStateException.class)
public void testMd5DoesntMatchAfterMd5Matches()
throws UnsupportedEncodingException {
ifMd5Matches(testBytes).ifMd5DoesntMatch(testBytes);
}
}

View File

@ -23,31 +23,36 @@
*/
package org.jclouds.aws.s3.commands.options;
import static org.jclouds.aws.s3.commands.options.CreateBucketOptions.Builder.locationConstraint;
import static org.jclouds.aws.s3.commands.options.PutBucketOptions.Builder.locationConstraint;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNull;
import org.jclouds.aws.s3.domain.S3Bucket.MetaData.LocationConstraint;
import org.testng.annotations.Test;
public class CreateBucketOptionsTest {
/**
* Tests possible uses of GetBucketOptions and GetBucketOptions.Builder.*
*
* @author Adrian Cole
*/
public class PutBucketOptionsTest {
@Test
public void testLocationConstraint() {
CreateBucketOptions options = new CreateBucketOptions();
PutBucketOptions options = new PutBucketOptions();
options.locationConstraint(LocationConstraint.EU);
assertEquals(options.getLocationConstraint(), LocationConstraint.EU);
}
@Test
public void testNullLocationConstraint() {
CreateBucketOptions options = new CreateBucketOptions();
PutBucketOptions options = new PutBucketOptions();
assertNull(options.getLocationConstraint());
}
@Test
public void testLocationConstraintStatic() {
CreateBucketOptions options = locationConstraint(LocationConstraint.EU);
PutBucketOptions options = locationConstraint(LocationConstraint.EU);
assertEquals(options.getLocationConstraint(), LocationConstraint.EU);
}