diff --git a/apis/s3/src/main/java/org/jclouds/s3/S3AsyncClient.java b/apis/s3/src/main/java/org/jclouds/s3/S3AsyncClient.java
index 3bd477de58..749d28c80d 100644
--- a/apis/s3/src/main/java/org/jclouds/s3/S3AsyncClient.java
+++ b/apis/s3/src/main/java/org/jclouds/s3/S3AsyncClient.java
@@ -23,7 +23,6 @@ import static org.jclouds.blobstore.attr.BlobScopes.CONTAINER;
import java.util.Set;
import java.util.concurrent.ExecutionException;
-import org.jclouds.javax.annotation.Nullable;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.HEAD;
@@ -41,8 +40,9 @@ import org.jclouds.blobstore.functions.ThrowContainerNotFoundOn404;
import org.jclouds.blobstore.functions.ThrowKeyNotFoundOn404;
import org.jclouds.http.functions.ParseETagHeader;
import org.jclouds.http.options.GetOptions;
-import org.jclouds.location.functions.RegionToEndpointOrProviderIfNull;
+import org.jclouds.javax.annotation.Nullable;
import org.jclouds.rest.annotations.BinderParam;
+import org.jclouds.rest.annotations.Endpoint;
import org.jclouds.rest.annotations.EndpointParam;
import org.jclouds.rest.annotations.ExceptionParser;
import org.jclouds.rest.annotations.Headers;
@@ -69,11 +69,13 @@ import org.jclouds.s3.domain.ObjectMetadata;
import org.jclouds.s3.domain.Payer;
import org.jclouds.s3.domain.S3Object;
import org.jclouds.s3.filters.RequestAuthorizeSignature;
+import org.jclouds.s3.functions.AssignCorrectHostnameForBucket;
import org.jclouds.s3.functions.BindRegionToXmlPayload;
+import org.jclouds.s3.functions.DefaultEndpointThenInvalidateRegion;
import org.jclouds.s3.functions.ObjectKey;
import org.jclouds.s3.functions.ParseObjectFromHeadersAndHttpContent;
import org.jclouds.s3.functions.ParseObjectMetadataFromHeaders;
-import org.jclouds.s3.functions.ReturnFalseIfBucketAlreadyOwnedByYouOrIllegalState;
+import org.jclouds.s3.functions.ReturnFalseIfBucketAlreadyOwnedByYouOrOperationAbortedWhenBucketExists;
import org.jclouds.s3.functions.ReturnTrueOn404OrNotFoundFalseOnIllegalState;
import org.jclouds.s3.options.CopyObjectOptions;
import org.jclouds.s3.options.ListBucketOptions;
@@ -95,7 +97,7 @@ import com.google.inject.Provides;
* Provides asynchronous access to S3 via their REST API.
*
* All commands return a ListenableFuture of the result from S3. Any exceptions incurred during
- * processing will be wrapped in an {@link ExecutionException} as documented in
+ * processing will be backend in an {@link ExecutionException} as documented in
* {@link ListenableFuture#get()}.
*
* @author Adrian Cole
@@ -123,8 +125,8 @@ public interface S3AsyncClient {
@ExceptionParser(ReturnNullOnKeyNotFound.class)
@ResponseParser(ParseObjectFromHeadersAndHttpContent.class)
ListenableFuture getObject(
- @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators({ BucketNameValidator.class }) String bucketName,
- @PathParam("key") String key, GetOptions... options);
+ @Bucket @EndpointParam(parser = AssignCorrectHostnameForBucket.class) @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators(BucketNameValidator.class) String bucketName,
+ @PathParam("key") String key, GetOptions... options);
/**
* @see S3Client#headObject
@@ -134,8 +136,8 @@ public interface S3AsyncClient {
@ExceptionParser(ReturnNullOnKeyNotFound.class)
@ResponseParser(ParseObjectMetadataFromHeaders.class)
ListenableFuture headObject(
- @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators({ BucketNameValidator.class }) String bucketName,
- @PathParam("key") String key);
+ @Bucket @EndpointParam(parser = AssignCorrectHostnameForBucket.class) @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators(BucketNameValidator.class) String bucketName,
+ @PathParam("key") String key);
/**
* @see S3Client#objectExists
@@ -144,8 +146,8 @@ public interface S3AsyncClient {
@Path("/{key}")
@ExceptionParser(ReturnFalseOnKeyNotFound.class)
ListenableFuture objectExists(
- @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators({ BucketNameValidator.class }) String bucketName,
- @PathParam("key") String key);
+ @Bucket @EndpointParam(parser = AssignCorrectHostnameForBucket.class) @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators(BucketNameValidator.class) String bucketName,
+ @PathParam("key") String key);
/**
* @see S3Client#deleteObject
@@ -154,8 +156,8 @@ public interface S3AsyncClient {
@Path("/{key}")
@ExceptionParser(ReturnVoidOnNotFoundOr404.class)
ListenableFuture deleteObject(
- @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators({ BucketNameValidator.class }) String bucketName,
- @PathParam("key") String key);
+ @Bucket @EndpointParam(parser = AssignCorrectHostnameForBucket.class) @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators(BucketNameValidator.class) String bucketName,
+ @PathParam("key") String key);
/**
* @see S3Client#putObject
@@ -164,39 +166,43 @@ public interface S3AsyncClient {
@Path("/{key}")
@ResponseParser(ParseETagHeader.class)
ListenableFuture putObject(
- @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators({ BucketNameValidator.class }) String bucketName,
- @PathParam("key") @ParamParser(ObjectKey.class) @BinderParam(BindS3ObjectMetadataToRequest.class) S3Object object,
- PutObjectOptions... options);
+ @Bucket @EndpointParam(parser = AssignCorrectHostnameForBucket.class) @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators(BucketNameValidator.class) String bucketName,
+ @PathParam("key") @ParamParser(ObjectKey.class) @BinderParam(BindS3ObjectMetadataToRequest.class) S3Object object,
+ PutObjectOptions... options);
/**
* @see S3Client#putBucketInRegion
*/
@PUT
@Path("/")
- @ExceptionParser(ReturnFalseIfBucketAlreadyOwnedByYouOrIllegalState.class)
+ @Endpoint(Bucket.class)
+ @ExceptionParser(ReturnFalseIfBucketAlreadyOwnedByYouOrOperationAbortedWhenBucketExists.class)
ListenableFuture putBucketInRegion(
- @EndpointParam(parser = RegionToEndpointOrProviderIfNull.class) @BinderParam(BindRegionToXmlPayload.class) @Nullable String region,
- @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators({ BucketNameValidator.class }) String bucketName,
- PutBucketOptions... options);
+ @BinderParam(BindRegionToXmlPayload.class) @Nullable String region,
+ @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators(BucketNameValidator.class) String bucketName,
+ PutBucketOptions... options);
/**
* @see S3Client#deleteBucketIfEmpty
*/
@DELETE
@Path("/")
+ @Endpoint(Bucket.class)
@ExceptionParser(ReturnTrueOn404OrNotFoundFalseOnIllegalState.class)
ListenableFuture deleteBucketIfEmpty(
- @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators({ BucketNameValidator.class }) String bucketName);
+ @Bucket @EndpointParam(parser = DefaultEndpointThenInvalidateRegion.class) @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators(BucketNameValidator.class) String bucketName);
/**
* @see S3Client#bucketExists
*/
@HEAD
@Path("/")
+ @Endpoint(Bucket.class)
@QueryParams(keys = "max-keys", values = "0")
@ExceptionParser(ReturnFalseOnContainerNotFound.class)
ListenableFuture bucketExists(
- @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators({ BucketNameValidator.class }) String bucketName);
+ @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators(BucketNameValidator.class) String bucketName);
+
/**
* @see S3Client#getBucketLocation
@@ -204,9 +210,10 @@ public interface S3AsyncClient {
@GET
@QueryParams(keys = "location")
@Path("/")
+ @Endpoint(Bucket.class)
@XMLResponseParser(LocationConstraintHandler.class)
ListenableFuture getBucketLocation(
- @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators({ BucketNameValidator.class }) String bucketName);
+ @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators(BucketNameValidator.class) String bucketName);
/**
* @see S3Client#getBucketPayer
@@ -216,7 +223,7 @@ public interface S3AsyncClient {
@Path("/")
@XMLResponseParser(PayerHandler.class)
ListenableFuture getBucketPayer(
- @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators({ BucketNameValidator.class }) String bucketName);
+ @Bucket @EndpointParam(parser = AssignCorrectHostnameForBucket.class) @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators(BucketNameValidator.class) String bucketName);
/**
* @see S3Client#setBucketPayer
@@ -225,8 +232,8 @@ public interface S3AsyncClient {
@QueryParams(keys = "requestPayment")
@Path("/")
ListenableFuture setBucketPayer(
- @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators({ BucketNameValidator.class }) String bucketName,
- @BinderParam(BindPayerToXmlPayload.class) Payer payer);
+ @Bucket @EndpointParam(parser = AssignCorrectHostnameForBucket.class) @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators(BucketNameValidator.class) String bucketName,
+ @BinderParam(BindPayerToXmlPayload.class) Payer payer);
/**
* @see S3Client#listBucket
@@ -235,8 +242,8 @@ public interface S3AsyncClient {
@Path("/")
@XMLResponseParser(ListBucketHandler.class)
ListenableFuture listBucket(
- @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators({ BucketNameValidator.class }) String bucketName,
- ListBucketOptions... options);
+ @Bucket @EndpointParam(parser = AssignCorrectHostnameForBucket.class) @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators(BucketNameValidator.class) String bucketName,
+ ListBucketOptions... options);
/**
* @see S3Client#listOwnedBuckets
@@ -255,10 +262,10 @@ public interface S3AsyncClient {
@Headers(keys = "x-amz-copy-source", values = "/{sourceBucket}/{sourceObject}")
@XMLResponseParser(CopyObjectHandler.class)
ListenableFuture copyObject(
- @PathParam("sourceBucket") String sourceBucket,
- @PathParam("sourceObject") String sourceObject,
- @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators({ BucketNameValidator.class }) String destinationBucket,
- @PathParam("destinationObject") String destinationObject, CopyObjectOptions... options);
+ @PathParam("sourceBucket") String sourceBucket,
+ @PathParam("sourceObject") String sourceObject,
+ @Bucket @EndpointParam(parser = AssignCorrectHostnameForBucket.class) @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators(BucketNameValidator.class) String destinationBucket,
+ @PathParam("destinationObject") String destinationObject, CopyObjectOptions... options);
/**
* @see S3Client#getBucketACL
@@ -269,7 +276,7 @@ public interface S3AsyncClient {
@ExceptionParser(ThrowContainerNotFoundOn404.class)
@Path("/")
ListenableFuture getBucketACL(
- @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators({ BucketNameValidator.class }) String bucketName);
+ @Bucket @EndpointParam(parser = AssignCorrectHostnameForBucket.class) @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators(BucketNameValidator.class) String bucketName);
/**
* @see S3Client#putBucketACL
@@ -278,8 +285,8 @@ public interface S3AsyncClient {
@Path("/")
@QueryParams(keys = "acl")
ListenableFuture putBucketACL(
- @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators({ BucketNameValidator.class }) String bucketName,
- @BinderParam(BindACLToXMLPayload.class) AccessControlList acl);
+ @Bucket @EndpointParam(parser = AssignCorrectHostnameForBucket.class) @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators(BucketNameValidator.class) String bucketName,
+ @BinderParam(BindACLToXMLPayload.class) AccessControlList acl);
/**
* @see S3Client#getObjectACL
@@ -290,8 +297,8 @@ public interface S3AsyncClient {
@XMLResponseParser(AccessControlListHandler.class)
@ExceptionParser(ThrowKeyNotFoundOn404.class)
ListenableFuture getObjectACL(
- @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators({ BucketNameValidator.class }) String bucketName,
- @PathParam("key") String key);
+ @Bucket @EndpointParam(parser = AssignCorrectHostnameForBucket.class) @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators(BucketNameValidator.class) String bucketName,
+ @PathParam("key") String key);
/**
* @see S3Client#putObjectACL
@@ -300,8 +307,8 @@ public interface S3AsyncClient {
@QueryParams(keys = "acl")
@Path("/{key}")
ListenableFuture putObjectACL(
- @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators({ BucketNameValidator.class }) String bucketName,
- @PathParam("key") String key, @BinderParam(BindACLToXMLPayload.class) AccessControlList acl);
+ @Bucket @EndpointParam(parser = AssignCorrectHostnameForBucket.class) @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators(BucketNameValidator.class) String bucketName,
+ @PathParam("key") String key, @BinderParam(BindACLToXMLPayload.class) AccessControlList acl);
/**
* @see S3Client#getBucketLogging
@@ -312,7 +319,7 @@ public interface S3AsyncClient {
@ExceptionParser(ThrowContainerNotFoundOn404.class)
@Path("/")
ListenableFuture getBucketLogging(
- @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators({ BucketNameValidator.class }) String bucketName);
+ @Bucket @EndpointParam(parser = AssignCorrectHostnameForBucket.class) @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators(BucketNameValidator.class) String bucketName);
/**
* @see S3Client#enableBucketLogging
@@ -321,8 +328,8 @@ public interface S3AsyncClient {
@Path("/")
@QueryParams(keys = "logging")
ListenableFuture enableBucketLogging(
- @Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators({ BucketNameValidator.class }) String bucketName,
- @BinderParam(BindBucketLoggingToXmlPayload.class) BucketLogging logging);
+ @Bucket @EndpointParam(parser = AssignCorrectHostnameForBucket.class) @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators(BucketNameValidator.class) String bucketName,
+ @BinderParam(BindBucketLoggingToXmlPayload.class) BucketLogging logging);
/**
* @see S3Client#putBucketLogging
@@ -332,6 +339,6 @@ public interface S3AsyncClient {
@QueryParams(keys = "logging")
@Produces(MediaType.TEXT_XML)
ListenableFuture disableBucketLogging(
- @Bucket @BinderParam(BindNoBucketLoggingToXmlPayload.class) @ParamValidators({ BucketNameValidator.class }) String bucketName);
+ @Bucket @EndpointParam(parser = AssignCorrectHostnameForBucket.class) @BinderParam(BindNoBucketLoggingToXmlPayload.class) @ParamValidators(BucketNameValidator.class) String bucketName);
}
diff --git a/apis/s3/src/main/java/org/jclouds/s3/S3Client.java b/apis/s3/src/main/java/org/jclouds/s3/S3Client.java
index 68133874e3..c974ca29b0 100644
--- a/apis/s3/src/main/java/org/jclouds/s3/S3Client.java
+++ b/apis/s3/src/main/java/org/jclouds/s3/S3Client.java
@@ -198,7 +198,7 @@ public interface S3Client {
*
*/
@Timeout(duration = 90, timeUnit = TimeUnit.SECONDS)
- boolean putBucketInRegion(@Nullable String region, String bucketName, PutBucketOptions... options);
+ boolean putBucketInRegion(@Nullable String region, @Bucket String bucketName, PutBucketOptions... options);
/**
* Deletes the bucket, if it is empty.
diff --git a/apis/s3/src/main/java/org/jclouds/s3/blobstore/S3AsyncBlobStore.java b/apis/s3/src/main/java/org/jclouds/s3/blobstore/S3AsyncBlobStore.java
index 7f2a5b6053..fe071f0626 100644
--- a/apis/s3/src/main/java/org/jclouds/s3/blobstore/S3AsyncBlobStore.java
+++ b/apis/s3/src/main/java/org/jclouds/s3/blobstore/S3AsyncBlobStore.java
@@ -34,7 +34,6 @@ import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.domain.BlobMetadata;
import org.jclouds.blobstore.domain.PageSet;
import org.jclouds.blobstore.domain.StorageMetadata;
-import org.jclouds.blobstore.domain.internal.PageSetImpl;
import org.jclouds.blobstore.functions.BlobToHttpGetOptions;
import org.jclouds.blobstore.internal.BaseAsyncBlobStore;
import org.jclouds.blobstore.options.CreateContainerOptions;
@@ -50,7 +49,6 @@ import org.jclouds.s3.S3AsyncClient;
import org.jclouds.s3.S3Client;
import org.jclouds.s3.blobstore.functions.BlobToObject;
import org.jclouds.s3.blobstore.functions.BucketToResourceList;
-import org.jclouds.s3.blobstore.functions.BucketToResourceMetadata;
import org.jclouds.s3.blobstore.functions.ContainerToBucketListOptions;
import org.jclouds.s3.blobstore.functions.ObjectToBlob;
import org.jclouds.s3.blobstore.functions.ObjectToBlobMetadata;
@@ -70,7 +68,6 @@ import com.google.common.base.Function;
import com.google.common.base.Supplier;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
-import com.google.common.collect.Iterables;
import com.google.common.util.concurrent.ListenableFuture;
/**
@@ -82,7 +79,7 @@ public class S3AsyncBlobStore extends BaseAsyncBlobStore {
private final S3AsyncClient async;
private final S3Client sync;
- private final BucketToResourceMetadata bucket2ResourceMd;
+ private final Function, PageSet extends StorageMetadata>> convertBucketsToStorageMetadata;
private final ContainerToBucketListOptions container2BucketListOptions;
private final BlobToHttpGetOptions blob2ObjectGetOptions;
private final BucketToResourceList bucket2ResourceList;
@@ -94,17 +91,18 @@ public class S3AsyncBlobStore extends BaseAsyncBlobStore {
@Inject
protected S3AsyncBlobStore(BlobStoreContext context, BlobUtils blobUtils,
- @Named(Constants.PROPERTY_USER_THREADS) ExecutorService service, Supplier defaultLocation,
- @Memoized Supplier> locations, S3AsyncClient async, S3Client sync,
- BucketToResourceMetadata bucket2ResourceMd, ContainerToBucketListOptions container2BucketListOptions,
- BucketToResourceList bucket2ResourceList, ObjectToBlob object2Blob,
- BlobToHttpGetOptions blob2ObjectGetOptions, BlobToObject blob2Object, ObjectToBlobMetadata object2BlobMd,
- Provider fetchBlobMetadataProvider, LoadingCache bucketAcls) {
+ @Named(Constants.PROPERTY_USER_THREADS) ExecutorService service, Supplier defaultLocation,
+ @Memoized Supplier> locations, S3AsyncClient async, S3Client sync,
+ Function, PageSet extends StorageMetadata>> convertBucketsToStorageMetadata,
+ ContainerToBucketListOptions container2BucketListOptions, BucketToResourceList bucket2ResourceList,
+ ObjectToBlob object2Blob, BlobToHttpGetOptions blob2ObjectGetOptions, BlobToObject blob2Object,
+ ObjectToBlobMetadata object2BlobMd, Provider fetchBlobMetadataProvider,
+ LoadingCache bucketAcls) {
super(context, blobUtils, service, defaultLocation, locations);
this.blob2ObjectGetOptions = checkNotNull(blob2ObjectGetOptions, "blob2ObjectGetOptions");
this.async = checkNotNull(async, "async");
this.sync = checkNotNull(sync, "sync");
- this.bucket2ResourceMd = checkNotNull(bucket2ResourceMd, "bucket2ResourceMd");
+ this.convertBucketsToStorageMetadata = checkNotNull(convertBucketsToStorageMetadata, "convertBucketsToStorageMetadata");
this.container2BucketListOptions = checkNotNull(container2BucketListOptions, "container2BucketListOptions");
this.bucket2ResourceList = checkNotNull(bucket2ResourceList, "bucket2ResourceList");
this.object2Blob = checkNotNull(object2Blob, "object2Blob");
@@ -122,7 +120,7 @@ public class S3AsyncBlobStore extends BaseAsyncBlobStore {
return Futures.compose(async.listOwnedBuckets(),
new Function, org.jclouds.blobstore.domain.PageSet extends StorageMetadata>>() {
public org.jclouds.blobstore.domain.PageSet extends StorageMetadata> apply(Set from) {
- return new PageSetImpl(Iterables.transform(from, bucket2ResourceMd), null);
+ return convertBucketsToStorageMetadata.apply(from);
}
}, service);
}
diff --git a/apis/s3/src/main/java/org/jclouds/s3/blobstore/S3BlobStore.java b/apis/s3/src/main/java/org/jclouds/s3/blobstore/S3BlobStore.java
index 6f8d01a925..41c6b4999a 100644
--- a/apis/s3/src/main/java/org/jclouds/s3/blobstore/S3BlobStore.java
+++ b/apis/s3/src/main/java/org/jclouds/s3/blobstore/S3BlobStore.java
@@ -27,11 +27,11 @@ import javax.inject.Provider;
import javax.inject.Singleton;
import org.jclouds.blobstore.BlobStoreContext;
+import org.jclouds.blobstore.ContainerNotFoundException;
import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.domain.BlobMetadata;
import org.jclouds.blobstore.domain.PageSet;
import org.jclouds.blobstore.domain.StorageMetadata;
-import org.jclouds.blobstore.domain.internal.PageSetImpl;
import org.jclouds.blobstore.functions.BlobToHttpGetOptions;
import org.jclouds.blobstore.internal.BaseBlobStore;
import org.jclouds.blobstore.options.CreateContainerOptions;
@@ -45,7 +45,6 @@ import org.jclouds.http.options.GetOptions;
import org.jclouds.s3.S3Client;
import org.jclouds.s3.blobstore.functions.BlobToObject;
import org.jclouds.s3.blobstore.functions.BucketToResourceList;
-import org.jclouds.s3.blobstore.functions.BucketToResourceMetadata;
import org.jclouds.s3.blobstore.functions.ContainerToBucketListOptions;
import org.jclouds.s3.blobstore.functions.ObjectToBlob;
import org.jclouds.s3.blobstore.functions.ObjectToBlobMetadata;
@@ -64,7 +63,6 @@ import com.google.common.base.Function;
import com.google.common.base.Supplier;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
-import com.google.common.collect.Iterables;
/**
*
@@ -73,7 +71,7 @@ import com.google.common.collect.Iterables;
@Singleton
public class S3BlobStore extends BaseBlobStore {
private final S3Client sync;
- private final BucketToResourceMetadata bucket2ResourceMd;
+ private final Function, PageSet extends StorageMetadata>> convertBucketsToStorageMetadata;
private final ContainerToBucketListOptions container2BucketListOptions;
private final BucketToResourceList bucket2ResourceList;
private final ObjectToBlob object2Blob;
@@ -85,15 +83,16 @@ public class S3BlobStore extends BaseBlobStore {
@Inject
protected S3BlobStore(BlobStoreContext context, BlobUtils blobUtils, Supplier defaultLocation,
- @Memoized Supplier> locations, S3Client sync,
- BucketToResourceMetadata bucket2ResourceMd, ContainerToBucketListOptions container2BucketListOptions,
- BucketToResourceList bucket2ResourceList, ObjectToBlob object2Blob,
- BlobToHttpGetOptions blob2ObjectGetOptions, BlobToObject blob2Object, ObjectToBlobMetadata object2BlobMd,
- Provider fetchBlobMetadataProvider, LoadingCache bucketAcls) {
+ @Memoized Supplier> locations, S3Client sync,
+ Function, PageSet extends StorageMetadata>> convertBucketsToStorageMetadata,
+ ContainerToBucketListOptions container2BucketListOptions, BucketToResourceList bucket2ResourceList,
+ ObjectToBlob object2Blob, BlobToHttpGetOptions blob2ObjectGetOptions, BlobToObject blob2Object,
+ ObjectToBlobMetadata object2BlobMd, Provider fetchBlobMetadataProvider,
+ LoadingCache bucketAcls) {
super(context, blobUtils, defaultLocation, locations);
this.blob2ObjectGetOptions = checkNotNull(blob2ObjectGetOptions, "blob2ObjectGetOptions");
this.sync = checkNotNull(sync, "sync");
- this.bucket2ResourceMd = checkNotNull(bucket2ResourceMd, "bucket2ResourceMd");
+ this.convertBucketsToStorageMetadata = checkNotNull(convertBucketsToStorageMetadata, "convertBucketsToStorageMetadata");
this.container2BucketListOptions = checkNotNull(container2BucketListOptions, "container2BucketListOptions");
this.bucket2ResourceList = checkNotNull(bucket2ResourceList, "bucket2ResourceList");
this.object2Blob = checkNotNull(object2Blob, "object2Blob");
@@ -108,11 +107,7 @@ public class S3BlobStore extends BaseBlobStore {
*/
@Override
public PageSet extends StorageMetadata> list() {
- return new Function, org.jclouds.blobstore.domain.PageSet extends StorageMetadata>>() {
- public org.jclouds.blobstore.domain.PageSet extends StorageMetadata> apply(Set from) {
- return new PageSetImpl(Iterables.transform(from, bucket2ResourceMd), null);
- }
- }.apply(sync.listOwnedBuckets());
+ return convertBucketsToStorageMetadata.apply(sync.listOwnedBuckets());
}
/**
@@ -169,10 +164,15 @@ public class S3BlobStore extends BaseBlobStore {
*/
public void clearAndDeleteContainer(final String container) {
try {
+ //TODO: probably it is better to use a retryable predicate
if (!Assertions.eventuallyTrue(new Supplier() {
public Boolean get() {
- clearContainer(container);
- return sync.deleteBucketIfEmpty(container);
+ try {
+ clearContainer(container);
+ return sync.deleteBucketIfEmpty(container);
+ } catch (ContainerNotFoundException e) {
+ return true;
+ }
}
}, 30000)) {
throw new IllegalStateException(container + " still exists after deleting!");
diff --git a/apis/s3/src/main/java/org/jclouds/s3/blobstore/config/S3BlobStoreContextModule.java b/apis/s3/src/main/java/org/jclouds/s3/blobstore/config/S3BlobStoreContextModule.java
index 709333e7df..c05acb54d7 100644
--- a/apis/s3/src/main/java/org/jclouds/s3/blobstore/config/S3BlobStoreContextModule.java
+++ b/apis/s3/src/main/java/org/jclouds/s3/blobstore/config/S3BlobStoreContextModule.java
@@ -35,7 +35,7 @@ import org.jclouds.s3.S3Client;
import org.jclouds.s3.blobstore.S3AsyncBlobStore;
import org.jclouds.s3.blobstore.S3BlobRequestSigner;
import org.jclouds.s3.blobstore.S3BlobStore;
-import org.jclouds.s3.blobstore.functions.LocationFromBucketLocation;
+import org.jclouds.s3.blobstore.functions.LocationFromBucketName;
import org.jclouds.s3.domain.AccessControlList;
import org.jclouds.s3.domain.BucketMetadata;
@@ -63,7 +63,8 @@ public class S3BlobStoreContextModule extends AbstractModule {
bind(BlobStore.class).to(S3BlobStore.class).in(Scopes.SINGLETON);
bindContext();
bind(BlobRequestSigner.class).to(S3BlobRequestSigner.class);
- bindBucketLocationStrategy();
+ bind(new TypeLiteral>() {
+ }).to(LocationFromBucketName.class);
}
protected void bindContext() {
@@ -71,11 +72,6 @@ public class S3BlobStoreContextModule extends AbstractModule {
}).in(Scopes.SINGLETON);
}
- protected void bindBucketLocationStrategy() {
- bind(new TypeLiteral>() {
- }).to(LocationFromBucketLocation.class);
- }
-
@Provides
@Singleton
protected LoadingCache bucketAcls(final S3Client client) {
diff --git a/apis/s3/src/main/java/org/jclouds/s3/blobstore/functions/BucketToResourceMetadata.java b/apis/s3/src/main/java/org/jclouds/s3/blobstore/functions/BucketToResourceMetadata.java
index 8fc3212750..b7aa78b328 100644
--- a/apis/s3/src/main/java/org/jclouds/s3/blobstore/functions/BucketToResourceMetadata.java
+++ b/apis/s3/src/main/java/org/jclouds/s3/blobstore/functions/BucketToResourceMetadata.java
@@ -36,10 +36,10 @@ import com.google.common.base.Function;
*/
@Singleton
public class BucketToResourceMetadata implements Function {
- private final Function locationOfBucket;
+ private final Function locationOfBucket;
@Inject
- BucketToResourceMetadata(Function locationOfBucket) {
+ BucketToResourceMetadata(Function locationOfBucket) {
this.locationOfBucket = locationOfBucket;
}
@@ -47,7 +47,7 @@ public class BucketToResourceMetadata implements Function, PageSet extends StorageMetadata>> {
+
+ @Resource
+ protected Logger logger = Logger.NULL;
+
+ private final ExecutorService userExecutor;
+ private final BucketToResourceMetadata bucket2ResourceMd;
+
+ @Inject
+ public BucketsToStorageMetadata(@Named(Constants.PROPERTY_USER_THREADS) ExecutorService userExecutor, BucketToResourceMetadata bucket2ResourceMd) {
+ this.userExecutor = checkNotNull(userExecutor, "userExecutor");
+ this.bucket2ResourceMd = checkNotNull(bucket2ResourceMd, "bucket2ResourceMd");
+ }
+
+
+ @Override
+ public PageSet extends StorageMetadata> apply(Set input) {
+ // parallel as listing buckets is slow when looking up regions
+ Iterable extends StorageMetadata> buckets = FutureIterables
+ . transformParallel(input,
+ new Function>() {
+ @Override
+ public Future apply(final BucketMetadata from) {
+ return userExecutor.submit(new Callable() {
+
+ @Override
+ public StorageMetadata call() throws Exception {
+ return bucket2ResourceMd.apply(from);
+ }
+
+ @Override
+ public String toString() {
+ return "bucket2ResourceMd.apply(" + from + ")";
+ }
+ });
+ }
+
+ }, userExecutor, null, logger, "my buckets");
+ return new PageSetImpl(buckets, null);
+ }
+
+}
diff --git a/apis/s3/src/main/java/org/jclouds/s3/blobstore/functions/LocationFromBucketLocation.java b/apis/s3/src/main/java/org/jclouds/s3/blobstore/functions/LocationFromBucketLocation.java
deleted file mode 100644
index 49c765f74b..0000000000
--- a/apis/s3/src/main/java/org/jclouds/s3/blobstore/functions/LocationFromBucketLocation.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/**
- * Licensed to jclouds, Inc. (jclouds) under one or more
- * contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. jclouds 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.s3.blobstore.functions;
-
-import java.util.NoSuchElementException;
-import java.util.Set;
-
-import javax.annotation.Resource;
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
-import org.jclouds.s3.S3Client;
-import org.jclouds.s3.domain.BucketMetadata;
-import org.jclouds.blobstore.ContainerNotFoundException;
-import org.jclouds.collect.Memoized;
-import org.jclouds.domain.Location;
-import org.jclouds.logging.Logger;
-
-import com.google.common.base.Function;
-import com.google.common.base.Predicate;
-import com.google.common.base.Supplier;
-import com.google.common.collect.Iterables;
-
-/**
- * @author Adrian Cole
- */
-@Singleton
-public class LocationFromBucketLocation implements Function {
- private final Location onlyLocation;
- private final Supplier> locations;
- private final S3Client client;
-
- @Resource
- protected Logger logger = Logger.NULL;
-
- @Inject
- LocationFromBucketLocation(S3Client client, @Memoized Supplier> locations) {
- this.client = client;
- this.onlyLocation = locations.get().size() == 1 ? Iterables.get(locations.get(), 0) : null;
- this.locations = locations;
- }
-
- public Location apply(BucketMetadata from) {
- if (onlyLocation != null)
- return onlyLocation;
- try {
- Set extends Location> locations = this.locations.get();
- final String region = client.getBucketLocation(from.getName());
- assert region != null : String.format("could not get region for %s", from.getName());
- if (region != null) {
- try {
- return Iterables.find(locations, new Predicate() {
-
- @Override
- public boolean apply(Location input) {
- return input.getId().equalsIgnoreCase(region.toString());
- }
-
- });
- } catch (NoSuchElementException e) {
- logger.error("could not get location for region %s in %s", region, locations);
- }
- } else {
- logger.error("could not get region for %s", from.getName());
- }
- } catch (ContainerNotFoundException e) {
- logger.error(e, "could not get region for %s, as service suggests the bucket doesn't exist", from.getName());
- }
- return null;
- }
-}
diff --git a/apis/s3/src/main/java/org/jclouds/s3/blobstore/functions/LocationFromBucketName.java b/apis/s3/src/main/java/org/jclouds/s3/blobstore/functions/LocationFromBucketName.java
new file mode 100644
index 0000000000..f4ec53e5e9
--- /dev/null
+++ b/apis/s3/src/main/java/org/jclouds/s3/blobstore/functions/LocationFromBucketName.java
@@ -0,0 +1,75 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. jclouds 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.s3.blobstore.functions;
+
+import static com.google.common.collect.Iterables.find;
+import static com.google.common.collect.Iterables.get;
+import static org.jclouds.location.predicates.LocationPredicates.idEquals;
+
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+import javax.annotation.Resource;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.jclouds.collect.Memoized;
+import org.jclouds.domain.Location;
+import org.jclouds.logging.Logger;
+import org.jclouds.s3.Bucket;
+
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.base.Supplier;
+
+/**
+ * @author Adrian Cole
+ */
+@Singleton
+public class LocationFromBucketName implements Function {
+ private final Supplier> locations;
+ private final Function> bucketToRegion;
+
+ @Resource
+ protected Logger logger = Logger.NULL;
+
+ @Inject
+ LocationFromBucketName(@Bucket Function> bucketToRegion,
+ @Memoized Supplier> locations) {
+ this.bucketToRegion = bucketToRegion;
+ this.locations = locations;
+ }
+
+ public Location apply(String bucket) {
+ Set extends Location> locations = this.locations.get();
+ if (locations.size() == 1)
+ return get(locations, 0);
+ final Optional region = bucketToRegion.apply(bucket);
+ if (region.isPresent()) {
+ try {
+ return find(locations, idEquals(region.get()));
+ } catch (NoSuchElementException e) {
+ logger.debug("could not get location for region %s in %s", region, locations);
+ }
+ } else {
+ logger.debug("could not get region for %s", bucket);
+ }
+ return null;
+ }
+}
diff --git a/apis/s3/src/main/java/org/jclouds/s3/blobstore/functions/ObjectToBlobMetadata.java b/apis/s3/src/main/java/org/jclouds/s3/blobstore/functions/ObjectToBlobMetadata.java
index 3fe757a005..64d11a6c5a 100644
--- a/apis/s3/src/main/java/org/jclouds/s3/blobstore/functions/ObjectToBlobMetadata.java
+++ b/apis/s3/src/main/java/org/jclouds/s3/blobstore/functions/ObjectToBlobMetadata.java
@@ -25,6 +25,7 @@ import org.jclouds.blobstore.domain.MutableBlobMetadata;
import org.jclouds.blobstore.domain.StorageType;
import org.jclouds.blobstore.domain.internal.MutableBlobMetadataImpl;
import org.jclouds.blobstore.strategy.IfDirectoryReturnNameStrategy;
+import org.jclouds.domain.Location;
import org.jclouds.http.HttpUtils;
import org.jclouds.s3.domain.AccessControlList;
import org.jclouds.s3.domain.ObjectMetadata;
@@ -42,12 +43,14 @@ import com.google.common.cache.LoadingCache;
public class ObjectToBlobMetadata implements Function {
private final IfDirectoryReturnNameStrategy ifDirectoryReturnName;
private final LoadingCache bucketAcls;
+ private final Function locationOfBucket;
@Inject
public ObjectToBlobMetadata(IfDirectoryReturnNameStrategy ifDirectoryReturnName,
- LoadingCache bucketAcls) {
+ LoadingCache bucketAcls,Function locationOfBucket) {
this.ifDirectoryReturnName = ifDirectoryReturnName;
this.bucketAcls = bucketAcls;
+ this.locationOfBucket = locationOfBucket;
}
public MutableBlobMetadata apply(ObjectMetadata from) {
@@ -68,6 +71,7 @@ public class ObjectToBlobMetadata implements Function ext
@Provides
@Bucket
@Singleton
- protected Map bucketToRegion() {
- return Maps.newConcurrentMap();
+ protected CacheLoader> bucketToRegion(@Region Supplier> regionSupplier,
+ final S3Client client) {
+ Set regions = regionSupplier.get();
+ if (regions.size() == 0) {
+ return new CacheLoader>() {
+
+ @Override
+ public Optional load(String bucket) {
+ return Optional.absent();
+ }
+
+ @Override
+ public String toString() {
+ return "noRegions()";
+ }
+ };
+ } else if (regions.size() == 1) {
+ final String onlyRegion = Iterables.getOnlyElement(regions);
+ return new CacheLoader>() {
+ Optional onlyRegionOption = Optional.of(onlyRegion);
+
+ @Override
+ public Optional load(String bucket) {
+ return onlyRegionOption;
+ }
+
+ @Override
+ public String toString() {
+ return "onlyRegion(" + onlyRegion + ")";
+ }
+ };
+ } else {
+ return new CacheLoader>() {
+ @Override
+ public Optional load(String bucket) {
+ try {
+ return Optional.fromNullable(client.getBucketLocation(bucket));
+ } catch (ContainerNotFoundException e) {
+ return null;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "bucketToRegion()";
+ }
+ };
+ }
+ }
+
+ @Provides
+ @Bucket
+ @Singleton
+ protected LoadingCache> bucketToRegion(@Bucket CacheLoader> loader) {
+ return CacheBuilder.newBuilder().build(loader);
}
@Provides
@@ -80,12 +149,24 @@ public class S3RestClientModule ext
return defaultRegion;
}
+ @Provides
+ @Singleton
+ @Bucket
+ protected Supplier provideBucketURI(@Bucket Supplier defaultRegion,
+ RegionToEndpointOrProviderIfNull regionToEndpoint) {
+ return Suppliers.compose(regionToEndpoint, defaultRegion);
+ }
+
@Override
protected void configure() {
+ super.configure();
install(new S3ObjectModule());
install(new S3ParserModule());
bind(RequestAuthorizeSignature.class).in(Scopes.SINGLETON);
- super.configure();
+ bind(new TypeLiteral>>() {
+ }).annotatedWith(Bucket.class).to(GetRegionForBucket.class);
+ bind(new TypeLiteral, PageSet extends StorageMetadata>>>() {
+ }).to(BucketsToStorageMetadata.class);
}
@Override
@@ -104,6 +185,7 @@ public class S3RestClientModule ext
@Override
protected void bindRetryHandlers() {
bind(HttpRetryHandler.class).annotatedWith(Redirection.class).to(S3RedirectionRetryHandler.class);
+ bind(HttpRetryHandler.class).annotatedWith(ClientError.class).to(AWSClientErrorRetryHandler.class);
}
@Provides
diff --git a/apis/s3/src/main/java/org/jclouds/s3/domain/BucketMetadata.java b/apis/s3/src/main/java/org/jclouds/s3/domain/BucketMetadata.java
index 27be757a28..9723487824 100644
--- a/apis/s3/src/main/java/org/jclouds/s3/domain/BucketMetadata.java
+++ b/apis/s3/src/main/java/org/jclouds/s3/domain/BucketMetadata.java
@@ -27,8 +27,8 @@ import java.util.Date;
*/
public class BucketMetadata implements Comparable {
private final Date creationDate;
- private final CanonicalUser owner;
private final String name;
+ private final CanonicalUser owner;
public BucketMetadata(String name, Date creationDate, CanonicalUser owner) {
this.name = name;
diff --git a/apis/s3/src/main/java/org/jclouds/s3/functions/AssignCorrectHostnameForBucket.java b/apis/s3/src/main/java/org/jclouds/s3/functions/AssignCorrectHostnameForBucket.java
new file mode 100644
index 0000000000..e1adae7be4
--- /dev/null
+++ b/apis/s3/src/main/java/org/jclouds/s3/functions/AssignCorrectHostnameForBucket.java
@@ -0,0 +1,64 @@
+/**
+ * Licensed to jclouds, Inc. (jclouds) under one or more
+ * contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. jclouds 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.s3.functions;
+
+import java.net.URI;
+
+import javax.annotation.Resource;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.jclouds.javax.annotation.Nullable;
+import org.jclouds.location.functions.RegionToEndpointOrProviderIfNull;
+import org.jclouds.logging.Logger;
+import org.jclouds.s3.Bucket;
+
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+
+/**
+ *
+ * @author Adrian Cole
+ */
+@Singleton
+public class AssignCorrectHostnameForBucket implements Function