From 557d28783f838fd24ecac042d872ea3693267d48 Mon Sep 17 00:00:00 2001 From: "adrian.f.cole" Date: Mon, 5 Oct 2009 16:27:48 +0000 Subject: [PATCH] Issue 109, Issue 73: more work on compatibility layer; expose vendor interface as getApi(); more Strategies git-svn-id: http://jclouds.googlecode.com/svn/trunk@1946 3d8758e0-26b5-11de-8745-db77d3ebf521 --- .../java/org/jclouds/aws/s3/S3BlobStore.java | 212 +----- .../java/org/jclouds/aws/s3/S3Connection.java | 426 +++++++++++ .../java/org/jclouds/aws/s3/S3Context.java | 2 +- .../org/jclouds/aws/s3/S3ContextBuilder.java | 4 +- .../aws/s3/binders/S3ObjectBinder.java | 3 +- .../aws/s3/config/RestS3ConnectionModule.java | 13 +- .../aws/s3/config/S3ContextModule.java | 14 +- .../ClearAndDeleteBucketIfNotEmpty.java | 45 ++ .../jclouds/aws/s3/xml/ListBucketHandler.java | 1 + .../jclouds/aws/s3/S3ConnectionLiveTest.java | 705 ++++++++++++++++++ .../jclouds/aws/s3/S3ContextBuilderTest.java | 4 + .../aws/s3/config/S3ContextModuleTest.java | 4 +- .../aws/s3/config/StubS3BlobStoreModule.java | 13 +- .../s3/integration/S3BlobIntegrationTest.java | 388 +--------- .../aws/s3/integration/S3BlobLiveTest.java | 54 +- .../integration/S3BlobMapIntegrationTest.java | 6 +- .../S3ContainerIntegrationTest.java | 152 +--- .../s3/integration/S3ContainerLiveTest.java | 75 +- .../S3InputStreamMapIntegrationTest.java | 6 +- .../integration/S3ServiceIntegrationTest.java | 18 +- .../aws/s3/integration/S3TestInitializer.java | 8 +- ...S3BlobStore.java => StubS3Connection.java} | 67 +- .../aws/s3/jets3t/JCloudsS3Service.java | 32 +- .../s3/jets3t/JCloudsS3ServiceLiveTest.java | 34 +- .../s3/BaseJCloudsPerformanceLiveTest.java | 8 +- .../aws/s3/BasePerformanceLiveTest.java | 4 +- .../org/jclouds/aws/s3/samples/MainApp.java | 4 +- .../GetAllBucketsController.java | 2 +- .../googleappengine/JCloudsServlet.java | 4 +- .../storage/blob/AzureBlobConnection.java | 316 ++++++++ .../azure/storage/blob/AzureBlobContext.java | 2 +- .../storage/blob/AzureBlobContextBuilder.java | 12 +- .../azure/storage/blob/AzureBlobStore.java | 83 +-- .../azure/storage/blob/AzureBlobUtil.java | 63 ++ .../storage/blob/binders/BlobBinder.java | 3 +- .../blob/config/AzureBlobContextModule.java | 15 +- .../blob/config/RestAzureBlobStoreModule.java | 29 +- .../blob/domain/ContainerMetadata.java | 46 +- .../functions/MD5EnforcingBlobBinder.java | 55 ++ .../ParseContainerMetadataFromHeaders.java | 120 +++ .../AzureBlobClientErrorRetryHandler.java | 92 +++ .../blob/xml/AddMD5ToListBlobsResponse.java | 62 ++ ....java => AzureBlobConnectionLiveTest.java} | 21 +- .../storage/blob/AzureBlobConnectionTest.java | 357 +++++++++ .../blob/AzureBlobContextBuilderTest.java | 21 +- .../blob/AzureBlobContextModuleTest.java | 76 ++ .../storage/blob/AzureBlobStoreTest.java | 170 ++--- .../RestAzureBlobConnectionModuleTest.java | 94 +++ .../blob/config/StubAzureBlobStoreModule.java | 13 +- .../AzureBlobContainerIntegrationTest.java | 11 +- .../AzureBlobContainerLiveTest.java | 6 +- ...zureBlobInputStreamMapIntegrationTest.java | 35 +- .../integration/AzureBlobIntegrationTest.java | 8 +- .../blob/integration/AzureBlobLiveTest.java | 6 +- .../AzureBlobMapIntegrationTest.java | 13 +- .../AzureBlobServiceIntegrationTest.java | 6 +- .../integration/AzureBlobTestInitializer.java | 8 +- ...tore.java => StubAzureBlobConnection.java} | 82 +- .../xml/AddMD5ToListBlobsResponseTest.java | 133 ++++ .../queue/AzureQueueConnectionLiveTest.java | 8 +- .../java/org/jclouds/blobstore/BlobStore.java | 8 +- .../jclouds/blobstore/BlobStoreContext.java | 4 +- .../blobstore/BlobStoreContextBuilder.java | 11 +- .../blobstore/BlobStoreContextImpl.java | 11 +- .../blobstore/BlobStoreMapsModule.java | 147 +++- .../jclouds/blobstore/binders/BlobBinder.java | 3 +- .../functions/ClearAndDeleteIfNotEmpty.java | 111 +++ .../blobstore/functions/ObjectMD5.java | 64 ++ ...04.java => ReturnVoidOnNotFoundOr404.java} | 25 +- .../blobstore/internal/BaseBlobMap.java | 92 +-- .../blobstore/internal/BlobMapImpl.java | 15 +- .../internal/BlobRuntimeException.java | 36 + .../internal/InputStreamMapImpl.java | 15 +- .../strategy/ClearContainerStrategy.java | 40 + .../strategy/ContainerCountStrategy.java | 40 + .../strategy/ContainsValueStrategy.java | 40 + ...ntainerListGetAllBlobMetadataStrategy.java | 2 +- .../ContentMD5ContainsValueStrategy.java | 74 ++ .../DeleteAllKeysClearContainerStrategy.java | 80 ++ .../strategy/internal/KeyCountStrategy.java | 53 ++ .../RetryOnNotFoundGetAllBlobsStrategy.java | 2 +- .../blobstore/BlobStoreMapsModuleTest.java | 63 +- .../StubBlobStoreContextBuilder.java | 2 +- .../internal/BaseBlobIntegrationTest.java | 53 +- .../internal/BaseBlobLiveTest.java | 7 +- .../internal/BaseBlobMapIntegrationTest.java | 3 +- .../BaseBlobStoreIntegrationTest.java | 120 +-- .../BaseContainerIntegrationTest.java | 73 +- .../internal/BaseContainerLiveTest.java | 3 +- .../BaseInputStreamMapIntegrationTest.java | 3 +- .../internal/BaseMapIntegrationTest.java | 3 +- .../internal/BaseServiceIntegrationTest.java | 5 +- .../internal/BaseTestInitializer.java | 3 +- .../integration/internal/StubBlobStore.java | 22 +- .../blobstore/internal/BaseBlobMapTest.java | 4 +- .../functions/ParseContentMD5FromHeaders.java | 91 +++ .../jclouds/http/functions/ParseURIList.java | 56 ++ .../http/functions/ReturnInputStream.java | 19 +- .../http/functions/ReturnVoidIf2xx.java | 59 ++ .../handlers/BackoffLimitedRetryHandler.java | 7 +- .../rest/JaxrsAnnotationProcessor.java | 31 +- .../org/jclouds/rest/RestClientProxy.java | 7 + .../rest/JaxrsAnnotationProcessorTest.java | 137 +++- .../org/jclouds/mezeo/pcs2/PCSBlobStore.java | 15 +- .../org/jclouds/mezeo/pcs2/PCSConnection.java | 104 +++ .../org/jclouds/mezeo/pcs2/PCSContext.java | 2 +- .../jclouds/mezeo/pcs2/PCSContextBuilder.java | 4 +- .../mezeo/pcs2/config/PCSContextModule.java | 18 +- .../pcs2/config/RestPCSBlobStoreModule.java | 13 +- .../pcs2/functions/FindIdInContainerList.java | 32 +- .../pcs2/functions/FindIdInFileList.java | 34 +- ...eContainerNameCacheAndReturnTrueIf2xx.java | 23 + ...alidatePCSKeyCacheAndReturnVoidIf2xx.java} | 31 +- .../org/jclouds/mezeo/pcs2/functions/Key.java | 23 + .../handlers/PCSClientErrorRetryHandler.java | 23 + .../mezeo/pcs2/PCSBlobStoreLiveTest.java | 203 ----- .../jclouds/mezeo/pcs2/PCSBlobStoreTest.java | 161 ++-- .../mezeo/pcs2/PCSConnectionLiveTest.java | 135 ++++ .../jclouds/mezeo/pcs2/PCSConnectionTest.java | 273 +++++++ .../mezeo/pcs2/PCSContextBuilderTest.java | 29 +- .../mezeo/pcs2/PCSContextModuleTest.java | 72 ++ .../pcs2/config/PCSContextModuleTest.java | 27 +- .../pcs2/config/StubPCSBlobStoreModule.java | 15 +- .../functions/FindIdInContainerListTest.java | 6 +- .../functions/FindIdInFileListIdTest.java | 13 +- .../PCSBlobMapIntegrationTest.java | 4 +- .../PCSBlobStoreContainerIntegrationTest.java | 19 +- .../PCSBlobStoreContainerLiveTest.java | 4 +- .../PCSBlobStoreIntegrationTest.java | 4 +- .../integration/PCSBlobStoreLiveTest.java | 4 +- .../PCSBlobStoreServiceIntegrationTest.java | 4 +- .../PCSInputStreamMapIntegrationTest.java | 4 +- .../pcs2/integration/PCSTestInitializer.java | 4 +- ...SBlobStore.java => StubPCSConnection.java} | 51 +- .../cloudfiles/CloudFilesBlobStore.java | 84 +-- .../cloudfiles/CloudFilesConnection.java | 228 ++++++ .../cloudfiles/CloudFilesContext.java | 5 +- .../cloudfiles/CloudFilesContextBuilder.java | 4 +- .../cloudfiles/binders/CFObjectBinder.java | 3 + .../config/CloudFilesContextModule.java | 12 +- .../config/RestCloudFilesBlobStoreModule.java | 14 +- .../ClearAndDeleteContainerIfNotEmpty.java | 45 ++ ...ParseBlobMetadataListFromJsonResponse.java | 1 + ...java => CloudFilesConnectionLiveTest.java} | 70 +- .../CloudFilesContextBuilderTest.java | 15 +- .../CloudFilesContextModuleTest.java | 74 ++ .../CloudFilesBlobIntegrationTest.java | 4 +- .../integration/CloudFilesBlobLiveTest.java | 7 +- .../CloudFilesBlobMapIntegrationTest.java | 4 +- .../CloudFilesContainerIntegrationTest.java | 4 +- .../CloudFilesContainerLiveTest.java | 4 +- ...oudFilesInputStreamMapIntegrationTest.java | 4 +- .../CloudFilesServiceIntegrationTest.java | 10 +- .../CloudFilesTestInitializer.java | 10 +- ...ava => StubCloudFilesBlobStoreModule.java} | 16 +- ...ore.java => StubCloudFilesConnection.java} | 62 +- 156 files changed, 5698 insertions(+), 2254 deletions(-) create mode 100644 aws/s3/core/src/main/java/org/jclouds/aws/s3/S3Connection.java create mode 100644 aws/s3/core/src/main/java/org/jclouds/aws/s3/functions/ClearAndDeleteBucketIfNotEmpty.java create mode 100644 aws/s3/core/src/test/java/org/jclouds/aws/s3/S3ConnectionLiveTest.java rename aws/s3/core/src/test/java/org/jclouds/aws/s3/internal/{StubS3BlobStore.java => StubS3Connection.java} (84%) create mode 100644 azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/AzureBlobConnection.java create mode 100644 azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/AzureBlobUtil.java create mode 100644 azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/functions/MD5EnforcingBlobBinder.java create mode 100644 azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/functions/ParseContainerMetadataFromHeaders.java create mode 100644 azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/handlers/AzureBlobClientErrorRetryHandler.java create mode 100644 azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/xml/AddMD5ToListBlobsResponse.java rename azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/{AzureBlobStoreLiveTest.java => AzureBlobConnectionLiveTest.java} (94%) create mode 100644 azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/AzureBlobConnectionTest.java create mode 100644 azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/AzureBlobContextModuleTest.java create mode 100644 azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/config/RestAzureBlobConnectionModuleTest.java rename azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/internal/{StubAzureBlobStore.java => StubAzureBlobConnection.java} (65%) create mode 100644 azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/xml/AddMD5ToListBlobsResponseTest.java create mode 100644 blobstore/core/src/main/java/org/jclouds/blobstore/functions/ClearAndDeleteIfNotEmpty.java create mode 100644 blobstore/core/src/main/java/org/jclouds/blobstore/functions/ObjectMD5.java rename blobstore/core/src/main/java/org/jclouds/blobstore/functions/{ReturnTrueOnNotFoundOr404.java => ReturnVoidOnNotFoundOr404.java} (69%) create mode 100644 blobstore/core/src/main/java/org/jclouds/blobstore/internal/BlobRuntimeException.java create mode 100644 blobstore/core/src/main/java/org/jclouds/blobstore/strategy/ClearContainerStrategy.java create mode 100644 blobstore/core/src/main/java/org/jclouds/blobstore/strategy/ContainerCountStrategy.java create mode 100644 blobstore/core/src/main/java/org/jclouds/blobstore/strategy/ContainsValueStrategy.java create mode 100644 blobstore/core/src/main/java/org/jclouds/blobstore/strategy/internal/ContentMD5ContainsValueStrategy.java create mode 100644 blobstore/core/src/main/java/org/jclouds/blobstore/strategy/internal/DeleteAllKeysClearContainerStrategy.java create mode 100644 blobstore/core/src/main/java/org/jclouds/blobstore/strategy/internal/KeyCountStrategy.java create mode 100644 core/src/main/java/org/jclouds/http/functions/ParseContentMD5FromHeaders.java create mode 100644 core/src/main/java/org/jclouds/http/functions/ParseURIList.java rename mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/functions/ReturnTrueIfContainerNotFound.java => core/src/main/java/org/jclouds/http/functions/ReturnInputStream.java (77%) create mode 100644 core/src/main/java/org/jclouds/http/functions/ReturnVoidIf2xx.java create mode 100644 mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/PCSConnection.java rename mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/functions/{InvalidatePCSKeyCacheAndReturnTrueIf2xx.java => InvalidatePCSKeyCacheAndReturnVoidIf2xx.java} (52%) delete mode 100644 mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/PCSBlobStoreLiveTest.java create mode 100644 mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/PCSConnectionLiveTest.java create mode 100644 mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/PCSConnectionTest.java create mode 100644 mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/PCSContextModuleTest.java rename mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/internal/{StubPCSBlobStore.java => StubPCSConnection.java} (60%) create mode 100644 rackspace/cloudfiles/core/src/main/java/org/jclouds/rackspace/cloudfiles/CloudFilesConnection.java create mode 100644 rackspace/cloudfiles/core/src/main/java/org/jclouds/rackspace/cloudfiles/functions/ClearAndDeleteContainerIfNotEmpty.java rename rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/{CloudFilesBlobStoreLiveTest.java => CloudFilesConnectionLiveTest.java} (84%) create mode 100644 rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/CloudFilesContextModuleTest.java rename rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/integration/{StubCloudFilesConnectionModule.java => StubCloudFilesBlobStoreModule.java} (72%) rename rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/internal/{StubCloudFilesBlobStore.java => StubCloudFilesConnection.java} (60%) diff --git a/aws/s3/core/src/main/java/org/jclouds/aws/s3/S3BlobStore.java b/aws/s3/core/src/main/java/org/jclouds/aws/s3/S3BlobStore.java index d79381feb2..52dc60367e 100644 --- a/aws/s3/core/src/main/java/org/jclouds/aws/s3/S3BlobStore.java +++ b/aws/s3/core/src/main/java/org/jclouds/aws/s3/S3BlobStore.java @@ -34,30 +34,23 @@ import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; -import org.jclouds.aws.s3.binders.AccessControlListBinder; import org.jclouds.aws.s3.binders.S3ObjectBinder; -import org.jclouds.aws.s3.domain.AccessControlList; import org.jclouds.aws.s3.domain.BucketMetadata; import org.jclouds.aws.s3.domain.ListBucketResponse; import org.jclouds.aws.s3.domain.ObjectMetadata; import org.jclouds.aws.s3.domain.S3Object; import org.jclouds.aws.s3.filters.RequestAuthorizeSignature; +import org.jclouds.aws.s3.functions.ClearAndDeleteBucketIfNotEmpty; import org.jclouds.aws.s3.functions.ParseObjectFromHeadersAndHttpContent; import org.jclouds.aws.s3.functions.ParseObjectMetadataFromHeaders; import org.jclouds.aws.s3.functions.ReturnTrueIfBucketAlreadyOwnedByYou; -import org.jclouds.aws.s3.functions.ReturnTrueOn404FalseIfNotEmpty; -import org.jclouds.aws.s3.options.CopyObjectOptions; import org.jclouds.aws.s3.options.ListBucketOptions; -import org.jclouds.aws.s3.options.PutBucketOptions; -import org.jclouds.aws.s3.options.PutObjectOptions; -import org.jclouds.aws.s3.xml.AccessControlListHandler; -import org.jclouds.aws.s3.xml.CopyObjectHandler; import org.jclouds.aws.s3.xml.ListAllMyBucketsHandler; import org.jclouds.aws.s3.xml.ListBucketHandler; import org.jclouds.blobstore.BlobStore; import org.jclouds.blobstore.domain.Blob; import org.jclouds.blobstore.functions.BlobKey; -import org.jclouds.blobstore.functions.ThrowContainerNotFoundOn404; +import org.jclouds.blobstore.functions.ReturnVoidOnNotFoundOr404; import org.jclouds.blobstore.functions.ThrowKeyNotFoundOn404; import org.jclouds.http.functions.ParseETagHeader; import org.jclouds.http.functions.ReturnFalseOn404; @@ -65,7 +58,6 @@ import org.jclouds.http.options.GetOptions; import org.jclouds.rest.Endpoint; import org.jclouds.rest.EntityParam; import org.jclouds.rest.ExceptionParser; -import org.jclouds.rest.Headers; import org.jclouds.rest.HostPrefixParam; import org.jclouds.rest.ParamParser; import org.jclouds.rest.QueryParams; @@ -74,9 +66,6 @@ import org.jclouds.rest.ResponseParser; import org.jclouds.rest.SkipEncoding; import org.jclouds.rest.VirtualHost; import org.jclouds.rest.XMLResponseParser; -import org.jclouds.rest.binders.HttpRequestOptionsBinder; - -import com.google.inject.internal.Nullable; /** * Provides access to S3 via their REST API. @@ -184,38 +173,8 @@ public interface S3BlobStore extends BlobStore removeBlob(@HostPrefixParam String bucketName, @PathParam("key") String key); - - /** - * Store data by creating or overwriting an object. - *

- * This method will store the object with the default private acl. - * - *

- * This returns a byte[] of the eTag hash of what Amazon S3 received - *

- * - * @param bucketName - * namespace of the object you are storing - * @param object - * contains the data and metadata to create or overwrite - * @param options - * options for creating the object - * @return MD5 hash of the content uploaded - * @throws org.jclouds.http.HttpResponseException - * if the conditions requested set are not satisfied by the object on the server. - * @see org.jclouds.aws.s3.domain.CannedAccessPolicy#PRIVATE - * @see - */ - @PUT - @Path("{key}") - @ResponseParser(ParseETagHeader.class) - Future putBlob( - @HostPrefixParam String bucketName, - @PathParam("key") @ParamParser(BlobKey.class) @EntityParam(S3ObjectBinder.class) S3Object object, - PutObjectOptions options); + @ExceptionParser(ReturnVoidOnNotFoundOr404.class) + Future removeBlob(@HostPrefixParam String bucketName, @PathParam("key") String key); @PUT @Path("{key}") @@ -224,33 +183,6 @@ public interface S3BlobStore extends BlobStore - * you can use {@link PutBucketOptions} to create the bucket in EU. - *

- * The PUT request operation with a bucket URI creates a new bucket. 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 via - * {@link PutBucketOptions}. - * - * @param options - * for creating your bucket - * @return true, if the bucket was created or already exists - * - * @see PutBucketOptions - * @see - * - */ - @PUT - @Path("/") - @ExceptionParser(ReturnTrueIfBucketAlreadyOwnedByYou.class) - Future createContainer(@HostPrefixParam String bucketName, - @Nullable @EntityParam(HttpRequestOptionsBinder.class) PutBucketOptions options); - @PUT @Path("/") @ExceptionParser(ReturnTrueIfBucketAlreadyOwnedByYou.class) @@ -267,7 +199,6 @@ public interface S3BlobStore extends BlobStore deleteContainer(@HostPrefixParam String bucketName); + @ExceptionParser(ClearAndDeleteBucketIfNotEmpty.class) + Future deleteContainer(@HostPrefixParam String bucketName); /** * Issues a HEAD command to determine if the bucket exists or not. @@ -308,12 +239,6 @@ public interface S3BlobStore extends BlobStore listBlobs(@HostPrefixParam String bucketName, - @Nullable ListBucketOptions options); - - @GET - @Path("/") - @XMLResponseParser(ListBucketHandler.class) Future listBlobs(@HostPrefixParam String bucketName); /** @@ -330,129 +255,4 @@ public interface S3BlobStore extends BlobStore listContainers(); - /** - * Copies one object to another bucket, retaining UserMetadata from the source. The destination - * will have a private acl. The copy operation creates a copy of an object that is already stored - * in Amazon S3. - *

- * When copying an object, you can preserve all metadata (default) or - * {@link CopyObjectOptions#overrideMetadataWith(com.google.common.collect.Multimap) specify new - * metadata}. However, the ACL is not preserved and is set to private for the user making the - * request. To override the default ACL setting, - * {@link CopyObjectOptions#overrideAcl(org.jclouds.aws.s3.domain.CannedAccessPolicy) specify a - * new ACL} when generating a copy request. - * - * @return metadata populated with lastModified and eTag of the new object - * @see org.jclouds.aws.s3.commands.CopyObject - * @see - * @throws org.jclouds.http.HttpResponseException - * if the conditions requested set are not satisfied by the object on the server. - * @see CopyObjectOptions - * @see org.jclouds.aws.s3.domain.CannedAccessPolicy - */ - @PUT - @Path("{destinationObject}") - @Headers(keys = "x-amz-copy-source", values = "/{sourceBucket}/{sourceObject}") - @XMLResponseParser(CopyObjectHandler.class) - Future copyBlob(@PathParam("sourceBucket") String sourceBucket, - @PathParam("sourceObject") String sourceObject, - @HostPrefixParam String destinationBucket, - @PathParam("destinationObject") String destinationObject, - @Nullable CopyObjectOptions options); - - @PUT - @Path("{destinationObject}") - @Headers(keys = "x-amz-copy-source", values = "/{sourceBucket}/{sourceObject}") - @XMLResponseParser(CopyObjectHandler.class) - Future copyBlob(@PathParam("sourceBucket") String sourceBucket, - @PathParam("sourceObject") String sourceObject, - @HostPrefixParam String destinationBucket, - @PathParam("destinationObject") String destinationObject); - - /** - * - * A GET request operation directed at an object or bucket URI with the "acl" parameter retrieves - * the Access Control List (ACL) settings for that S3 item. - *

- * To list a bucket's ACL, you must have READ_ACP access to the item. - * - * @return access permissions of the bucket - * - * @see - */ - @GET - @QueryParams(keys = "acl") - @XMLResponseParser(AccessControlListHandler.class) - @ExceptionParser(ThrowContainerNotFoundOn404.class) - @Path("/") - Future getContainerACL(@HostPrefixParam String bucketName); - - /** - * Update a bucket's Access Control List settings. - *

- * A PUT request operation directed at a bucket URI with the "acl" parameter sets the Access - * Control List (ACL) settings for that S3 item. - *

- * To set a bucket or object's ACL, you must have WRITE_ACP or FULL_CONTROL access to the item. - * - * @param bucketName - * the bucket whose Access Control List settings will be updated. - * @param acl - * the ACL to apply to the bucket. This acl object must include a - * valid owner identifier string in {@link AccessControlList#getOwner()}. - * @return true if the bucket's Access Control List was updated successfully. - * - * @see - */ - @PUT - @Path("/") - @QueryParams(keys = "acl") - Future putContainerACL(@HostPrefixParam String bucketName, - @EntityParam(AccessControlListBinder.class) AccessControlList acl); - - /** - * A GET request operation directed at an object or bucket URI with the "acl" parameter retrieves - * the Access Control List (ACL) settings for that S3 item. - *

- * To list a object's ACL, you must have READ_ACP access to the item. - * - * @return access permissions of the object - * - * @see - */ - @GET - @QueryParams(keys = "acl") - @Path("{key}") - @XMLResponseParser(AccessControlListHandler.class) - @ExceptionParser(ThrowKeyNotFoundOn404.class) - Future getBlobACL(@HostPrefixParam String bucketName, - @PathParam("key") String key); - - /** - * Update an object's Access Control List settings. - *

- * A PUT request operation directed at an object URI with the "acl" parameter sets the Access - * Control List (ACL) settings for that S3 item. - *

- * To set a bucket or object's ACL, you must have WRITE_ACP or FULL_CONTROL access to the item. - * - * @param bucket - * the bucket containing the object to be updated - * @param objectKey - * the key of the object whose Access Control List settings will be updated. - * @param acl - * the ACL to apply to the object. This acl object must include a - * valid owner identifier string in {@link AccessControlList#getOwner()}. - * @return true if the object's Access Control List was updated successfully. - * - * @see - */ - @PUT - @QueryParams(keys = "acl") - @Path("{key}") - Future putBlobACL(@HostPrefixParam String bucketName, @PathParam("key") String key, - @EntityParam(AccessControlListBinder.class) AccessControlList acl); - } diff --git a/aws/s3/core/src/main/java/org/jclouds/aws/s3/S3Connection.java b/aws/s3/core/src/main/java/org/jclouds/aws/s3/S3Connection.java new file mode 100644 index 0000000000..e406ffbb22 --- /dev/null +++ b/aws/s3/core/src/main/java/org/jclouds/aws/s3/S3Connection.java @@ -0,0 +1,426 @@ +/** + * + * Copyright (C) 2009 Global Cloud Specialists, Inc. + * + * ==================================================================== + * 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; + +import java.util.SortedSet; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.HEAD; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; + +import org.jclouds.aws.s3.binders.AccessControlListBinder; +import org.jclouds.aws.s3.binders.S3ObjectBinder; +import org.jclouds.aws.s3.domain.AccessControlList; +import org.jclouds.aws.s3.domain.BucketMetadata; +import org.jclouds.aws.s3.domain.ListBucketResponse; +import org.jclouds.aws.s3.domain.ObjectMetadata; +import org.jclouds.aws.s3.domain.S3Object; +import org.jclouds.aws.s3.filters.RequestAuthorizeSignature; +import org.jclouds.aws.s3.functions.ParseObjectFromHeadersAndHttpContent; +import org.jclouds.aws.s3.functions.ParseObjectMetadataFromHeaders; +import org.jclouds.aws.s3.functions.ReturnTrueIfBucketAlreadyOwnedByYou; +import org.jclouds.aws.s3.functions.ReturnTrueOn404FalseIfNotEmpty; +import org.jclouds.aws.s3.options.CopyObjectOptions; +import org.jclouds.aws.s3.options.ListBucketOptions; +import org.jclouds.aws.s3.options.PutBucketOptions; +import org.jclouds.aws.s3.options.PutObjectOptions; +import org.jclouds.aws.s3.xml.AccessControlListHandler; +import org.jclouds.aws.s3.xml.CopyObjectHandler; +import org.jclouds.aws.s3.xml.ListAllMyBucketsHandler; +import org.jclouds.aws.s3.xml.ListBucketHandler; +import org.jclouds.blobstore.domain.Blob; +import org.jclouds.blobstore.functions.BlobKey; +import org.jclouds.blobstore.functions.ReturnVoidOnNotFoundOr404; +import org.jclouds.blobstore.functions.ThrowContainerNotFoundOn404; +import org.jclouds.blobstore.functions.ThrowKeyNotFoundOn404; +import org.jclouds.http.functions.ParseETagHeader; +import org.jclouds.http.functions.ReturnFalseOn404; +import org.jclouds.http.options.GetOptions; +import org.jclouds.rest.Endpoint; +import org.jclouds.rest.EntityParam; +import org.jclouds.rest.ExceptionParser; +import org.jclouds.rest.Headers; +import org.jclouds.rest.HostPrefixParam; +import org.jclouds.rest.ParamParser; +import org.jclouds.rest.QueryParams; +import org.jclouds.rest.RequestFilters; +import org.jclouds.rest.ResponseParser; +import org.jclouds.rest.SkipEncoding; +import org.jclouds.rest.VirtualHost; +import org.jclouds.rest.XMLResponseParser; +import org.jclouds.rest.binders.HttpRequestOptionsBinder; + +import com.google.inject.internal.Nullable; + +/** + * Provides access to S3 via their REST API. + *

+ * All commands return a Future of the result from S3. Any exceptions incurred during processing + * will be wrapped in an {@link ExecutionException} as documented in {@link Future#get()}. + * + * @author Adrian Cole + * @author James Murty + * @see + */ +@VirtualHost +@SkipEncoding('/') +@RequestFilters(RequestAuthorizeSignature.class) +@Endpoint(S3.class) +public interface S3Connection { + + /** + * Retrieves the S3Object associated with the Key or {@link Blob #NOT_FOUND} if + * not available; + * + *

+ * To use GET, you must have READ access to the object. If READ access is granted to the + * anonymous user, you can request the object without an authorization header. + * + *

+ * This command allows you to specify {@link GetObjectOptions} to control delivery of content. + * + *

Note

If you specify any of the below options, you will receive partial content: + *
    + *
  • {@link GetObjectOptions#range}
  • + *
  • {@link GetObjectOptions#startAt}
  • + *
  • {@link GetObjectOptions#tail}
  • + *
+ * + * @param bucketName + * namespace of the object you are retrieving + * @param key + * unique key in the s3Bucket identifying the object + * @return Future reference to a fully populated S3Object including data stored in S3 or + * {@link S3Object#NOT_FOUND} if not present. + * + * @throws org.jclouds.http.HttpResponseException + * if the conditions requested set were not satisfied by the object on the server. + * @see #getObject(String, String) + * @see GetObjectOptions + */ + @GET + @Path("{key}") + @ExceptionParser(ThrowKeyNotFoundOn404.class) + @ResponseParser(ParseObjectFromHeadersAndHttpContent.class) + Future getObject(@HostPrefixParam String bucketName, @PathParam("key") String key, + GetOptions... options); + + /** + * Retrieves the {@link org.jclouds.aws.s3.domain.ObjectMetadata metadata} of the object + * associated with the key or {@link org.jclouds.aws.s3.domain.ObjectMetadata#NOT_FOUND} if not + * available. + * + *

+ * The HEAD operation is used to retrieve information about a specific object or object size, + * without actually fetching the object itself. This is useful if you're only interested in the + * object metadata, and don't want to waste bandwidth on the object data. + * + * + * @param bucketName + * namespace of the metadata you are retrieving + * @param key + * unique key in the s3Bucket identifying the object + * @return metadata associated with the key or + * {@link org.jclouds.aws.s3.domain.ObjectMetadata#NOT_FOUND} if not present; + * @see #getObject(String, String) + * @see + */ + @HEAD + @Path("{key}") + @ExceptionParser(ThrowKeyNotFoundOn404.class) + @ResponseParser(ParseObjectMetadataFromHeaders.class) + ObjectMetadata headObject(@HostPrefixParam String bucketName, @PathParam("key") String key); + + /** + * Removes the object and metadata associated with the key. + *

+ * The DELETE request operation removes the specified object from Amazon S3. Once deleted, there + * is no method to restore or undelete an object. + * + * + * @param bucketName + * namespace of the object you are deleting + * @param key + * unique key in the s3Bucket identifying the object + * @return true if deleted + * @throws org.jclouds.http.HttpResponseException + * if the bucket is not available + * @see + */ + @DELETE + @Path("{key}") + @ExceptionParser(ReturnVoidOnNotFoundOr404.class) + Future deleteObject(@HostPrefixParam String bucketName, @PathParam("key") String key); + + /** + * Store data by creating or overwriting an object. + *

+ * This method will store the object with the default private acl. + * + *

+ * This returns a byte[] of the eTag hash of what Amazon S3 received + *

+ * + * @param bucketName + * namespace of the object you are storing + * @param object + * contains the data and metadata to create or overwrite + * @param options + * options for creating the object + * @return MD5 hash of the content uploaded + * @throws org.jclouds.http.HttpResponseException + * if the conditions requested set are not satisfied by the object on the server. + * @see org.jclouds.aws.s3.domain.CannedAccessPolicy#PRIVATE + * @see + */ + @PUT + @Path("{key}") + @ResponseParser(ParseETagHeader.class) + Future putObject( + @HostPrefixParam String bucketName, + @PathParam("key") @ParamParser(BlobKey.class) @EntityParam(S3ObjectBinder.class) S3Object object, + PutObjectOptions... options); + + /** + * Create and name your own bucket in which to store your objects. + * + *

+ * you can use {@link PutBucketOptions} to create the bucket in EU. + *

+ * The PUT request operation with a bucket URI creates a new bucket. 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 via + * {@link PutBucketOptions}. + * + * @param options + * for creating your bucket + * @return true, if the bucket was created or already exists + * + * @see PutBucketOptions + * @see + * + */ + @PUT + @Path("/") + @ExceptionParser(ReturnTrueIfBucketAlreadyOwnedByYou.class) + Future putBucketIfNotExists(@HostPrefixParam String bucketName, + @Nullable @EntityParam(HttpRequestOptionsBinder.class) PutBucketOptions... options); + + /** + * Deletes the bucket, if it is empty. + *

+ * The DELETE request operation deletes the bucket named in the URI. All objects in the bucket + * must be deleted before the bucket itself can be deleted. + *

+ * Only the owner of a bucket can delete it, regardless of the bucket's access control policy. + * + * + * @param bucketName + * what to delete + * @return false, if the bucket was not empty and therefore not deleted + * @see org.jclouds.aws.s3.commands.DeleteBucket + * @see + */ + @DELETE + @Path("/") + @ExceptionParser(ReturnTrueOn404FalseIfNotEmpty.class) + Future deleteBucketIfEmpty(@HostPrefixParam String bucketName); + + /** + * Issues a HEAD command to determine if the bucket exists or not. + */ + @HEAD + @Path("/") + @QueryParams(keys = "max-keys", values = "0") + @ExceptionParser(ReturnFalseOn404.class) + boolean bucketExists(@HostPrefixParam String bucketName); + + /** + * Retrieve a S3Bucket listing. A GET request operation using a bucket URI lists + * information about the objects in the bucket. You can use {@link ListBucketOptions} to control + * the amount of S3Objects to return. + *

+ * To list the keys of a bucket, you must have READ access to the bucket. + *

+ * + * @param bucketName + * namespace of the objects you wish to list + * @return Future reference to a fully populated S3Bucket including metadata of the S3Objects it + * contains or {@link BoundedList#NOT_FOUND} if not present. + * @see ListBucketOptions + * + * @see + */ + @GET + @Path("/") + @XMLResponseParser(ListBucketHandler.class) + Future listBucket(@HostPrefixParam String bucketName, + ListBucketOptions... options); + + /** + * Returns a list of all of the buckets owned by the authenticated sender of the request. + * + * @return list of all of the buckets owned by the authenticated sender of the request. + * @see + * + */ + @GET + @XMLResponseParser(ListAllMyBucketsHandler.class) + @Path("/") + SortedSet listOwnedBuckets(); + + /** + * Copies one object to another bucket, retaining UserMetadata from the source. The destination + * will have a private acl. The copy operation creates a copy of an object that is already stored + * in Amazon S3. + *

+ * When copying an object, you can preserve all metadata (default) or + * {@link CopyObjectOptions#overrideMetadataWith(com.google.common.collect.Multimap) specify new + * metadata}. However, the ACL is not preserved and is set to private for the user making the + * request. To override the default ACL setting, + * {@link CopyObjectOptions#overrideAcl(org.jclouds.aws.s3.domain.CannedAccessPolicy) specify a + * new ACL} when generating a copy request. + * + * @return metadata populated with lastModified and eTag of the new object + * @see org.jclouds.aws.s3.commands.CopyObject + * @see + * @throws org.jclouds.http.HttpResponseException + * if the conditions requested set are not satisfied by the object on the server. + * @see CopyObjectOptions + * @see org.jclouds.aws.s3.domain.CannedAccessPolicy + */ + @PUT + @Path("{destinationObject}") + @Headers(keys = "x-amz-copy-source", values = "/{sourceBucket}/{sourceObject}") + @XMLResponseParser(CopyObjectHandler.class) + Future copyObject(@PathParam("sourceBucket") String sourceBucket, + @PathParam("sourceObject") String sourceObject, + @HostPrefixParam String destinationBucket, + @PathParam("destinationObject") String destinationObject, CopyObjectOptions... options); + + /** + * + * A GET request operation directed at an object or bucket URI with the "acl" parameter retrieves + * the Access Control List (ACL) settings for that S3 item. + *

+ * To list a bucket's ACL, you must have READ_ACP access to the item. + * + * @return access permissions of the bucket + * + * @see + */ + @GET + @QueryParams(keys = "acl") + @XMLResponseParser(AccessControlListHandler.class) + @ExceptionParser(ThrowContainerNotFoundOn404.class) + @Path("/") + Future getBucketACL(@HostPrefixParam String bucketName); + + /** + * Update a bucket's Access Control List settings. + *

+ * A PUT request operation directed at a bucket URI with the "acl" parameter sets the Access + * Control List (ACL) settings for that S3 item. + *

+ * To set a bucket or object's ACL, you must have WRITE_ACP or FULL_CONTROL access to the item. + * + * @param bucketName + * the bucket whose Access Control List settings will be updated. + * @param acl + * the ACL to apply to the bucket. This acl object must include a + * valid owner identifier string in {@link AccessControlList#getOwner()}. + * @return true if the bucket's Access Control List was updated successfully. + * + * @see + */ + @PUT + @Path("/") + @QueryParams(keys = "acl") + Future putBucketACL(@HostPrefixParam String bucketName, + @EntityParam(AccessControlListBinder.class) AccessControlList acl); + + /** + * A GET request operation directed at an object or bucket URI with the "acl" parameter retrieves + * the Access Control List (ACL) settings for that S3 item. + *

+ * To list a object's ACL, you must have READ_ACP access to the item. + * + * @return access permissions of the object + * + * @see + */ + @GET + @QueryParams(keys = "acl") + @Path("{key}") + @XMLResponseParser(AccessControlListHandler.class) + @ExceptionParser(ThrowKeyNotFoundOn404.class) + Future getObjectACL(@HostPrefixParam String bucketName, + @PathParam("key") String key); + + /** + * Update an object's Access Control List settings. + *

+ * A PUT request operation directed at an object URI with the "acl" parameter sets the Access + * Control List (ACL) settings for that S3 item. + *

+ * To set a bucket or object's ACL, you must have WRITE_ACP or FULL_CONTROL access to the item. + * + * @param bucket + * the bucket containing the object to be updated + * @param objectKey + * the key of the object whose Access Control List settings will be updated. + * @param acl + * the ACL to apply to the object. This acl object must include a + * valid owner identifier string in {@link AccessControlList#getOwner()}. + * @return true if the object's Access Control List was updated successfully. + * + * @see + */ + @PUT + @QueryParams(keys = "acl") + @Path("{key}") + Future putObjectACL(@HostPrefixParam String bucketName, @PathParam("key") String key, + @EntityParam(AccessControlListBinder.class) AccessControlList acl); + +} diff --git a/aws/s3/core/src/main/java/org/jclouds/aws/s3/S3Context.java b/aws/s3/core/src/main/java/org/jclouds/aws/s3/S3Context.java index b689f19c17..8275f8e2e5 100644 --- a/aws/s3/core/src/main/java/org/jclouds/aws/s3/S3Context.java +++ b/aws/s3/core/src/main/java/org/jclouds/aws/s3/S3Context.java @@ -41,6 +41,6 @@ import org.jclouds.blobstore.BlobStoreContext; * */ public interface S3Context extends - BlobStoreContext { + BlobStoreContext { } \ No newline at end of file diff --git a/aws/s3/core/src/main/java/org/jclouds/aws/s3/S3ContextBuilder.java b/aws/s3/core/src/main/java/org/jclouds/aws/s3/S3ContextBuilder.java index a2df751b17..fbc2d8b571 100644 --- a/aws/s3/core/src/main/java/org/jclouds/aws/s3/S3ContextBuilder.java +++ b/aws/s3/core/src/main/java/org/jclouds/aws/s3/S3ContextBuilder.java @@ -61,7 +61,7 @@ import com.google.inject.TypeLiteral; * @see S3Context */ public class S3ContextBuilder extends - BlobStoreContextBuilder { + BlobStoreContextBuilder { @Override public S3Context buildContext() { @@ -69,7 +69,7 @@ public class S3ContextBuilder extends } public S3ContextBuilder(Properties props) { - super(new TypeLiteral() { + super(new TypeLiteral() { }, new TypeLiteral() { }, new TypeLiteral() { }, new TypeLiteral() { diff --git a/aws/s3/core/src/main/java/org/jclouds/aws/s3/binders/S3ObjectBinder.java b/aws/s3/core/src/main/java/org/jclouds/aws/s3/binders/S3ObjectBinder.java index 6b8f3cc072..c1f803fe58 100644 --- a/aws/s3/core/src/main/java/org/jclouds/aws/s3/binders/S3ObjectBinder.java +++ b/aws/s3/core/src/main/java/org/jclouds/aws/s3/binders/S3ObjectBinder.java @@ -45,7 +45,8 @@ public class S3ObjectBinder extends BlobBinder { public void addEntityToRequest(Object entity, HttpRequest request) { Blob object = (Blob) entity; checkArgument(object.getMetadata().getSize() >= 0, "size must be set"); - + checkArgument(object.getContentLength() <= 5 * 1024 * 1024 * 1024, + "maximum size for put object is 5GB"); if (object instanceof S3Object) { S3Object s3Object = (S3Object) object; if (s3Object.getMetadata().getCacheControl() != null) { diff --git a/aws/s3/core/src/main/java/org/jclouds/aws/s3/config/RestS3ConnectionModule.java b/aws/s3/core/src/main/java/org/jclouds/aws/s3/config/RestS3ConnectionModule.java index 1908d9253f..0313fb7387 100644 --- a/aws/s3/core/src/main/java/org/jclouds/aws/s3/config/RestS3ConnectionModule.java +++ b/aws/s3/core/src/main/java/org/jclouds/aws/s3/config/RestS3ConnectionModule.java @@ -30,11 +30,16 @@ import javax.inject.Singleton; import org.jclouds.aws.s3.S3; import org.jclouds.aws.s3.S3BlobStore; +import org.jclouds.aws.s3.S3Connection; +import org.jclouds.aws.s3.domain.BucketMetadata; +import org.jclouds.aws.s3.domain.ObjectMetadata; +import org.jclouds.aws.s3.domain.S3Object; import org.jclouds.aws.s3.filters.RequestAuthorizeSignature; import org.jclouds.aws.s3.handlers.AWSClientErrorRetryHandler; import org.jclouds.aws.s3.handlers.AWSRedirectionRetryHandler; import org.jclouds.aws.s3.handlers.ParseAWSErrorFromXmlContent; import org.jclouds.aws.s3.reference.S3Constants; +import org.jclouds.blobstore.BlobStore; import org.jclouds.cloud.ConfiguresCloudConnection; import org.jclouds.http.HttpErrorHandler; import org.jclouds.http.HttpRetryHandler; @@ -73,10 +78,16 @@ public class RestS3ConnectionModule extends AbstractModule { @Provides @Singleton - protected S3BlobStore provideS3Connection(RestClientFactory factory) { + protected BlobStore provideS3BlobStore(RestClientFactory factory) { return factory.create(S3BlobStore.class); } + @Provides + @Singleton + protected S3Connection provideS3Connection(RestClientFactory factory) { + return factory.create(S3Connection.class); + } + protected void bindErrorHandlers() { bind(HttpErrorHandler.class).annotatedWith(Redirection.class).to( ParseAWSErrorFromXmlContent.class); diff --git a/aws/s3/core/src/main/java/org/jclouds/aws/s3/config/S3ContextModule.java b/aws/s3/core/src/main/java/org/jclouds/aws/s3/config/S3ContextModule.java index 8b4097952e..025c4c7b80 100644 --- a/aws/s3/core/src/main/java/org/jclouds/aws/s3/config/S3ContextModule.java +++ b/aws/s3/core/src/main/java/org/jclouds/aws/s3/config/S3ContextModule.java @@ -32,10 +32,12 @@ import javax.inject.Provider; import org.jclouds.aws.reference.AWSConstants; import org.jclouds.aws.s3.S3; import org.jclouds.aws.s3.S3BlobStore; +import org.jclouds.aws.s3.S3Connection; import org.jclouds.aws.s3.S3Context; import org.jclouds.aws.s3.domain.BucketMetadata; import org.jclouds.aws.s3.domain.ObjectMetadata; import org.jclouds.aws.s3.domain.S3Object; +import org.jclouds.blobstore.BlobStore; import org.jclouds.blobstore.BlobStoreContextImpl; import org.jclouds.blobstore.BlobMap.Factory; import org.jclouds.lifecycle.Closer; @@ -56,15 +58,17 @@ public class S3ContextModule extends AbstractModule { } public static class S3ContextImpl extends - BlobStoreContextImpl implements + BlobStoreContextImpl implements S3Context { @Inject S3ContextImpl(Factory blobMapFactory, org.jclouds.blobstore.InputStreamMap.Factory inputStreamMapFactory, - Closer closer, Provider blobProvider, S3BlobStore defaultApi, - @S3 URI endPoint, @Named(AWSConstants.PROPERTY_AWS_ACCESSKEYID) String account) { - super(blobMapFactory, inputStreamMapFactory, closer, blobProvider, defaultApi, endPoint, - account); + Closer closer, Provider blobProvider, + BlobStore blobStore, + S3Connection defaultApi, @S3 URI endPoint, + @Named(AWSConstants.PROPERTY_AWS_ACCESSKEYID) String account) { + super(blobMapFactory, inputStreamMapFactory, closer, blobProvider, blobStore, defaultApi, + endPoint, account); } } diff --git a/aws/s3/core/src/main/java/org/jclouds/aws/s3/functions/ClearAndDeleteBucketIfNotEmpty.java b/aws/s3/core/src/main/java/org/jclouds/aws/s3/functions/ClearAndDeleteBucketIfNotEmpty.java new file mode 100644 index 0000000000..987f24915d --- /dev/null +++ b/aws/s3/core/src/main/java/org/jclouds/aws/s3/functions/ClearAndDeleteBucketIfNotEmpty.java @@ -0,0 +1,45 @@ +/** + * + * Copyright (C) 2009 Global Cloud Specialists, Inc. + * + * ==================================================================== + * 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.functions; + +import javax.inject.Inject; + +import org.jclouds.aws.s3.domain.BucketMetadata; +import org.jclouds.aws.s3.domain.ObjectMetadata; +import org.jclouds.aws.s3.domain.S3Object; +import org.jclouds.blobstore.BlobStore; +import org.jclouds.blobstore.functions.ClearAndDeleteIfNotEmpty; +import org.jclouds.blobstore.strategy.ClearContainerStrategy; + +public class ClearAndDeleteBucketIfNotEmpty extends + ClearAndDeleteIfNotEmpty { + + @Inject + ClearAndDeleteBucketIfNotEmpty( + ClearContainerStrategy clear, + BlobStore connection) { + super(clear, connection); + } + +} diff --git a/aws/s3/core/src/main/java/org/jclouds/aws/s3/xml/ListBucketHandler.java b/aws/s3/core/src/main/java/org/jclouds/aws/s3/xml/ListBucketHandler.java index a44bc72bc4..ec2c64a0f2 100644 --- a/aws/s3/core/src/main/java/org/jclouds/aws/s3/xml/ListBucketHandler.java +++ b/aws/s3/core/src/main/java/org/jclouds/aws/s3/xml/ListBucketHandler.java @@ -95,6 +95,7 @@ public class ListBucketHandler extends ParseSax.HandlerWithResult + * + * ==================================================================== + * 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; + +import static org.jclouds.aws.s3.internal.StubS3Connection.TEST_ACL_EMAIL; +import static org.jclouds.aws.s3.internal.StubS3Connection.TEST_ACL_ID; +import static org.jclouds.aws.s3.options.CopyObjectOptions.Builder.ifSourceETagDoesntMatch; +import static org.jclouds.aws.s3.options.CopyObjectOptions.Builder.ifSourceETagMatches; +import static org.jclouds.aws.s3.options.CopyObjectOptions.Builder.ifSourceModifiedSince; +import static org.jclouds.aws.s3.options.CopyObjectOptions.Builder.ifSourceUnmodifiedSince; +import static org.jclouds.aws.s3.options.CopyObjectOptions.Builder.overrideAcl; +import static org.jclouds.aws.s3.options.CopyObjectOptions.Builder.overrideMetadataWith; +import static org.jclouds.aws.s3.options.ListBucketOptions.Builder.afterMarker; +import static org.jclouds.aws.s3.options.ListBucketOptions.Builder.delimiter; +import static org.jclouds.aws.s3.options.ListBucketOptions.Builder.maxResults; +import static org.jclouds.aws.s3.options.ListBucketOptions.Builder.withPrefix; +import static org.jclouds.aws.s3.options.PutBucketOptions.Builder.createIn; +import static org.jclouds.aws.s3.options.PutBucketOptions.Builder.withBucketAcl; +import static org.jclouds.aws.s3.options.PutObjectOptions.Builder.withAcl; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URL; +import java.util.SortedSet; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import org.jclouds.aws.s3.domain.AccessControlList; +import org.jclouds.aws.s3.domain.BucketMetadata; +import org.jclouds.aws.s3.domain.CannedAccessPolicy; +import org.jclouds.aws.s3.domain.ListBucketResponse; +import org.jclouds.aws.s3.domain.ObjectMetadata; +import org.jclouds.aws.s3.domain.S3Object; +import org.jclouds.aws.s3.domain.AccessControlList.CanonicalUserGrantee; +import org.jclouds.aws.s3.domain.AccessControlList.EmailAddressGrantee; +import org.jclouds.aws.s3.domain.AccessControlList.GroupGranteeURI; +import org.jclouds.aws.s3.domain.AccessControlList.Permission; +import org.jclouds.aws.s3.domain.BucketMetadata.LocationConstraint; +import org.jclouds.aws.s3.options.PutObjectOptions; +import org.jclouds.blobstore.integration.internal.BaseBlobStoreIntegrationTest; +import org.jclouds.http.HttpResponseException; +import org.jclouds.util.Utils; +import org.joda.time.DateTime; +import org.testng.annotations.Test; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimap; + +/** + * + * @author James Murty + * @author Adrian Cole + */ +@Test(groups = { "integration", "live" }, testName = "s3.S3ConnectionLiveTest") +public class S3ConnectionLiveTest extends + BaseBlobStoreIntegrationTest { + + /** + * this method overrides containerName to ensure it isn't found + */ + @Test(groups = { "integration", "live" }) + public void deleteContainerIfEmptyNotFound() throws Exception { + assert context.getApi().deleteBucketIfEmpty("dbienf").get(10, TimeUnit.SECONDS); + } + + @Test(groups = { "integration", "live" }) + public void deleteContainerIfEmptyButHasContents() throws Exception { + String containerName = getContainerName(); + try { + addBlobToContainer(containerName, "test"); + assert !context.getApi().deleteBucketIfEmpty(containerName).get(10, TimeUnit.SECONDS); + } finally { + returnContainer(containerName); + } + } + + public void testPutCannedAccessPolicyPublic() throws Exception { + String containerName = getContainerName(); + try { + String key = "hello"; + + context.getApi().putObject(containerName, new S3Object(key, TEST_STRING), + + withAcl(CannedAccessPolicy.PUBLIC_READ)).get(10, TimeUnit.SECONDS); + + URL url = new URL(String.format("http://%1$s.s3.amazonaws.com/%2$s", containerName, key)); + Utils.toStringAndClose(url.openStream()); + } finally { + returnContainer(containerName); + } + + } + + public void testCopyCannedAccessPolicyPublic() throws Exception { + String containerName = getContainerName(); + String destinationContainer = getContainerName(); + try { + addBlobToContainer(containerName, sourceKey); + validateContent(containerName, sourceKey); + + context.getApi().copyObject(containerName, sourceKey, destinationContainer, + destinationKey, overrideAcl(CannedAccessPolicy.PUBLIC_READ)).get(10, + TimeUnit.SECONDS); + + validateContent(destinationContainer, destinationKey); + + URL url = new URL(String.format("http://%1$s.s3.amazonaws.com/%2$s", destinationContainer, + destinationKey)); + Utils.toStringAndClose(url.openStream()); + + } finally { + returnContainer(containerName); + returnContainer(destinationContainer); + } + } + + String sourceKey = "apples"; + String destinationKey = "pears"; + + public void testPublicWriteOnObject() throws InterruptedException, ExecutionException, + TimeoutException, IOException { + final String publicReadWriteObjectKey = "public-read-write-acl"; + final String containerName = getContainerName(); + try { + // Public Read-Write object + context.getApi().putObject(containerName, new S3Object(publicReadWriteObjectKey, ""), + new PutObjectOptions().withAcl(CannedAccessPolicy.PUBLIC_READ_WRITE)).get(10, + TimeUnit.SECONDS); + + assertEventually(new Runnable() { + public void run() { + try { + AccessControlList acl = context.getApi().getObjectACL(containerName, + publicReadWriteObjectKey).get(10, TimeUnit.SECONDS); + assertEquals(acl.getGrants().size(), 3); + assertEquals(acl.getPermissions(GroupGranteeURI.ALL_USERS).size(), 2); + assertTrue(acl.getOwner() != null); + String ownerId = acl.getOwner().getId(); + assertTrue(acl.hasPermission(ownerId, Permission.FULL_CONTROL)); + assertTrue(acl.hasPermission(GroupGranteeURI.ALL_USERS, Permission.READ)); + assertTrue(acl.hasPermission(GroupGranteeURI.ALL_USERS, Permission.WRITE)); + assertFalse(acl.hasPermission(GroupGranteeURI.ALL_USERS, Permission.READ_ACP)); + assertFalse(acl.hasPermission(GroupGranteeURI.ALL_USERS, Permission.WRITE_ACP)); + assertFalse(acl.hasPermission(GroupGranteeURI.ALL_USERS, Permission.FULL_CONTROL)); + } catch (Exception e) { + Utils. rethrowIfRuntimeOrSameType(e); + } + } + }); + } finally { + returnContainer(containerName); + } + + } + + public void testUpdateObjectACL() throws InterruptedException, ExecutionException, + TimeoutException, IOException { + String containerName = getContainerName(); + try { + String objectKey = "private-acl"; + + // Private object + addBlobToContainer(containerName, objectKey); + AccessControlList acl = context.getApi().getObjectACL(containerName, objectKey).get(10, + TimeUnit.SECONDS); + String ownerId = acl.getOwner().getId(); + + assertEquals(acl.getGrants().size(), 1); + assertTrue(acl.hasPermission(ownerId, Permission.FULL_CONTROL)); + + addGrantsToACL(acl); + assertEquals(acl.getGrants().size(), 4); + assertTrue(context.getApi().putObjectACL(containerName, objectKey, acl).get(10, + TimeUnit.SECONDS)); + + // Confirm that the updated ACL has stuck. + acl = context.getApi().getObjectACL(containerName, objectKey).get(10, TimeUnit.SECONDS); + checkGrants(acl); + + /* + * Revoke all of owner's permissions! + */ + acl.revokeAllPermissions(new CanonicalUserGrantee(ownerId)); + if (!ownerId.equals(TEST_ACL_ID)) + acl.revokeAllPermissions(new CanonicalUserGrantee(TEST_ACL_ID)); + assertEquals(acl.getGrants().size(), 1); + // Only public read permission should remain... + assertTrue(acl.hasPermission(GroupGranteeURI.ALL_USERS, Permission.READ)); + + // Update the object's ACL settings + assertTrue(context.getApi().putObjectACL(containerName, objectKey, acl).get(10, + TimeUnit.SECONDS)); + + // Confirm that the updated ACL has stuck + acl = context.getApi().getObjectACL(containerName, objectKey).get(10, TimeUnit.SECONDS); + assertEquals(acl.getGrants().size(), 1); + assertEquals(acl.getPermissions(ownerId).size(), 0); + assertTrue(acl.hasPermission(GroupGranteeURI.ALL_USERS, Permission.READ), acl.toString()); + } finally { + returnContainer(containerName); + } + + } + + public void testPrivateAclIsDefaultForObject() throws InterruptedException, ExecutionException, + TimeoutException, IOException { + String privateObjectKey = "private-acl"; + String containerName = getContainerName(); + try { + // Private object + addBlobToContainer(containerName, privateObjectKey); + AccessControlList acl = context.getApi().getObjectACL(containerName, privateObjectKey) + .get(10, TimeUnit.SECONDS); + + assertEquals(acl.getGrants().size(), 1); + assertTrue(acl.getOwner() != null); + String ownerId = acl.getOwner().getId(); + assertTrue(acl.hasPermission(ownerId, Permission.FULL_CONTROL)); + } finally { + returnContainer(containerName); + } + + } + + public void testPublicReadOnObject() throws InterruptedException, ExecutionException, + TimeoutException, IOException { + final String publicReadObjectKey = "public-read-acl"; + final String containerName = getContainerName(); + try { + context.getApi().putObject(containerName, new S3Object(publicReadObjectKey, ""), + new PutObjectOptions().withAcl(CannedAccessPolicy.PUBLIC_READ)).get(10, + TimeUnit.SECONDS); + + assertEventually(new Runnable() { + public void run() { + try { + AccessControlList acl = context.getApi().getObjectACL(containerName, + publicReadObjectKey).get(10, TimeUnit.SECONDS); + + assertEquals(acl.getGrants().size(), 2); + assertEquals(acl.getPermissions(GroupGranteeURI.ALL_USERS).size(), 1); + assertTrue(acl.getOwner() != null); + String ownerId = acl.getOwner().getId(); + assertTrue(acl.hasPermission(ownerId, Permission.FULL_CONTROL)); + assertTrue(acl.hasPermission(GroupGranteeURI.ALL_USERS, Permission.READ)); + } catch (Exception e) { + Utils. rethrowIfRuntimeOrSameType(e); + } + } + }); + + } finally { + returnContainer(containerName); + } + + } + + public void testMetadataWithCacheControlAndContentDisposition() throws Exception { + String key = "hello"; + + S3Object object = context.newBlob(key); + object.setData(TEST_STRING); + object.getMetadata().setCacheControl("no-cache"); + object.getMetadata().setContentDisposition("attachment; filename=hello.txt"); + String containerName = getContainerName(); + try { + addBlobToContainer(containerName, object); + S3Object newObject = validateContent(containerName, key); + + assertEquals(newObject.getMetadata().getCacheControl(), "no-cache"); + assertEquals(newObject.getMetadata().getContentDisposition(), + "attachment; filename=hello.txt"); + } finally { + returnContainer(containerName); + } + } + + @Test(groups = { "integration", "live" }) + public void testMetadataContentEncoding() throws Exception { + String key = "hello"; + + S3Object object = context.newBlob(key); + object.setData(TEST_STRING); + object.getMetadata().setContentEncoding("x-compress"); + String containerName = getContainerName(); + try { + addBlobToContainer(containerName, object); + S3Object newObject = validateContent(containerName, key); + + assertEquals(newObject.getMetadata().getContentEncoding(), "x-compress"); + } finally { + returnContainer(containerName); + } + } + + public void testCopyObject() throws Exception { + String containerName = getContainerName(); + String destinationContainer = getContainerName(); + + try { + addToContainerAndValidate(containerName, sourceKey); + + context.getApi() + .copyObject(containerName, sourceKey, destinationContainer, destinationKey).get( + 10, TimeUnit.SECONDS); + + validateContent(destinationContainer, destinationKey); + } finally { + returnContainer(containerName); + returnContainer(destinationContainer); + + } + } + + private void addToContainerAndValidate(String containerName, String sourceKey) + throws InterruptedException, ExecutionException, TimeoutException, IOException { + addBlobToContainer(containerName, sourceKey); + validateContent(containerName, sourceKey); + } + + // TODO: fails on linux and windows + public void testCopyIfModifiedSince() throws InterruptedException, ExecutionException, + TimeoutException, IOException { + String containerName = getContainerName(); + String destinationContainer = getContainerName(); + try { + DateTime before = new DateTime(); + addToContainerAndValidate(containerName, sourceKey + "mod"); + DateTime after = new DateTime().plusSeconds(1); + + context.getApi().copyObject(containerName, sourceKey + "mod", destinationContainer, + destinationKey, ifSourceModifiedSince(before)).get(10, TimeUnit.SECONDS); + validateContent(destinationContainer, destinationKey); + + try { + context.getApi().copyObject(containerName, sourceKey + "mod", destinationContainer, + destinationKey, ifSourceModifiedSince(after)).get(10, TimeUnit.SECONDS); + } catch (ExecutionException e) { + if (e.getCause() instanceof HttpResponseException) { + HttpResponseException ex = (HttpResponseException) e.getCause(); + assertEquals(ex.getResponse().getStatusCode(), 412); + } else { + throw e; + } + } + } finally { + returnContainer(containerName); + returnContainer(destinationContainer); + + } + } + + // TODO: fails on linux and windows + public void testCopyIfUnmodifiedSince() throws InterruptedException, ExecutionException, + TimeoutException, IOException { + String containerName = getContainerName(); + String destinationContainer = getContainerName(); + try { + DateTime before = new DateTime(); + addToContainerAndValidate(containerName, sourceKey + "un"); + DateTime after = new DateTime().plusSeconds(1); + + context.getApi().copyObject(containerName, sourceKey + "un", destinationContainer, + destinationKey, ifSourceUnmodifiedSince(after)).get(10, TimeUnit.SECONDS); + validateContent(destinationContainer, destinationKey); + + try { + context.getApi().copyObject(containerName, sourceKey + "un", destinationContainer, + destinationKey, ifSourceModifiedSince(before)).get(10, TimeUnit.SECONDS); + } catch (ExecutionException e) { + HttpResponseException ex = (HttpResponseException) e.getCause(); + assertEquals(ex.getResponse().getStatusCode(), 412); + } + } finally { + returnContainer(containerName); + returnContainer(destinationContainer); + } + } + + public void testCopyIfMatch() throws InterruptedException, ExecutionException, TimeoutException, + IOException { + String containerName = getContainerName(); + String destinationContainer = getContainerName(); + try { + addToContainerAndValidate(containerName, sourceKey); + + context.getApi().copyObject(containerName, sourceKey, destinationContainer, + destinationKey, ifSourceETagMatches(goodETag)).get(10, TimeUnit.SECONDS); + validateContent(destinationContainer, destinationKey); + + try { + context.getApi().copyObject(containerName, sourceKey, destinationContainer, + destinationKey, ifSourceETagMatches(badETag)).get(10, TimeUnit.SECONDS); + } catch (ExecutionException e) { + HttpResponseException ex = (HttpResponseException) e.getCause(); + assertEquals(ex.getResponse().getStatusCode(), 412); + } + } finally { + returnContainer(containerName); + returnContainer(destinationContainer); + } + } + + public void testCopyIfNoneMatch() throws IOException, InterruptedException, ExecutionException, + TimeoutException { + String containerName = getContainerName(); + String destinationContainer = getContainerName(); + try { + addToContainerAndValidate(containerName, sourceKey); + + context.getApi().copyObject(containerName, sourceKey, destinationContainer, + destinationKey, ifSourceETagDoesntMatch(badETag)).get(10, TimeUnit.SECONDS); + validateContent(destinationContainer, destinationKey); + + try { + context.getApi().copyObject(containerName, sourceKey, destinationContainer, + destinationKey, ifSourceETagDoesntMatch(goodETag)).get(10, TimeUnit.SECONDS); + } catch (ExecutionException e) { + HttpResponseException ex = (HttpResponseException) e.getCause(); + assertEquals(ex.getResponse().getStatusCode(), 412); + } + } finally { + returnContainer(containerName); + returnContainer(destinationContainer); + + } + } + + public void testCopyWithMetadata() throws InterruptedException, ExecutionException, + TimeoutException, IOException { + String containerName = getContainerName(); + String destinationContainer = getContainerName(); + try { + addToContainerAndValidate(containerName, sourceKey); + + Multimap metadata = HashMultimap.create(); + metadata.put("adrian", "cole"); + + context.getApi().copyObject(containerName, sourceKey, destinationContainer, + destinationKey, overrideMetadataWith(metadata)).get(10, TimeUnit.SECONDS); + + validateContent(destinationContainer, destinationKey); + + ObjectMetadata objectMeta = context.getApi().headObject(destinationContainer, + destinationKey); + + assertEquals(objectMeta.getUserMetadata(), metadata); + } finally { + returnContainer(containerName); + returnContainer(destinationContainer); + + } + } + + public void testListContainerDelimiter() throws InterruptedException, ExecutionException, + TimeoutException, UnsupportedEncodingException { + String containerName = getContainerName(); + try { + String prefix = "apps"; + addTenObjectsUnderPrefix(containerName, prefix); + add15UnderRoot(containerName); + ListBucketResponse container = context.getApi().listBucket(containerName, delimiter("/")) + .get(10, TimeUnit.SECONDS); + assertEquals(container.getDelimiter(), "/"); + assert !container.isTruncated(); + assertEquals(container.size(), 15); + assertEquals(container.getCommonPrefixes().size(), 1); + } finally { + returnContainer(containerName); + } + + } + + public void testListContainerPrefix() throws InterruptedException, ExecutionException, + TimeoutException, UnsupportedEncodingException { + String containerName = getContainerName(); + try { + String prefix = "apps"; + addTenObjectsUnderPrefix(containerName, prefix); + add15UnderRoot(containerName); + + ListBucketResponse container = context.getApi().listBucket(containerName, + withPrefix("apps/")).get(10, TimeUnit.SECONDS); + assert !container.isTruncated(); + assertEquals(container.size(), 10); + assertEquals(container.getPrefix(), "apps/"); + } finally { + returnContainer(containerName); + } + + } + + public void testPrivateAclIsDefaultForContainer() throws InterruptedException, + ExecutionException, TimeoutException, IOException { + String containerName = getContainerName(); + try { + AccessControlList acl = context.getApi().getBucketACL(containerName).get(10, + TimeUnit.SECONDS); + assertEquals(acl.getGrants().size(), 1); + assertTrue(acl.getOwner() != null); + String ownerId = acl.getOwner().getId(); + assertTrue(acl.hasPermission(ownerId, Permission.FULL_CONTROL)); + } finally { + returnContainer(containerName); + } + + } + + public void testUpdateContainerACL() throws InterruptedException, ExecutionException, + TimeoutException, IOException, Exception { + String containerName = getContainerName(); + try { + // Confirm the container is private + AccessControlList acl = context.getApi().getBucketACL(containerName).get(10, + TimeUnit.SECONDS); + String ownerId = acl.getOwner().getId(); + assertEquals(acl.getGrants().size(), 1); + assertTrue(acl.hasPermission(ownerId, Permission.FULL_CONTROL)); + + addGrantsToACL(acl); + assertEquals(acl.getGrants().size(), 4); + assertTrue(context.getApi().putBucketACL(containerName, acl).get(10, TimeUnit.SECONDS)); + + // Confirm that the updated ACL has stuck. + acl = context.getApi().getBucketACL(containerName).get(10, TimeUnit.SECONDS); + checkGrants(acl); + } finally { + destroyContainer(containerName); + } + + } + + private void checkGrants(AccessControlList acl) { + String ownerId = acl.getOwner().getId(); + + assertEquals(acl.getGrants().size(), 4, acl.toString()); + + assertTrue(acl.hasPermission(ownerId, Permission.FULL_CONTROL), acl.toString()); + assertTrue(acl.hasPermission(GroupGranteeURI.ALL_USERS, Permission.READ), acl.toString()); + assertTrue(acl.hasPermission(ownerId, Permission.WRITE_ACP), acl.toString()); + // EmailAddressGrantee is replaced by a CanonicalUserGrantee, so we cannot test by email addr + assertTrue(acl.hasPermission(TEST_ACL_ID, Permission.READ_ACP), acl.toString()); + } + + private void addGrantsToACL(AccessControlList acl) { + String ownerId = acl.getOwner().getId(); + acl.addPermission(GroupGranteeURI.ALL_USERS, Permission.READ); + acl.addPermission(new EmailAddressGrantee(TEST_ACL_EMAIL), Permission.READ_ACP); + acl.addPermission(new CanonicalUserGrantee(ownerId), Permission.WRITE_ACP); + } + + public void testListContainerMarker() throws InterruptedException, ExecutionException, + TimeoutException, UnsupportedEncodingException { + String containerName = getContainerName(); + try { + addAlphabetUnderRoot(containerName); + ListBucketResponse container = context.getApi() + .listBucket(containerName, afterMarker("y")).get(10, TimeUnit.SECONDS); + assertEquals(container.getMarker(), "y"); + assert !container.isTruncated(); + assertEquals(container.size(), 1); + } finally { + returnContainer(containerName); + } + } + + public void testListContainerMaxResults() throws InterruptedException, ExecutionException, + TimeoutException, UnsupportedEncodingException { + String containerName = getContainerName(); + try { + addAlphabetUnderRoot(containerName); + ListBucketResponse container = context.getApi().listBucket(containerName, maxResults(5)) + .get(10, TimeUnit.SECONDS); + assertEquals(container.getMaxResults(), 5); + assert container.isTruncated(); + assertEquals(container.size(), 5); + } finally { + returnContainer(containerName); + } + } + + public void testPublicReadAccessPolicy() throws Exception { + String containerName = getScratchContainerName(); + try { + context.getApi().putBucketIfNotExists(containerName, + withBucketAcl(CannedAccessPolicy.PUBLIC_READ)).get(10, TimeUnit.SECONDS); + AccessControlList acl = context.getApi().getBucketACL(containerName).get(10, + TimeUnit.SECONDS); + assertTrue(acl.hasPermission(GroupGranteeURI.ALL_USERS, Permission.READ), acl.toString()); + // TODO: I believe that the following should work based on the above acl assertion passing. + // However, it fails on 403 + // URL url = new URL(String.format("http://%s.s3.amazonaws.com", containerName)); + // Utils.toStringAndClose(url.openStream()); + } finally { + destroyContainer(containerName); + } + } + + @Test(expectedExceptions = IOException.class) + public void testDefaultAccessPolicy() throws Exception { + String containerName = getContainerName(); + try { + URL url = new URL(String.format("https://%s.s3.amazonaws.com", containerName)); + Utils.toStringAndClose(url.openStream()); + } finally { + returnContainer(containerName); + } + + } + + /** + * using scratch containerName as we are changing location + */ + public void testEu() throws Exception { + final String containerName = getScratchContainerName(); + try { + context.getApi().putBucketIfNotExists(containerName + "eu", + createIn(LocationConstraint.EU).withBucketAcl(CannedAccessPolicy.PUBLIC_READ)) + .get(30, TimeUnit.SECONDS); + assertEventually(new Runnable() { + public void run() { + try { + AccessControlList acl = context.getApi().getBucketACL(containerName + "eu").get( + 30, TimeUnit.SECONDS); + assertTrue(acl.hasPermission(GroupGranteeURI.ALL_USERS, Permission.READ), acl + .toString()); + } catch (Exception e) { + Utils. rethrowIfRuntimeOrSameType(e); + } + } + }); + // TODO: I believe that the following should work based on the above acl assertion passing. + // However, it fails on 403 + // URL url = new URL(String.format("http://%s.s3.amazonaws.com", containerName)); + // Utils.toStringAndClose(url.openStream()); + } finally { + destroyContainer(containerName + "eu"); + } + } + + void containerExists() throws Exception { + String containerName = getContainerName(); + try { + SortedSet list = context.getApi().listOwnedBuckets(); + BucketMetadata firstContainer = list.first(); + BucketMetadata toMatch = new BucketMetadata(containerName); + toMatch.setOwner(firstContainer.getOwner()); + assert list.contains(toMatch); + } finally { + returnContainer(containerName); + } + } + + protected void addAlphabetUnderRoot(String containerName) throws InterruptedException, + ExecutionException, TimeoutException { + for (char letter = 'a'; letter <= 'z'; letter++) { + S3Object blob = context.newBlob(letter + ""); + blob.setData(letter + "content"); + context.getApi().putObject(containerName, blob).get(10, TimeUnit.SECONDS); + } + } + + protected void add15UnderRoot(String containerName) throws InterruptedException, + ExecutionException, TimeoutException { + for (int i = 0; i < 15; i++) { + S3Object blob = context.newBlob(i + ""); + blob.setData(i + "content"); + context.getApi().putObject(containerName, blob).get(10, TimeUnit.SECONDS); + } + } + + protected void addTenObjectsUnderPrefix(String containerName, String prefix) + throws InterruptedException, ExecutionException, TimeoutException { + for (int i = 0; i < 10; i++) { + S3Object blob = context.newBlob(prefix + "/" + i); + blob.setData(i + "content"); + context.getApi().putObject(containerName, blob).get(10, TimeUnit.SECONDS); + } + } +} \ No newline at end of file diff --git a/aws/s3/core/src/test/java/org/jclouds/aws/s3/S3ContextBuilderTest.java b/aws/s3/core/src/test/java/org/jclouds/aws/s3/S3ContextBuilderTest.java index 5ada8e185d..d961f6706e 100644 --- a/aws/s3/core/src/test/java/org/jclouds/aws/s3/S3ContextBuilderTest.java +++ b/aws/s3/core/src/test/java/org/jclouds/aws/s3/S3ContextBuilderTest.java @@ -36,6 +36,8 @@ import org.jclouds.aws.s3.config.RestS3ConnectionModule; import org.jclouds.aws.s3.config.S3ContextModule; import org.jclouds.aws.s3.config.StubS3BlobStoreModule; import org.jclouds.aws.s3.config.S3ContextModule.S3ContextImpl; +import org.jclouds.aws.s3.internal.StubS3Connection; +import org.jclouds.blobstore.integration.internal.StubBlobStore; import org.testng.annotations.Test; import com.google.inject.Injector; @@ -61,6 +63,8 @@ public class S3ContextBuilderTest { S3Context context = new S3ContextBuilder("id", "secret").withModules( new StubS3BlobStoreModule()).buildContext(); assertEquals(context.getClass(), S3ContextImpl.class); + assertEquals(context.getApi().getClass(), StubS3Connection.class); + assertEquals(context.getBlobStore().getClass(), StubBlobStore.class); assertEquals(context.getAccount(), "id"); assertEquals(context.getEndPoint(), URI.create("https://localhost/s3stub")); } diff --git a/aws/s3/core/src/test/java/org/jclouds/aws/s3/config/S3ContextModuleTest.java b/aws/s3/core/src/test/java/org/jclouds/aws/s3/config/S3ContextModuleTest.java index f9fd05553d..38024d1f42 100644 --- a/aws/s3/core/src/test/java/org/jclouds/aws/s3/config/S3ContextModuleTest.java +++ b/aws/s3/core/src/test/java/org/jclouds/aws/s3/config/S3ContextModuleTest.java @@ -25,7 +25,6 @@ package org.jclouds.aws.s3.config; import static org.testng.Assert.assertEquals; -import org.jclouds.aws.s3.S3BlobStore; import org.jclouds.aws.s3.S3Context; import org.jclouds.aws.s3.config.S3ContextModule.S3ContextImpl; import org.jclouds.aws.s3.domain.BucketMetadata; @@ -48,8 +47,7 @@ public class S3ContextModuleTest { Injector createInjector() { return Guice.createInjector(new StubS3BlobStoreModule(), BlobStoreMapsModule.Builder - .newBuilder(new TypeLiteral() { - }, new TypeLiteral() { + .newBuilder(new TypeLiteral() { }, new TypeLiteral() { }, new TypeLiteral() { }).build(), new S3ContextModule() { diff --git a/aws/s3/core/src/test/java/org/jclouds/aws/s3/config/StubS3BlobStoreModule.java b/aws/s3/core/src/test/java/org/jclouds/aws/s3/config/StubS3BlobStoreModule.java index 82645e0632..776de064c7 100755 --- a/aws/s3/core/src/test/java/org/jclouds/aws/s3/config/StubS3BlobStoreModule.java +++ b/aws/s3/core/src/test/java/org/jclouds/aws/s3/config/StubS3BlobStoreModule.java @@ -28,9 +28,13 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.jclouds.aws.s3.S3; -import org.jclouds.aws.s3.S3BlobStore; +import org.jclouds.aws.s3.S3Connection; +import org.jclouds.aws.s3.domain.BucketMetadata; +import org.jclouds.aws.s3.domain.ObjectMetadata; import org.jclouds.aws.s3.domain.S3Object; -import org.jclouds.aws.s3.internal.StubS3BlobStore; +import org.jclouds.aws.s3.internal.StubS3Connection; +import org.jclouds.blobstore.BlobStore; +import org.jclouds.blobstore.integration.internal.StubBlobStore; import org.jclouds.cloud.ConfiguresCloudConnection; import org.jclouds.http.functions.config.ParserModule; @@ -50,7 +54,10 @@ public class StubS3BlobStoreModule extends AbstractModule { install(new ParserModule()); bind(new TypeLiteral>>() { }).toInstance(map); - bind(S3BlobStore.class).to(StubS3BlobStore.class).asEagerSingleton(); + bind(new TypeLiteral>() { + }).to(new TypeLiteral>() { + }).asEagerSingleton(); + bind(S3Connection.class).to(StubS3Connection.class).asEagerSingleton(); bind(URI.class).annotatedWith(S3.class).toInstance(URI.create("https://localhost/s3stub")); } } diff --git a/aws/s3/core/src/test/java/org/jclouds/aws/s3/integration/S3BlobIntegrationTest.java b/aws/s3/core/src/test/java/org/jclouds/aws/s3/integration/S3BlobIntegrationTest.java index c56eda0c94..748e05d570 100755 --- a/aws/s3/core/src/test/java/org/jclouds/aws/s3/integration/S3BlobIntegrationTest.java +++ b/aws/s3/core/src/test/java/org/jclouds/aws/s3/integration/S3BlobIntegrationTest.java @@ -23,42 +23,13 @@ */ package org.jclouds.aws.s3.integration; -import static org.jclouds.aws.s3.internal.StubS3BlobStore.TEST_ACL_EMAIL; -import static org.jclouds.aws.s3.internal.StubS3BlobStore.TEST_ACL_ID; -import static org.jclouds.aws.s3.options.CopyObjectOptions.Builder.ifSourceETagDoesntMatch; -import static org.jclouds.aws.s3.options.CopyObjectOptions.Builder.ifSourceETagMatches; -import static org.jclouds.aws.s3.options.CopyObjectOptions.Builder.ifSourceModifiedSince; -import static org.jclouds.aws.s3.options.CopyObjectOptions.Builder.ifSourceUnmodifiedSince; -import static org.jclouds.aws.s3.options.CopyObjectOptions.Builder.overrideMetadataWith; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertTrue; - -import java.io.IOException; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -import org.jclouds.aws.s3.S3BlobStore; -import org.jclouds.aws.s3.domain.AccessControlList; +import org.jclouds.aws.s3.S3Connection; import org.jclouds.aws.s3.domain.BucketMetadata; -import org.jclouds.aws.s3.domain.CannedAccessPolicy; import org.jclouds.aws.s3.domain.ObjectMetadata; import org.jclouds.aws.s3.domain.S3Object; -import org.jclouds.aws.s3.domain.AccessControlList.CanonicalUserGrantee; -import org.jclouds.aws.s3.domain.AccessControlList.EmailAddressGrantee; -import org.jclouds.aws.s3.domain.AccessControlList.GroupGranteeURI; -import org.jclouds.aws.s3.domain.AccessControlList.Permission; -import org.jclouds.aws.s3.options.PutObjectOptions; import org.jclouds.blobstore.integration.internal.BaseBlobIntegrationTest; -import org.jclouds.http.HttpResponseException; -import org.jclouds.util.Utils; -import org.joda.time.DateTime; import org.testng.annotations.Test; -import com.google.common.collect.HashMultimap; -import com.google.common.collect.Multimap; - /** * * @author James Murty @@ -66,361 +37,6 @@ import com.google.common.collect.Multimap; */ @Test(groups = { "integration", "live" }, testName = "s3.S3BlobIntegrationTest") public class S3BlobIntegrationTest extends - BaseBlobIntegrationTest { - String sourceKey = "apples"; - String destinationKey = "pears"; - - public void testPublicWriteOnObject() throws InterruptedException, ExecutionException, - TimeoutException, IOException { - final String publicReadWriteObjectKey = "public-read-write-acl"; - final String containerName = getContainerName(); - try { - // Public Read-Write object - context.getApi().putBlob(containerName, new S3Object(publicReadWriteObjectKey, ""), - new PutObjectOptions().withAcl(CannedAccessPolicy.PUBLIC_READ_WRITE)).get(10, - TimeUnit.SECONDS); - - assertEventually(new Runnable() { - public void run() { - try { - AccessControlList acl = context.getApi().getBlobACL(containerName, - publicReadWriteObjectKey).get(10, TimeUnit.SECONDS); - assertEquals(acl.getGrants().size(), 3); - assertEquals(acl.getPermissions(GroupGranteeURI.ALL_USERS).size(), 2); - assertTrue(acl.getOwner() != null); - String ownerId = acl.getOwner().getId(); - assertTrue(acl.hasPermission(ownerId, Permission.FULL_CONTROL)); - assertTrue(acl.hasPermission(GroupGranteeURI.ALL_USERS, Permission.READ)); - assertTrue(acl.hasPermission(GroupGranteeURI.ALL_USERS, Permission.WRITE)); - assertFalse(acl.hasPermission(GroupGranteeURI.ALL_USERS, Permission.READ_ACP)); - assertFalse(acl.hasPermission(GroupGranteeURI.ALL_USERS, Permission.WRITE_ACP)); - assertFalse(acl.hasPermission(GroupGranteeURI.ALL_USERS, Permission.FULL_CONTROL)); - } catch (Exception e) { - Utils. rethrowIfRuntimeOrSameType(e); - } - } - }); - } finally { - returnContainer(containerName); - } - - } - - public void testUpdateObjectACL() throws InterruptedException, ExecutionException, - TimeoutException, IOException { - String containerName = getContainerName(); - try { - String objectKey = "private-acl"; - - // Private object - addBlobToContainer(containerName, objectKey); - AccessControlList acl = context.getApi().getBlobACL(containerName, objectKey).get(10, - TimeUnit.SECONDS); - String ownerId = acl.getOwner().getId(); - - assertEquals(acl.getGrants().size(), 1); - assertTrue(acl.hasPermission(ownerId, Permission.FULL_CONTROL)); - - addGrantsToACL(acl); - assertEquals(acl.getGrants().size(), 4); - assertTrue(context.getApi().putBlobACL(containerName, objectKey, acl).get(10, - TimeUnit.SECONDS)); - - // Confirm that the updated ACL has stuck. - acl = context.getApi().getBlobACL(containerName, objectKey).get(10, TimeUnit.SECONDS); - checkGrants(acl); - - /* - * Revoke all of owner's permissions! - */ - acl.revokeAllPermissions(new CanonicalUserGrantee(ownerId)); - if (!ownerId.equals(TEST_ACL_ID)) - acl.revokeAllPermissions(new CanonicalUserGrantee(TEST_ACL_ID)); - assertEquals(acl.getGrants().size(), 1); - // Only public read permission should remain... - assertTrue(acl.hasPermission(GroupGranteeURI.ALL_USERS, Permission.READ)); - - // Update the object's ACL settings - assertTrue(context.getApi().putBlobACL(containerName, objectKey, acl).get(10, - TimeUnit.SECONDS)); - - // Confirm that the updated ACL has stuck - acl = context.getApi().getBlobACL(containerName, objectKey).get(10, TimeUnit.SECONDS); - assertEquals(acl.getGrants().size(), 1); - assertEquals(acl.getPermissions(ownerId).size(), 0); - assertTrue(acl.hasPermission(GroupGranteeURI.ALL_USERS, Permission.READ), acl.toString()); - } finally { - returnContainer(containerName); - } - - } - - private void checkGrants(AccessControlList acl) { - String ownerId = acl.getOwner().getId(); - - assertEquals(acl.getGrants().size(), 4, acl.toString()); - - assertTrue(acl.hasPermission(ownerId, Permission.FULL_CONTROL), acl.toString()); - assertTrue(acl.hasPermission(GroupGranteeURI.ALL_USERS, Permission.READ), acl.toString()); - assertTrue(acl.hasPermission(ownerId, Permission.WRITE_ACP), acl.toString()); - // EmailAddressGrantee is replaced by a CanonicalUserGrantee, so we cannot test by email addr - assertTrue(acl.hasPermission(TEST_ACL_ID, Permission.READ_ACP), acl.toString()); - } - - private void addGrantsToACL(AccessControlList acl) { - String ownerId = acl.getOwner().getId(); - acl.addPermission(GroupGranteeURI.ALL_USERS, Permission.READ); - acl.addPermission(new EmailAddressGrantee(TEST_ACL_EMAIL), Permission.READ_ACP); - acl.addPermission(new CanonicalUserGrantee(ownerId), Permission.WRITE_ACP); - } - - public void testPrivateAclIsDefaultForObject() throws InterruptedException, ExecutionException, - TimeoutException, IOException { - String privateObjectKey = "private-acl"; - String containerName = getContainerName(); - try { - // Private object - addBlobToContainer(containerName, privateObjectKey); - AccessControlList acl = context.getApi().getBlobACL(containerName, privateObjectKey).get( - 10, TimeUnit.SECONDS); - - assertEquals(acl.getGrants().size(), 1); - assertTrue(acl.getOwner() != null); - String ownerId = acl.getOwner().getId(); - assertTrue(acl.hasPermission(ownerId, Permission.FULL_CONTROL)); - } finally { - returnContainer(containerName); - } - - } - - public void testPublicReadOnObject() throws InterruptedException, ExecutionException, - TimeoutException, IOException { - final String publicReadObjectKey = "public-read-acl"; - final String containerName = getContainerName(); - try { - context.getApi().putBlob(containerName, new S3Object(publicReadObjectKey, ""), - new PutObjectOptions().withAcl(CannedAccessPolicy.PUBLIC_READ)).get(10, - TimeUnit.SECONDS); - - assertEventually(new Runnable() { - public void run() { - try { - AccessControlList acl = context.getApi().getBlobACL(containerName, - publicReadObjectKey).get(10, TimeUnit.SECONDS); - - assertEquals(acl.getGrants().size(), 2); - assertEquals(acl.getPermissions(GroupGranteeURI.ALL_USERS).size(), 1); - assertTrue(acl.getOwner() != null); - String ownerId = acl.getOwner().getId(); - assertTrue(acl.hasPermission(ownerId, Permission.FULL_CONTROL)); - assertTrue(acl.hasPermission(GroupGranteeURI.ALL_USERS, Permission.READ)); - } catch (Exception e) { - Utils. rethrowIfRuntimeOrSameType(e); - } - } - }); - - } finally { - returnContainer(containerName); - } - - } - - public void testMetadataWithCacheControlAndContentDisposition() throws Exception { - String key = "hello"; - - S3Object object = context.newBlob(key); - object.setData(TEST_STRING); - object.getMetadata().setCacheControl("no-cache"); - object.getMetadata().setContentDisposition("attachment; filename=hello.txt"); - String containerName = getContainerName(); - try { - addBlobToContainer(containerName, object); - S3Object newObject = validateContent(containerName, key); - - assertEquals(newObject.getMetadata().getCacheControl(), "no-cache"); - assertEquals(newObject.getMetadata().getContentDisposition(), - "attachment; filename=hello.txt"); - } finally { - returnContainer(containerName); - } - } - - @Test(groups = { "integration", "live" }) - public void testMetadataContentEncoding() throws Exception { - String key = "hello"; - - S3Object object = context.newBlob(key); - object.setData(TEST_STRING); - object.getMetadata().setContentEncoding("x-compress"); - String containerName = getContainerName(); - try { - addBlobToContainer(containerName, object); - S3Object newObject = validateContent(containerName, key); - - assertEquals(newObject.getMetadata().getContentEncoding(), "x-compress"); - } finally { - returnContainer(containerName); - } - } - - public void testCopyObject() throws Exception { - String containerName = getContainerName(); - String destinationContainer = getContainerName(); - - try { - addToContainerAndValidate(containerName, sourceKey); - - context.getApi().copyBlob(containerName, sourceKey, destinationContainer, destinationKey) - .get(10, TimeUnit.SECONDS); - - validateContent(destinationContainer, destinationKey); - } finally { - returnContainer(containerName); - returnContainer(destinationContainer); - - } - } - - private void addToContainerAndValidate(String containerName, String sourceKey) - throws InterruptedException, ExecutionException, TimeoutException, IOException { - addBlobToContainer(containerName, sourceKey); - validateContent(containerName, sourceKey); - } - - // TODO: fails on linux and windows - public void testCopyIfModifiedSince() throws InterruptedException, ExecutionException, - TimeoutException, IOException { - String containerName = getContainerName(); - String destinationContainer = getContainerName(); - try { - DateTime before = new DateTime(); - addToContainerAndValidate(containerName, sourceKey + "mod"); - DateTime after = new DateTime().plusSeconds(1); - - context.getApi().copyBlob(containerName, sourceKey + "mod", destinationContainer, - destinationKey, ifSourceModifiedSince(before)).get(10, TimeUnit.SECONDS); - validateContent(destinationContainer, destinationKey); - - try { - context.getApi().copyBlob(containerName, sourceKey + "mod", destinationContainer, - destinationKey, ifSourceModifiedSince(after)).get(10, TimeUnit.SECONDS); - } catch (ExecutionException e) { - if (e.getCause() instanceof HttpResponseException) { - HttpResponseException ex = (HttpResponseException) e.getCause(); - assertEquals(ex.getResponse().getStatusCode(), 412); - } else { - throw e; - } - } - } finally { - returnContainer(containerName); - returnContainer(destinationContainer); - - } - } - - // TODO: fails on linux and windows - public void testCopyIfUnmodifiedSince() throws InterruptedException, ExecutionException, - TimeoutException, IOException { - String containerName = getContainerName(); - String destinationContainer = getContainerName(); - try { - DateTime before = new DateTime(); - addToContainerAndValidate(containerName, sourceKey + "un"); - DateTime after = new DateTime().plusSeconds(1); - - context.getApi().copyBlob(containerName, sourceKey + "un", destinationContainer, - destinationKey, ifSourceUnmodifiedSince(after)).get(10, TimeUnit.SECONDS); - validateContent(destinationContainer, destinationKey); - - try { - context.getApi().copyBlob(containerName, sourceKey + "un", destinationContainer, - destinationKey, ifSourceModifiedSince(before)).get(10, TimeUnit.SECONDS); - } catch (ExecutionException e) { - HttpResponseException ex = (HttpResponseException) e.getCause(); - assertEquals(ex.getResponse().getStatusCode(), 412); - } - } finally { - returnContainer(containerName); - returnContainer(destinationContainer); - } - } - - public void testCopyIfMatch() throws InterruptedException, ExecutionException, TimeoutException, - IOException { - String containerName = getContainerName(); - String destinationContainer = getContainerName(); - try { - addToContainerAndValidate(containerName, sourceKey); - - context.getApi().copyBlob(containerName, sourceKey, destinationContainer, destinationKey, - ifSourceETagMatches(goodETag)).get(10, TimeUnit.SECONDS); - validateContent(destinationContainer, destinationKey); - - try { - context.getApi().copyBlob(containerName, sourceKey, destinationContainer, - destinationKey, ifSourceETagMatches(badETag)).get(10, TimeUnit.SECONDS); - } catch (ExecutionException e) { - HttpResponseException ex = (HttpResponseException) e.getCause(); - assertEquals(ex.getResponse().getStatusCode(), 412); - } - } finally { - returnContainer(containerName); - returnContainer(destinationContainer); - } - } - - public void testCopyIfNoneMatch() throws IOException, InterruptedException, ExecutionException, - TimeoutException { - String containerName = getContainerName(); - String destinationContainer = getContainerName(); - try { - addToContainerAndValidate(containerName, sourceKey); - - context.getApi().copyBlob(containerName, sourceKey, destinationContainer, destinationKey, - ifSourceETagDoesntMatch(badETag)).get(10, TimeUnit.SECONDS); - validateContent(destinationContainer, destinationKey); - - try { - context.getApi().copyBlob(containerName, sourceKey, destinationContainer, - destinationKey, ifSourceETagDoesntMatch(goodETag)).get(10, TimeUnit.SECONDS); - } catch (ExecutionException e) { - HttpResponseException ex = (HttpResponseException) e.getCause(); - assertEquals(ex.getResponse().getStatusCode(), 412); - } - } finally { - returnContainer(containerName); - returnContainer(destinationContainer); - - } - } - - public void testCopyWithMetadata() throws InterruptedException, ExecutionException, - TimeoutException, IOException { - String containerName = getContainerName(); - String destinationContainer = getContainerName(); - try { - addToContainerAndValidate(containerName, sourceKey); - - Multimap metadata = HashMultimap.create(); - metadata.put("adrian", "cole"); - - context.getApi().copyBlob(containerName, sourceKey, destinationContainer, destinationKey, - overrideMetadataWith(metadata)).get(10, TimeUnit.SECONDS); - - validateContent(destinationContainer, destinationKey); - - ObjectMetadata objectMeta = context.getApi().blobMetadata(destinationContainer, - destinationKey); - - assertEquals(objectMeta.getUserMetadata(), metadata); - } finally { - returnContainer(containerName); - returnContainer(destinationContainer); - - } - } + BaseBlobIntegrationTest { } \ No newline at end of file diff --git a/aws/s3/core/src/test/java/org/jclouds/aws/s3/integration/S3BlobLiveTest.java b/aws/s3/core/src/test/java/org/jclouds/aws/s3/integration/S3BlobLiveTest.java index ff887ac069..8cda3e9568 100644 --- a/aws/s3/core/src/test/java/org/jclouds/aws/s3/integration/S3BlobLiveTest.java +++ b/aws/s3/core/src/test/java/org/jclouds/aws/s3/integration/S3BlobLiveTest.java @@ -23,19 +23,11 @@ */ package org.jclouds.aws.s3.integration; -import static org.jclouds.aws.s3.options.CopyObjectOptions.Builder.overrideAcl; -import static org.jclouds.aws.s3.options.PutObjectOptions.Builder.withAcl; - -import java.net.URL; -import java.util.concurrent.TimeUnit; - -import org.jclouds.aws.s3.S3BlobStore; +import org.jclouds.aws.s3.S3Connection; import org.jclouds.aws.s3.domain.BucketMetadata; -import org.jclouds.aws.s3.domain.CannedAccessPolicy; import org.jclouds.aws.s3.domain.ObjectMetadata; import org.jclouds.aws.s3.domain.S3Object; import org.jclouds.blobstore.integration.internal.BaseBlobLiveTest; -import org.jclouds.util.Utils; import org.testng.annotations.Test; /** @@ -45,48 +37,6 @@ import org.testng.annotations.Test; */ @Test(groups = { "live" }, testName = "s3.S3BlobLiveTest") public class S3BlobLiveTest extends - BaseBlobLiveTest { - - public void testPutCannedAccessPolicyPublic() throws Exception { - String containerName = getContainerName(); - try { - String key = "hello"; - - context.getApi().putBlob(containerName, new S3Object(key, TEST_STRING), - - withAcl(CannedAccessPolicy.PUBLIC_READ)).get(10, TimeUnit.SECONDS); - - URL url = new URL(String.format("http://%1$s.s3.amazonaws.com/%2$s", containerName, key)); - Utils.toStringAndClose(url.openStream()); - } finally { - returnContainer(containerName); - } - - } - - String sourceKey = "apples"; - String destinationKey = "pears"; - - public void testCopyCannedAccessPolicyPublic() throws Exception { - String containerName = getContainerName(); - String destinationContainer = getContainerName(); - try { - addBlobToContainer(containerName, sourceKey); - validateContent(containerName, sourceKey); - - context.getApi().copyBlob(containerName, sourceKey, destinationContainer, destinationKey, - overrideAcl(CannedAccessPolicy.PUBLIC_READ)).get(10, TimeUnit.SECONDS); - - validateContent(destinationContainer, destinationKey); - - URL url = new URL(String.format("http://%1$s.s3.amazonaws.com/%2$s", destinationContainer, - destinationKey)); - Utils.toStringAndClose(url.openStream()); - - } finally { - returnContainer(containerName); - returnContainer(destinationContainer); - } - } + BaseBlobLiveTest { } \ No newline at end of file diff --git a/aws/s3/core/src/test/java/org/jclouds/aws/s3/integration/S3BlobMapIntegrationTest.java b/aws/s3/core/src/test/java/org/jclouds/aws/s3/integration/S3BlobMapIntegrationTest.java index f04c7df1b4..6ebb27b665 100644 --- a/aws/s3/core/src/test/java/org/jclouds/aws/s3/integration/S3BlobMapIntegrationTest.java +++ b/aws/s3/core/src/test/java/org/jclouds/aws/s3/integration/S3BlobMapIntegrationTest.java @@ -23,7 +23,7 @@ */ package org.jclouds.aws.s3.integration; -import org.jclouds.aws.s3.S3BlobStore; +import org.jclouds.aws.s3.S3Connection; import org.jclouds.aws.s3.domain.BucketMetadata; import org.jclouds.aws.s3.domain.ObjectMetadata; import org.jclouds.aws.s3.domain.S3Object; @@ -33,8 +33,8 @@ import org.testng.annotations.Test; /** * @author Adrian Cole */ -@Test(groups = { "integration", "live" }, testName = "s3.S3BlobMapIntegrationTest") +@Test(groups = { "integration", "live" }, testName = "s3.S3BlobMapIntegrationTest") public class S3BlobMapIntegrationTest extends - BaseBlobMapIntegrationTest { + BaseBlobMapIntegrationTest { } \ No newline at end of file diff --git a/aws/s3/core/src/test/java/org/jclouds/aws/s3/integration/S3ContainerIntegrationTest.java b/aws/s3/core/src/test/java/org/jclouds/aws/s3/integration/S3ContainerIntegrationTest.java index 1a2f813577..15f50a6d79 100755 --- a/aws/s3/core/src/test/java/org/jclouds/aws/s3/integration/S3ContainerIntegrationTest.java +++ b/aws/s3/core/src/test/java/org/jclouds/aws/s3/integration/S3ContainerIntegrationTest.java @@ -23,31 +23,10 @@ */ package org.jclouds.aws.s3.integration; -import static org.jclouds.aws.s3.internal.StubS3BlobStore.TEST_ACL_EMAIL; -import static org.jclouds.aws.s3.internal.StubS3BlobStore.TEST_ACL_ID; -import static org.jclouds.aws.s3.options.ListBucketOptions.Builder.afterMarker; -import static org.jclouds.aws.s3.options.ListBucketOptions.Builder.delimiter; -import static org.jclouds.aws.s3.options.ListBucketOptions.Builder.maxResults; -import static org.jclouds.aws.s3.options.ListBucketOptions.Builder.withPrefix; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertTrue; - -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -import org.jclouds.aws.s3.S3BlobStore; -import org.jclouds.aws.s3.domain.AccessControlList; +import org.jclouds.aws.s3.S3Connection; import org.jclouds.aws.s3.domain.BucketMetadata; -import org.jclouds.aws.s3.domain.ListBucketResponse; import org.jclouds.aws.s3.domain.ObjectMetadata; import org.jclouds.aws.s3.domain.S3Object; -import org.jclouds.aws.s3.domain.AccessControlList.CanonicalUserGrantee; -import org.jclouds.aws.s3.domain.AccessControlList.EmailAddressGrantee; -import org.jclouds.aws.s3.domain.AccessControlList.GroupGranteeURI; -import org.jclouds.aws.s3.domain.AccessControlList.Permission; import org.jclouds.blobstore.integration.internal.BaseContainerIntegrationTest; import org.testng.annotations.Test; @@ -57,133 +36,6 @@ import org.testng.annotations.Test; */ @Test(groups = { "integration", "live" }, testName = "s3.S3ContainerIntegrationTest") public class S3ContainerIntegrationTest extends - BaseContainerIntegrationTest { - - public void testListContainerDelimiter() throws InterruptedException, ExecutionException, - TimeoutException, UnsupportedEncodingException { - String containerName = getContainerName(); - try { - String prefix = "apps"; - addTenObjectsUnderPrefix(containerName, prefix); - add15UnderRoot(containerName); - ListBucketResponse container = context.getApi().listBlobs(containerName, delimiter("/")) - .get(10, TimeUnit.SECONDS); - assertEquals(container.getDelimiter(), "/"); - assert !container.isTruncated(); - assertEquals(container.size(), 15); - assertEquals(container.getCommonPrefixes().size(), 1); - } finally { - returnContainer(containerName); - } - - } - - public void testListContainerPrefix() throws InterruptedException, ExecutionException, - TimeoutException, UnsupportedEncodingException { - String containerName = getContainerName(); - try { - String prefix = "apps"; - addTenObjectsUnderPrefix(containerName, prefix); - add15UnderRoot(containerName); - - ListBucketResponse container = context.getApi().listBlobs(containerName, - withPrefix("apps/")).get(10, TimeUnit.SECONDS); - assert !container.isTruncated(); - assertEquals(container.size(), 10); - assertEquals(container.getPrefix(), "apps/"); - } finally { - returnContainer(containerName); - } - - } - - public void testPrivateAclIsDefaultForContainer() throws InterruptedException, - ExecutionException, TimeoutException, IOException { - String containerName = getContainerName(); - try { - AccessControlList acl = context.getApi().getContainerACL(containerName).get(10, - TimeUnit.SECONDS); - assertEquals(acl.getGrants().size(), 1); - assertTrue(acl.getOwner() != null); - String ownerId = acl.getOwner().getId(); - assertTrue(acl.hasPermission(ownerId, Permission.FULL_CONTROL)); - } finally { - returnContainer(containerName); - } - - } - - public void testUpdateContainerACL() throws InterruptedException, ExecutionException, - TimeoutException, IOException, Exception { - String containerName = getContainerName(); - try { - // Confirm the container is private - AccessControlList acl = context.getApi().getContainerACL(containerName).get(10, - TimeUnit.SECONDS); - String ownerId = acl.getOwner().getId(); - assertEquals(acl.getGrants().size(), 1); - assertTrue(acl.hasPermission(ownerId, Permission.FULL_CONTROL)); - - addGrantsToACL(acl); - assertEquals(acl.getGrants().size(), 4); - assertTrue(context.getApi().putContainerACL(containerName, acl).get(10, TimeUnit.SECONDS)); - - // Confirm that the updated ACL has stuck. - acl = context.getApi().getContainerACL(containerName).get(10, TimeUnit.SECONDS); - checkGrants(acl); - } finally { - destroyContainer(containerName); - } - - } - - private void checkGrants(AccessControlList acl) { - String ownerId = acl.getOwner().getId(); - - assertEquals(acl.getGrants().size(), 4, acl.toString()); - - assertTrue(acl.hasPermission(ownerId, Permission.FULL_CONTROL), acl.toString()); - assertTrue(acl.hasPermission(GroupGranteeURI.ALL_USERS, Permission.READ), acl.toString()); - assertTrue(acl.hasPermission(ownerId, Permission.WRITE_ACP), acl.toString()); - // EmailAddressGrantee is replaced by a CanonicalUserGrantee, so we cannot test by email addr - assertTrue(acl.hasPermission(TEST_ACL_ID, Permission.READ_ACP), acl.toString()); - } - - private void addGrantsToACL(AccessControlList acl) { - String ownerId = acl.getOwner().getId(); - acl.addPermission(GroupGranteeURI.ALL_USERS, Permission.READ); - acl.addPermission(new EmailAddressGrantee(TEST_ACL_EMAIL), Permission.READ_ACP); - acl.addPermission(new CanonicalUserGrantee(ownerId), Permission.WRITE_ACP); - } - - public void testListContainerMarker() throws InterruptedException, ExecutionException, - TimeoutException, UnsupportedEncodingException { - String containerName = getContainerName(); - try { - addAlphabetUnderRoot(containerName); - ListBucketResponse container = context.getApi().listBlobs(containerName, afterMarker("y")) - .get(10, TimeUnit.SECONDS); - assertEquals(container.getMarker(), "y"); - assert !container.isTruncated(); - assertEquals(container.size(), 1); - } finally { - returnContainer(containerName); - } - } - - public void testListContainerMaxResults() throws InterruptedException, ExecutionException, - TimeoutException, UnsupportedEncodingException { - String containerName = getContainerName(); - try { - addAlphabetUnderRoot(containerName); - ListBucketResponse container = context.getApi().listBlobs(containerName, maxResults(5)) - .get(10, TimeUnit.SECONDS); - assertEquals(container.getMaxResults(), 5); - assert container.isTruncated(); - assertEquals(container.size(), 5); - } finally { - returnContainer(containerName); - } - } + BaseContainerIntegrationTest { } \ No newline at end of file diff --git a/aws/s3/core/src/test/java/org/jclouds/aws/s3/integration/S3ContainerLiveTest.java b/aws/s3/core/src/test/java/org/jclouds/aws/s3/integration/S3ContainerLiveTest.java index e4172c4dfd..ef4670ad22 100644 --- a/aws/s3/core/src/test/java/org/jclouds/aws/s3/integration/S3ContainerLiveTest.java +++ b/aws/s3/core/src/test/java/org/jclouds/aws/s3/integration/S3ContainerLiveTest.java @@ -23,25 +23,11 @@ */ package org.jclouds.aws.s3.integration; -import static org.jclouds.aws.s3.options.PutBucketOptions.Builder.createIn; -import static org.jclouds.aws.s3.options.PutBucketOptions.Builder.withBucketAcl; -import static org.testng.Assert.assertTrue; - -import java.io.IOException; -import java.net.URL; -import java.util.concurrent.TimeUnit; - -import org.jclouds.aws.s3.S3BlobStore; -import org.jclouds.aws.s3.domain.AccessControlList; +import org.jclouds.aws.s3.S3Connection; import org.jclouds.aws.s3.domain.BucketMetadata; -import org.jclouds.aws.s3.domain.CannedAccessPolicy; import org.jclouds.aws.s3.domain.ObjectMetadata; import org.jclouds.aws.s3.domain.S3Object; -import org.jclouds.aws.s3.domain.AccessControlList.GroupGranteeURI; -import org.jclouds.aws.s3.domain.AccessControlList.Permission; -import org.jclouds.aws.s3.domain.BucketMetadata.LocationConstraint; import org.jclouds.blobstore.integration.internal.BaseContainerLiveTest; -import org.jclouds.util.Utils; import org.testng.annotations.Test; /** @@ -50,63 +36,6 @@ import org.testng.annotations.Test; */ @Test(groups = { "live" }, testName = "s3.S3ContainerLiveTest") public class S3ContainerLiveTest extends - BaseContainerLiveTest { + BaseContainerLiveTest { - public void testPublicReadAccessPolicy() throws Exception { - String containerName = getScratchContainerName(); - try { - context.getApi().createContainer(containerName, withBucketAcl(CannedAccessPolicy.PUBLIC_READ)).get( - 10, TimeUnit.SECONDS); - AccessControlList acl = context.getApi().getContainerACL(containerName).get(10, TimeUnit.SECONDS); - assertTrue(acl.hasPermission(GroupGranteeURI.ALL_USERS, Permission.READ), acl.toString()); - // TODO: I believe that the following should work based on the above acl assertion passing. - // However, it fails on 403 - // URL url = new URL(String.format("http://%s.s3.amazonaws.com", containerName)); - // Utils.toStringAndClose(url.openStream()); - } finally { - destroyContainer(containerName); - } - } - - @Test(expectedExceptions = IOException.class) - public void testDefaultAccessPolicy() throws Exception { - String containerName = getContainerName(); - try { - URL url = new URL(String.format("https://%s.s3.amazonaws.com", containerName)); - Utils.toStringAndClose(url.openStream()); - } finally { - returnContainer(containerName); - } - - } - - /** - * using scratch containerName as we are changing location - */ - public void testEu() throws Exception { - final String containerName = getScratchContainerName(); - try { - context.getApi().createContainer(containerName + "eu", - createIn(LocationConstraint.EU).withBucketAcl(CannedAccessPolicy.PUBLIC_READ)) - .get(30, TimeUnit.SECONDS); - assertEventually(new Runnable() { - public void run() { - try { - AccessControlList acl = context.getApi().getContainerACL(containerName + "eu").get(30, - TimeUnit.SECONDS); - assertTrue(acl.hasPermission(GroupGranteeURI.ALL_USERS, Permission.READ), acl - .toString()); - } catch (Exception e) { - Utils. rethrowIfRuntimeOrSameType(e); - } - } - }); - // TODO: I believe that the following should work based on the above acl assertion passing. - // However, it fails on 403 - // URL url = new URL(String.format("http://%s.s3.amazonaws.com", containerName)); - // Utils.toStringAndClose(url.openStream()); - } finally { - destroyContainer(containerName + "eu"); - } - } } \ No newline at end of file diff --git a/aws/s3/core/src/test/java/org/jclouds/aws/s3/integration/S3InputStreamMapIntegrationTest.java b/aws/s3/core/src/test/java/org/jclouds/aws/s3/integration/S3InputStreamMapIntegrationTest.java index 4d03edb4f1..6acc151d05 100644 --- a/aws/s3/core/src/test/java/org/jclouds/aws/s3/integration/S3InputStreamMapIntegrationTest.java +++ b/aws/s3/core/src/test/java/org/jclouds/aws/s3/integration/S3InputStreamMapIntegrationTest.java @@ -23,7 +23,7 @@ */ package org.jclouds.aws.s3.integration; -import org.jclouds.aws.s3.S3BlobStore; +import org.jclouds.aws.s3.S3Connection; import org.jclouds.aws.s3.domain.BucketMetadata; import org.jclouds.aws.s3.domain.ObjectMetadata; import org.jclouds.aws.s3.domain.S3Object; @@ -33,8 +33,8 @@ import org.testng.annotations.Test; /** * @author Adrian Cole */ -@Test(groups = { "integration", "live" }, testName = "s3.S3InputStreamMapIntegrationTest") +@Test(groups = { "integration", "live" }, testName = "s3.S3InputStreamMapIntegrationTest") public class S3InputStreamMapIntegrationTest extends - BaseInputStreamMapIntegrationTest { + BaseInputStreamMapIntegrationTest { } \ No newline at end of file diff --git a/aws/s3/core/src/test/java/org/jclouds/aws/s3/integration/S3ServiceIntegrationTest.java b/aws/s3/core/src/test/java/org/jclouds/aws/s3/integration/S3ServiceIntegrationTest.java index 8d326afd75..adee8ea39f 100644 --- a/aws/s3/core/src/test/java/org/jclouds/aws/s3/integration/S3ServiceIntegrationTest.java +++ b/aws/s3/core/src/test/java/org/jclouds/aws/s3/integration/S3ServiceIntegrationTest.java @@ -23,9 +23,7 @@ */ package org.jclouds.aws.s3.integration; -import java.util.SortedSet; - -import org.jclouds.aws.s3.S3BlobStore; +import org.jclouds.aws.s3.S3Connection; import org.jclouds.aws.s3.domain.BucketMetadata; import org.jclouds.aws.s3.domain.ObjectMetadata; import org.jclouds.aws.s3.domain.S3Object; @@ -37,18 +35,6 @@ import org.testng.annotations.Test; */ @Test(groups = { "integration", "live" }, testName = "s3.S3ServiceIntegrationTest") public class S3ServiceIntegrationTest extends - BaseServiceIntegrationTest { + BaseServiceIntegrationTest { - void containerExists() throws Exception { - String containerName = getContainerName(); - try { - SortedSet list = context.getApi().listContainers(); - BucketMetadata firstContainer = list.first(); - BucketMetadata toMatch = new BucketMetadata(containerName); - toMatch.setOwner(firstContainer.getOwner()); - assert list.contains(toMatch); - } finally { - returnContainer(containerName); - } - } } \ No newline at end of file diff --git a/aws/s3/core/src/test/java/org/jclouds/aws/s3/integration/S3TestInitializer.java b/aws/s3/core/src/test/java/org/jclouds/aws/s3/integration/S3TestInitializer.java index 93b6f13f12..ce742c7632 100644 --- a/aws/s3/core/src/test/java/org/jclouds/aws/s3/integration/S3TestInitializer.java +++ b/aws/s3/core/src/test/java/org/jclouds/aws/s3/integration/S3TestInitializer.java @@ -23,7 +23,7 @@ */ package org.jclouds.aws.s3.integration; -import org.jclouds.aws.s3.S3BlobStore; +import org.jclouds.aws.s3.S3Connection; import org.jclouds.aws.s3.S3ContextBuilder; import org.jclouds.aws.s3.S3ContextFactory; import org.jclouds.aws.s3.config.StubS3BlobStoreModule; @@ -42,10 +42,10 @@ import com.google.inject.Module; * @author Adrian Cole */ public class S3TestInitializer extends - BaseTestInitializer { + BaseTestInitializer { @Override - protected BlobStoreContext createLiveContext( + protected BlobStoreContext createLiveContext( Module configurationModule, String url, String app, String account, String key) { BaseBlobStoreIntegrationTest.SANITY_CHECK_RETURNED_BUCKET_NAME = true; return new S3ContextBuilder(account, key).withSaxDebug().relaxSSLHostname().withModules( @@ -53,7 +53,7 @@ public class S3TestInitializer extends } @Override - protected BlobStoreContext createStubContext() { + protected BlobStoreContext createStubContext() { return S3ContextFactory.createContext("user", "pass", new StubS3BlobStoreModule()); } diff --git a/aws/s3/core/src/test/java/org/jclouds/aws/s3/internal/StubS3BlobStore.java b/aws/s3/core/src/test/java/org/jclouds/aws/s3/internal/StubS3Connection.java similarity index 84% rename from aws/s3/core/src/test/java/org/jclouds/aws/s3/internal/StubS3BlobStore.java rename to aws/s3/core/src/test/java/org/jclouds/aws/s3/internal/StubS3Connection.java index 3b99b398de..71437872cf 100755 --- a/aws/s3/core/src/test/java/org/jclouds/aws/s3/internal/StubS3BlobStore.java +++ b/aws/s3/core/src/test/java/org/jclouds/aws/s3/internal/StubS3Connection.java @@ -35,6 +35,7 @@ import javax.inject.Inject; import javax.inject.Provider; import org.jclouds.aws.s3.S3BlobStore; +import org.jclouds.aws.s3.S3Connection; import org.jclouds.aws.s3.domain.AccessControlList; import org.jclouds.aws.s3.domain.BucketMetadata; import org.jclouds.aws.s3.domain.CannedAccessPolicy; @@ -70,11 +71,11 @@ import com.google.inject.internal.Nullable; * @author Adrian Cole * @author James Murty */ -public class StubS3BlobStore extends StubBlobStore - implements S3BlobStore { +public class StubS3Connection extends StubBlobStore + implements S3Connection { @Inject - protected StubS3BlobStore(Map> containerToBlobs, + protected StubS3Connection(Map> containerToBlobs, DateService dateService, Provider containerMetaProvider, Provider blobProvider) { super(containerToBlobs, dateService, containerMetaProvider, blobProvider); @@ -91,16 +92,18 @@ public class StubS3BlobStore extends StubBlobStore createContainer(String name, @Nullable PutBucketOptions nullableOptions) { - final PutBucketOptions options = (nullableOptions == null) ? new PutBucketOptions() - : nullableOptions; + public Future putBucketIfNotExists(String name, PutBucketOptions... optionsList) { + final PutBucketOptions options = (optionsList.length == 0) ? new PutBucketOptions() + : optionsList[0]; if (options.getLocationConstraint() != null) bucketToLocation.put(name, options.getLocationConstraint()); keyToAcl.put(name, options.getAcl()); return super.createContainer(name); } - public Future listBlobs(final String name, final ListBucketOptions options) { + public Future listBucket(final String name, ListBucketOptions... optionsList) { + final ListBucketOptions options = (optionsList.length == 0) ? new ListBucketOptions() + : optionsList[0]; return new FutureBase() { public ListBucketResponse get() throws InterruptedException, ExecutionException { final Map realContents = getContainerToBlobs().get(name); @@ -172,11 +175,11 @@ public class StubS3BlobStore extends StubBlobStore copyBlob(final String sourceBucket, final String sourceObject, + public Future copyObject(final String sourceBucket, final String sourceObject, final String destinationBucket, final String destinationObject, - @Nullable CopyObjectOptions nullableOptions) { - final CopyObjectOptions options = (nullableOptions == null) ? new CopyObjectOptions() - : nullableOptions; + CopyObjectOptions... nullableOptions) { + final CopyObjectOptions options = (nullableOptions.length == 0) ? new CopyObjectOptions() + : nullableOptions[0]; return new FutureBase() { public ObjectMetadata get() throws InterruptedException, ExecutionException { Map source = getContainerToBlobs().get(sourceBucket); @@ -223,7 +226,7 @@ public class StubS3BlobStore extends StubBlobStore putBlob(final String bucketName, final S3Object object, + public Future putObject(final String bucketName, final S3Object object, @Nullable PutObjectOptions nullableOptions) { final PutObjectOptions options = (nullableOptions == null) ? new PutObjectOptions() : nullableOptions; @@ -248,7 +251,7 @@ public class StubS3BlobStore extends StubBlobStore getContainerACL(final String bucket) { + public Future getBucketACL(final String bucket) { return new FutureBase() { public AccessControlList get() throws InterruptedException, ExecutionException { return getACLforS3Item(bucket); @@ -256,7 +259,7 @@ public class StubS3BlobStore extends StubBlobStore getBlobACL(final String bucket, final String objectKey) { + public Future getObjectACL(final String bucket, final String objectKey) { return new FutureBase() { public AccessControlList get() throws InterruptedException, ExecutionException { return getACLforS3Item(bucket + "/" + objectKey); @@ -287,7 +290,7 @@ public class StubS3BlobStore extends StubBlobStore putContainerACL(final String bucket, final AccessControlList acl) { + public Future putBucketACL(final String bucket, final AccessControlList acl) { return new FutureBase() { public Boolean get() throws InterruptedException, ExecutionException { keyToAcl.put(bucket, sanitizeUploadedACL(acl)); @@ -296,7 +299,7 @@ public class StubS3BlobStore extends StubBlobStore putBlobACL(final String bucket, final String objectKey, + public Future putObjectACL(final String bucket, final String objectKey, final AccessControlList acl) { return new FutureBase() { public Boolean get() throws InterruptedException, ExecutionException { @@ -306,27 +309,33 @@ public class StubS3BlobStore extends StubBlobStore copyBlob(String sourceBucket, String sourceObject, - String destinationBucket, String destinationObject) { - return copyBlob(sourceBucket, sourceObject, destinationBucket, destinationObject, - CopyObjectOptions.NONE); + public Future getObject(String bucketName, String key, GetOptions... optionsList) { + return super.getBlob(bucketName, key, optionsList.length != 0 ? optionsList[0] : null); } - public Future createContainer(String bucketName) { - return createContainer(bucketName, PutBucketOptions.NONE); + public Future putObject(String bucketName, S3Object object, + PutObjectOptions... optionsList) { + return putObject(bucketName, object, optionsList.length != 0 ? optionsList[0] : null); } - @Override - public Future getBlob(String bucketName, String key) { - return getBlob(bucketName, key, GetOptions.NONE); + public boolean bucketExists(String bucketName) { + return super.containerExists(bucketName); } - public Future listBlobs(String bucketName) { - return listBlobs(bucketName, ListBucketOptions.NONE); + public Future deleteBucketIfEmpty(String bucketName) { + return super.deleteContainerImpl(bucketName); } - public Future putBlob(String bucketName, S3Object object) { - return putBlob(bucketName, object, PutObjectOptions.NONE); + public Future deleteObject(String bucketName, String key) { + return super.removeBlob(bucketName, key); + } + + public ObjectMetadata headObject(String bucketName, String key) { + return super.blobMetadata(bucketName, key); + } + + public SortedSet listOwnedBuckets() { + return super.listContainers(); } } diff --git a/aws/s3/extensions/jets3t/src/main/java/org/jclouds/aws/s3/jets3t/JCloudsS3Service.java b/aws/s3/extensions/jets3t/src/main/java/org/jclouds/aws/s3/jets3t/JCloudsS3Service.java index b62fecc5f6..3812110160 100644 --- a/aws/s3/extensions/jets3t/src/main/java/org/jclouds/aws/s3/jets3t/JCloudsS3Service.java +++ b/aws/s3/extensions/jets3t/src/main/java/org/jclouds/aws/s3/jets3t/JCloudsS3Service.java @@ -33,6 +33,7 @@ import java.util.SortedSet; import java.util.concurrent.TimeUnit; import org.jclouds.aws.s3.S3BlobStore; +import org.jclouds.aws.s3.S3Connection; import org.jclouds.aws.s3.S3ContextFactory; import org.jclouds.aws.s3.domain.BucketMetadata; import org.jclouds.aws.s3.domain.ListBucketResponse; @@ -65,8 +66,8 @@ public class JCloudsS3Service extends S3Service { private static final long serialVersionUID = 1L; - private final BlobStoreContext context; - private final S3BlobStore connection; + private final BlobStoreContext context; + private final S3Connection connection; private final long requestTimeoutMilliseconds = 10000; @@ -106,7 +107,7 @@ public class JCloudsS3Service extends S3Service { try { CopyObjectOptions options = Util.convertCopyObjectOptions(acl, destinationMetadata, ifModifiedSince, ifUnmodifiedSince, ifMatchTags, ifNoneMatchTags); - org.jclouds.aws.s3.domain.ObjectMetadata jcObjectMetadata = connection.copyBlob( + org.jclouds.aws.s3.domain.ObjectMetadata jcObjectMetadata = connection.copyObject( sourceBucketName, sourceObjectKey, destinationBucketName, destinationObjectKey, options).get(requestTimeoutMilliseconds, TimeUnit.MILLISECONDS); @@ -130,7 +131,7 @@ public class JCloudsS3Service extends S3Service { throw new UnsupportedOperationException("Bucket ACL is not yet supported"); try { - if (connection.createContainer(bucketName).get(requestTimeoutMilliseconds, + if (connection.putBucketIfNotExists(bucketName).get(requestTimeoutMilliseconds, TimeUnit.MILLISECONDS)) { // Bucket created. } @@ -149,7 +150,7 @@ public class JCloudsS3Service extends S3Service { @Override protected void deleteBucketImpl(String bucketName) throws S3ServiceException { try { - connection.deleteContainer(bucketName).get(requestTimeoutMilliseconds, + connection.deleteBucketIfEmpty(bucketName).get(requestTimeoutMilliseconds, TimeUnit.MILLISECONDS); } catch (Exception e) { Utils. rethrowIfRuntimeOrSameType(e); @@ -165,7 +166,7 @@ public class JCloudsS3Service extends S3Service { @Override protected void deleteObjectImpl(String bucketName, String objectKey) throws S3ServiceException { try { - connection.removeBlob(bucketName, objectKey).get(requestTimeoutMilliseconds, + connection.deleteObject(bucketName, objectKey).get(requestTimeoutMilliseconds, TimeUnit.MILLISECONDS); } catch (Exception e) { Utils. rethrowIfRuntimeOrSameType(e); @@ -177,7 +178,7 @@ public class JCloudsS3Service extends S3Service { @Override protected AccessControlList getBucketAclImpl(String bucketName) throws S3ServiceException { try { - org.jclouds.aws.s3.domain.AccessControlList jcACL = connection.getContainerACL(bucketName) + org.jclouds.aws.s3.domain.AccessControlList jcACL = connection.getBucketACL(bucketName) .get(requestTimeoutMilliseconds, TimeUnit.MILLISECONDS); return Util.convertAccessControlList(jcACL); } catch (Exception e) { @@ -203,7 +204,7 @@ public class JCloudsS3Service extends S3Service { protected AccessControlList getObjectAclImpl(String bucketName, String objectKey) throws S3ServiceException { try { - org.jclouds.aws.s3.domain.AccessControlList jcACL = connection.getBlobACL(bucketName, + org.jclouds.aws.s3.domain.AccessControlList jcACL = connection.getObjectACL(bucketName, objectKey).get(requestTimeoutMilliseconds, TimeUnit.MILLISECONDS); return Util.convertAccessControlList(jcACL); } catch (Exception e) { @@ -226,7 +227,7 @@ public class JCloudsS3Service extends S3Service { if (ifNoneMatchTags != null) throw new IllegalArgumentException("ifNoneMatchTags"); - return Util.convertObjectHead(connection.blobMetadata(bucketName, objectKey)); + return Util.convertObjectHead(connection.headObject(bucketName, objectKey)); } catch (Exception e) { Utils. rethrowIfRuntimeOrSameType(e); throw new S3ServiceException(String.format("error retrieving object head: %1$s:%2$s", @@ -241,7 +242,7 @@ public class JCloudsS3Service extends S3Service { try { GetOptions options = Util.convertGetObjectOptions(ifModifiedSince, ifUnmodifiedSince, ifMatchTags, ifNoneMatchTags); - return Util.convertObject(connection.getBlob(bucketName, objectKey, options).get( + return Util.convertObject(connection.getObject(bucketName, objectKey, options).get( requestTimeoutMilliseconds, TimeUnit.MILLISECONDS)); } catch (Exception e) { Utils. rethrowIfRuntimeOrSameType(e); @@ -265,7 +266,8 @@ public class JCloudsS3Service extends S3Service { @Override protected S3Bucket[] listAllBucketsImpl() throws S3ServiceException { try { - SortedSet jcBucketList = connection.listContainers(); + SortedSet jcBucketList = connection + .listOwnedBuckets(); return Util.convertBuckets(jcBucketList); } catch (Exception e) { Utils. rethrowIfRuntimeOrSameType(e); @@ -285,7 +287,7 @@ public class JCloudsS3Service extends S3Service { ListBucketOptions options = Util.convertListObjectOptions(prefix, priorLastKey, delimiter, maxListingLength); - jcBucket = connection.listBlobs(bucketName, options).get(requestTimeoutMilliseconds, + jcBucket = connection.listBucket(bucketName, options).get(requestTimeoutMilliseconds, TimeUnit.MILLISECONDS); jsObjects.addAll(Arrays.asList(Util.convertObjectHeads(jcBucket))); @@ -325,7 +327,7 @@ public class JCloudsS3Service extends S3Service { throws S3ServiceException { try { org.jclouds.aws.s3.domain.AccessControlList jcACL = Util.convertAccessControlList(jsACL); - connection.putContainerACL(bucketName, jcACL).get(requestTimeoutMilliseconds, + connection.putBucketACL(bucketName, jcACL).get(requestTimeoutMilliseconds, TimeUnit.MILLISECONDS); } catch (Exception e) { Utils. rethrowIfRuntimeOrSameType(e); @@ -338,7 +340,7 @@ public class JCloudsS3Service extends S3Service { throws S3ServiceException { try { org.jclouds.aws.s3.domain.AccessControlList jcACL = Util.convertAccessControlList(jsACL); - connection.putBlobACL(bucketName, objectKey, jcACL).get(requestTimeoutMilliseconds, + connection.putObjectACL(bucketName, objectKey, jcACL).get(requestTimeoutMilliseconds, TimeUnit.MILLISECONDS); } catch (Exception e) { Utils. rethrowIfRuntimeOrSameType(e); @@ -351,7 +353,7 @@ public class JCloudsS3Service extends S3Service { try { PutObjectOptions options = Util.convertPutObjectOptions(jsObject.getAcl()); org.jclouds.aws.s3.domain.S3Object jcObject = Util.convertObject(jsObject); - byte eTag[] = connection.putBlob(bucketName, jcObject, options).get( + byte eTag[] = connection.putObject(bucketName, jcObject, options).get( requestTimeoutMilliseconds, TimeUnit.MILLISECONDS); jsObject.setMd5Hash(eTag); return jsObject; diff --git a/aws/s3/extensions/jets3t/src/test/java/org/jclouds/aws/s3/jets3t/JCloudsS3ServiceLiveTest.java b/aws/s3/extensions/jets3t/src/test/java/org/jclouds/aws/s3/jets3t/JCloudsS3ServiceLiveTest.java index 0b072e5830..8587cb198d 100755 --- a/aws/s3/extensions/jets3t/src/test/java/org/jclouds/aws/s3/jets3t/JCloudsS3ServiceLiveTest.java +++ b/aws/s3/extensions/jets3t/src/test/java/org/jclouds/aws/s3/jets3t/JCloudsS3ServiceLiveTest.java @@ -44,7 +44,7 @@ import java.util.concurrent.TimeoutException; import javax.ws.rs.core.MediaType; import org.apache.commons.io.IOUtils; -import org.jclouds.aws.s3.S3BlobStore; +import org.jclouds.aws.s3.S3Connection; import org.jclouds.aws.s3.domain.BucketMetadata; import org.jclouds.aws.s3.domain.ListBucketResponse; import org.jclouds.aws.s3.domain.ObjectMetadata; @@ -82,7 +82,7 @@ import com.google.common.collect.Iterators; @Test(groups = { "live" }, testName = "jets3t.JCloudsS3ServiceIntegrationTest") public class JCloudsS3ServiceLiveTest extends - BaseBlobStoreIntegrationTest { + BaseBlobStoreIntegrationTest { AWSCredentials credentials; S3Service service; @@ -112,7 +112,7 @@ public class JCloudsS3ServiceLiveTest try { S3Bucket bucket = service.createBucket(new S3Bucket(bucketName)); assertEquals(bucket.getName(), bucketName); - assertTrue(context.getApi().containerExists(bucketName)); + assertTrue(context.getApi().bucketExists(bucketName)); } finally { returnContainer(bucketName); } @@ -125,7 +125,7 @@ public class JCloudsS3ServiceLiveTest service.deleteBucket(bucketName); assertEventually(new Runnable() { public void run() { - assertFalse(context.getApi().containerExists(bucketName)); + assertFalse(context.getApi().bucketExists(bucketName)); } }); } @@ -212,7 +212,7 @@ public class JCloudsS3ServiceLiveTest S3Bucket[] jsBuckets = service.listAllBuckets(); SortedSet jcBuckets = context.getApi() - .listContainers(); + .listOwnedBuckets(); assert jsBuckets.length == jcBuckets.size(); @@ -363,7 +363,7 @@ public class JCloudsS3ServiceLiveTest // Upload empty object requestObject = new S3Object(objectKey); jsResultObject = service.putObject(new S3Bucket(bucketName), requestObject); - jcObject = context.getApi().getBlob(bucketName, objectKey).get(10, TimeUnit.SECONDS); + jcObject = context.getApi().getObject(bucketName, objectKey).get(10, TimeUnit.SECONDS); // TODO null keys from s3object! assertEquals(jcObject.getKey(), objectKey); assertEquals(jcObject.getMetadata().getSize(), 0); assertEquals(jcObject.getMetadata().getContentType(), MediaType.APPLICATION_OCTET_STREAM); @@ -374,7 +374,7 @@ public class JCloudsS3ServiceLiveTest // Upload unicode-named object requestObject = new S3Object("nd-object"); jsResultObject = service.putObject(new S3Bucket(bucketName), requestObject); - jcObject = context.getApi().getBlob(bucketName, requestObject.getKey()).get(10, + jcObject = context.getApi().getObject(bucketName, requestObject.getKey()).get(10, TimeUnit.SECONDS); // TODO null keys from s3object! assertEquals(jcObject.getKey(), requestObject.getKey()); assertEquals(jcObject.getMetadata().getSize(), 0); @@ -387,7 +387,7 @@ public class JCloudsS3ServiceLiveTest String data = "This is my nd data"; requestObject = new S3Object(objectKey, data); jsResultObject = service.putObject(new S3Bucket(bucketName), requestObject); - jcObject = context.getApi().getBlob(bucketName, objectKey).get(10, TimeUnit.SECONDS); + jcObject = context.getApi().getObject(bucketName, objectKey).get(10, TimeUnit.SECONDS); assertEquals(jcObject.getMetadata().getSize(), data.getBytes("UTF-8").length); assertTrue(jcObject.getMetadata().getContentType().startsWith("text/plain")); assertEquals(jsResultObject.getContentLength(), data.getBytes("UTF-8").length); @@ -397,7 +397,7 @@ public class JCloudsS3ServiceLiveTest requestObject = new S3Object(objectKey); requestObject.addMetadata("x-amz-meta-" + "my-metadata-1", "value-1"); jsResultObject = service.putObject(new S3Bucket(bucketName), requestObject); - jcObject = context.getApi().getBlob(bucketName, objectKey).get(10, TimeUnit.SECONDS); + jcObject = context.getApi().getObject(bucketName, objectKey).get(10, TimeUnit.SECONDS); assertEquals(Iterables.getLast(jcObject.getMetadata().getUserMetadata().get( "my-metadata-1")), "value-1"); assertEquals(jsResultObject.getMetadata("x-amz-meta-" + "my-metadata-1"), "value-1"); @@ -406,7 +406,7 @@ public class JCloudsS3ServiceLiveTest requestObject = new S3Object(objectKey); requestObject.setAcl(AccessControlList.REST_CANNED_PUBLIC_READ); jsResultObject = service.putObject(new S3Bucket(bucketName), requestObject); - org.jclouds.aws.s3.domain.AccessControlList jcACL = context.getApi().getBlobACL( + org.jclouds.aws.s3.domain.AccessControlList jcACL = context.getApi().getObjectACL( bucketName, objectKey).get(10, TimeUnit.SECONDS); assertTrue(jcACL.hasPermission(GroupGranteeURI.ALL_USERS, Permission.READ)); assertTrue(jcACL.hasPermission(jcACL.getOwner().getId(), Permission.FULL_CONTROL)); @@ -422,7 +422,7 @@ public class JCloudsS3ServiceLiveTest data = "Here is some dt for you"; requestObject.setDataInputStream(new ByteArrayInputStream(data.getBytes("UTF-8"))); jsResultObject = service.putObject(new S3Bucket(bucketName), requestObject); - jcObject = context.getApi().getBlob(bucketName, objectKey).get(10, TimeUnit.SECONDS); + jcObject = context.getApi().getObject(bucketName, objectKey).get(10, TimeUnit.SECONDS); assertTrue(jsResultObject.verifyData(data.getBytes("UTF-8"))); assertEquals(jsResultObject.getMd5HashAsHex(), HttpUtils.toHexString(jcObject .getMetadata().getETag())); @@ -458,7 +458,7 @@ public class JCloudsS3ServiceLiveTest destinationObject = new S3Object(destinationObjectKey); copyResult = service.copyObject(bucketName, sourceObjectKey, bucketName, destinationObject, false); - jcDestinationObject = context.getApi().getBlob(bucketName, destinationObject.getKey()) + jcDestinationObject = context.getApi().getObject(bucketName, destinationObject.getKey()) .get(10, TimeUnit.SECONDS); // TODO null keys from s3object! assertEquals(jcDestinationObject.getKey(), // destinationObjectKey); @@ -467,7 +467,7 @@ public class JCloudsS3ServiceLiveTest assertEquals(copyResult.get("ETag"), HttpUtils.toHexString(jcDestinationObject .getMetadata().getETag())); // Test destination ACL is unchanged (ie private) - org.jclouds.aws.s3.domain.AccessControlList jcACL = context.getApi().getBlobACL( + org.jclouds.aws.s3.domain.AccessControlList jcACL = context.getApi().getObjectACL( bucketName, destinationObject.getKey()).get(10, TimeUnit.SECONDS); assertEquals(jcACL.getGrants().size(), 1); assertTrue(jcACL.hasPermission(jcACL.getOwner().getId(), Permission.FULL_CONTROL)); @@ -477,12 +477,12 @@ public class JCloudsS3ServiceLiveTest destinationObject.addMetadata("x-amz-meta-" + metadataName, destinationMetadataValue); copyResult = service.copyObject(bucketName, sourceObjectKey, bucketName, destinationObject, true); - jcDestinationObject = context.getApi().getBlob(bucketName, destinationObject.getKey()) + jcDestinationObject = context.getApi().getObject(bucketName, destinationObject.getKey()) .get(10, TimeUnit.SECONDS); assertEquals(Iterators.getLast(jcDestinationObject.getMetadata().getUserMetadata().get( metadataName).iterator()), destinationMetadataValue); // Test destination ACL is unchanged (ie private) - jcACL = context.getApi().getBlobACL(bucketName, destinationObject.getKey()).get(10, + jcACL = context.getApi().getObjectACL(bucketName, destinationObject.getKey()).get(10, TimeUnit.SECONDS); assertEquals(jcACL.getGrants().size(), 1); assertTrue(jcACL.hasPermission(jcACL.getOwner().getId(), Permission.FULL_CONTROL)); @@ -493,7 +493,7 @@ public class JCloudsS3ServiceLiveTest copyResult = service.copyObject(bucketName, sourceObjectKey, bucketName, destinationObject, false); // Test destination ACL is changed (ie public-read) - jcACL = context.getApi().getBlobACL(bucketName, destinationObject.getKey()).get(10, + jcACL = context.getApi().getObjectACL(bucketName, destinationObject.getKey()).get(10, TimeUnit.SECONDS); assertEquals(jcACL.getGrants().size(), 2); assertTrue(jcACL.hasPermission(jcACL.getOwner().getId(), Permission.FULL_CONTROL)); @@ -673,7 +673,7 @@ public class JCloudsS3ServiceLiveTest multiService.putObjects(bucket, objects); assertEquals(countOfUploadCompletions[0], OBJECT_COUNT); - ListBucketResponse theBucket = context.getApi().listBlobs(bucketName).get(10, + ListBucketResponse theBucket = context.getApi().listBucket(bucketName).get(10, TimeUnit.SECONDS); assertEquals(theBucket.size(), OBJECT_COUNT); diff --git a/aws/s3/perftest/src/test/java/org/jclouds/aws/s3/BaseJCloudsPerformanceLiveTest.java b/aws/s3/perftest/src/test/java/org/jclouds/aws/s3/BaseJCloudsPerformanceLiveTest.java index a81b9dae99..bbcc9c86b3 100644 --- a/aws/s3/perftest/src/test/java/org/jclouds/aws/s3/BaseJCloudsPerformanceLiveTest.java +++ b/aws/s3/perftest/src/test/java/org/jclouds/aws/s3/BaseJCloudsPerformanceLiveTest.java @@ -57,7 +57,7 @@ public abstract class BaseJCloudsPerformanceLiveTest extends BasePerformanceLive org.jclouds.aws.s3.domain.S3Object object = new org.jclouds.aws.s3.domain.S3Object(key); object.getMetadata().setContentType(contentType); object.setData(data); - return context.getApi().putBlob(bucket, object).get(120, TimeUnit.SECONDS) != null; + return context.getApi().putObject(bucket, object).get(120, TimeUnit.SECONDS) != null; } @Override @@ -66,7 +66,7 @@ public abstract class BaseJCloudsPerformanceLiveTest extends BasePerformanceLive org.jclouds.aws.s3.domain.S3Object object = new org.jclouds.aws.s3.domain.S3Object(key); object.getMetadata().setContentType(contentType); object.setData(data); - return context.getApi().putBlob(bucket, object).get(120, TimeUnit.SECONDS) != null; + return context.getApi().putObject(bucket, object).get(120, TimeUnit.SECONDS) != null; } @Override @@ -76,7 +76,7 @@ public abstract class BaseJCloudsPerformanceLiveTest extends BasePerformanceLive object.getMetadata().setContentType(contentType); object.setData(data); object.getMetadata().setSize(data.available()); - return context.getApi().putBlob(bucket, object).get(120, TimeUnit.SECONDS) != null; + return context.getApi().putObject(bucket, object).get(120, TimeUnit.SECONDS) != null; } @Override @@ -85,6 +85,6 @@ public abstract class BaseJCloudsPerformanceLiveTest extends BasePerformanceLive org.jclouds.aws.s3.domain.S3Object object = new org.jclouds.aws.s3.domain.S3Object(key); object.getMetadata().setContentType(contentType); object.setData(data); - return context.getApi().putBlob(bucket, object).get(120, TimeUnit.SECONDS) != null; + return context.getApi().putObject(bucket, object).get(120, TimeUnit.SECONDS) != null; } } diff --git a/aws/s3/perftest/src/test/java/org/jclouds/aws/s3/BasePerformanceLiveTest.java b/aws/s3/perftest/src/test/java/org/jclouds/aws/s3/BasePerformanceLiveTest.java index d587b3597d..fd4264966f 100755 --- a/aws/s3/perftest/src/test/java/org/jclouds/aws/s3/BasePerformanceLiveTest.java +++ b/aws/s3/perftest/src/test/java/org/jclouds/aws/s3/BasePerformanceLiveTest.java @@ -55,7 +55,7 @@ import org.testng.annotations.Test; * @author Adrian Cole */ public abstract class BasePerformanceLiveTest extends - BaseBlobStoreIntegrationTest { + BaseBlobStoreIntegrationTest { protected int timeoutSeconds = 10; protected int loopCount = 100; protected ExecutorService exec; @@ -94,7 +94,7 @@ public abstract class BasePerformanceLiveTest extends protected String createScratchContainerInEU() throws InterruptedException, ExecutionException, TimeoutException { String containerName = getScratchContainerName(); - context.getApi().createContainer(containerName, createIn(LocationConstraint.EU)).get(30, + context.getApi().putBucketIfNotExists(containerName, createIn(LocationConstraint.EU)).get(30, TimeUnit.SECONDS); return containerName; } diff --git a/aws/s3/samples/createandlistbuckets/src/main/java/org/jclouds/aws/s3/samples/MainApp.java b/aws/s3/samples/createandlistbuckets/src/main/java/org/jclouds/aws/s3/samples/MainApp.java index 09c3dee94a..22c2de3c0d 100644 --- a/aws/s3/samples/createandlistbuckets/src/main/java/org/jclouds/aws/s3/samples/MainApp.java +++ b/aws/s3/samples/createandlistbuckets/src/main/java/org/jclouds/aws/s3/samples/MainApp.java @@ -66,14 +66,14 @@ public class MainApp { try { // Create Bucket - ((S3Context) context).getApi().createContainer(bucketName).get(10, TimeUnit.SECONDS); + ((S3Context) context).getApi().putBucketIfNotExists(bucketName).get(10, TimeUnit.SECONDS); BlobMap blobMap = context.createBlobMap(bucketName); Blob blob = context.newBlob("test"); blob.setData("testdata"); blobMap.put("test", blob); // List bucket - for (BucketMetadata bucketObj : ((S3Context) context).getApi().listContainers()) { + for (BucketMetadata bucketObj : ((S3Context) context).getApi().listOwnedBuckets()) { System.out.println(String.format(" %1$s", bucketObj)); System.out.println(String.format(": %1$s entries%n", context.createInputStreamMap( bucketObj.getName()).size())); diff --git a/aws/s3/samples/googleappengine/src/main/java/org/jclouds/samples/googleappengine/GetAllBucketsController.java b/aws/s3/samples/googleappengine/src/main/java/org/jclouds/samples/googleappengine/GetAllBucketsController.java index 752e2d163e..cc058892c1 100644 --- a/aws/s3/samples/googleappengine/src/main/java/org/jclouds/samples/googleappengine/GetAllBucketsController.java +++ b/aws/s3/samples/googleappengine/src/main/java/org/jclouds/samples/googleappengine/GetAllBucketsController.java @@ -86,7 +86,7 @@ public class GetAllBucketsController extends HttpServlet { private void addMyBucketsToRequest(HttpServletRequest request) throws InterruptedException, ExecutionException, TimeoutException { System.err.println(context.getAccount() + ":" + context.getEndPoint()); - SortedSet myBucketMetadata = context.getApi().listContainers(); + SortedSet myBucketMetadata = context.getApi().listOwnedBuckets(); SortedSet myBuckets = Sets.newTreeSet(Iterables.transform(myBucketMetadata, metadataToBucketResultProvider.get())); request.setAttribute("buckets", myBuckets); diff --git a/aws/s3/samples/googleappengine/src/main/java/org/jclouds/samples/googleappengine/JCloudsServlet.java b/aws/s3/samples/googleappengine/src/main/java/org/jclouds/samples/googleappengine/JCloudsServlet.java index eda558c9db..e93d031a6e 100644 --- a/aws/s3/samples/googleappengine/src/main/java/org/jclouds/samples/googleappengine/JCloudsServlet.java +++ b/aws/s3/samples/googleappengine/src/main/java/org/jclouds/samples/googleappengine/JCloudsServlet.java @@ -108,14 +108,14 @@ public class JCloudsServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { - SortedSet myBucketMetadata = context.getApi().listContainers(); + SortedSet myBucketMetadata = context.getApi().listOwnedBuckets(); SortedSet myBuckets = Sets.newTreeSet(); for (BucketMetadata metadata : myBucketMetadata) { BucketResult result = new BucketResult(); result.setName(metadata.getName()); try { try { - ListBucketResponse bucket = context.getApi().listBlobs(metadata.getName()).get( + ListBucketResponse bucket = context.getApi().listBucket(metadata.getName()).get( 10, TimeUnit.SECONDS); result.setSize(bucket.size() + ""); } catch (ContainerNotFoundException ex) { diff --git a/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/AzureBlobConnection.java b/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/AzureBlobConnection.java new file mode 100644 index 0000000000..f3b1e3e510 --- /dev/null +++ b/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/AzureBlobConnection.java @@ -0,0 +1,316 @@ +/** + * + * Copyright (C) 2009 Global Cloud Specialists, Inc. + * + * ==================================================================== + * 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.azure.storage.blob; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.HEAD; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; + +import org.jclouds.azure.storage.AzureBlob; +import org.jclouds.azure.storage.blob.domain.Blob; +import org.jclouds.azure.storage.blob.domain.BlobMetadata; +import org.jclouds.azure.storage.blob.domain.ContainerMetadata; +import org.jclouds.azure.storage.blob.domain.ListBlobsResponse; +import org.jclouds.azure.storage.blob.functions.ParseBlobFromHeadersAndHttpContent; +import org.jclouds.azure.storage.blob.functions.ParseBlobMetadataFromHeaders; +import org.jclouds.azure.storage.blob.functions.ParseContainerMetadataFromHeaders; +import org.jclouds.azure.storage.blob.functions.ReturnTrueIfContainerAlreadyExists; +import org.jclouds.azure.storage.blob.options.CreateContainerOptions; +import org.jclouds.azure.storage.blob.options.ListBlobsOptions; +import org.jclouds.azure.storage.blob.xml.AccountNameEnumerationResultsHandler; +import org.jclouds.azure.storage.blob.xml.ContainerNameEnumerationResultsHandler; +import org.jclouds.azure.storage.domain.BoundedSortedSet; +import org.jclouds.azure.storage.filters.SharedKeyAuthentication; +import org.jclouds.azure.storage.options.ListOptions; +import org.jclouds.azure.storage.reference.AzureStorageHeaders; +import org.jclouds.blobstore.binders.BlobBinder; +import org.jclouds.blobstore.binders.UserMetadataBinder; +import org.jclouds.blobstore.functions.BlobKey; +import org.jclouds.blobstore.functions.ReturnVoidOnNotFoundOr404; +import org.jclouds.blobstore.functions.ThrowKeyNotFoundOn404; +import org.jclouds.http.functions.ParseETagHeader; +import org.jclouds.http.functions.ReturnTrueOn404; +import org.jclouds.http.options.GetOptions; +import org.jclouds.rest.Endpoint; +import org.jclouds.rest.EntityParam; +import org.jclouds.rest.ExceptionParser; +import org.jclouds.rest.Headers; +import org.jclouds.rest.ParamParser; +import org.jclouds.rest.QueryParams; +import org.jclouds.rest.RequestFilters; +import org.jclouds.rest.ResponseParser; +import org.jclouds.rest.SkipEncoding; +import org.jclouds.rest.XMLResponseParser; + +import com.google.common.collect.Multimap; + +/** + * Provides access to Azure Blob via their REST API. + *

+ * All commands return a Future of the result from Azure Blob. Any exceptions incurred during + * processing will be wrapped in an {@link ExecutionException} as documented in {@link Future#get()}. + * + * @see + * @author Adrian Cole + */ +@SkipEncoding('/') +@RequestFilters(SharedKeyAuthentication.class) +@Headers(keys = AzureStorageHeaders.VERSION, values = "2009-07-17") +@Endpoint(AzureBlob.class) +public interface AzureBlobConnection { + + /** + * The List Containers operation returns a list of the containers under the specified account. + *

+ * The 2009-07-17 version of the List Containers operation times out after 30 seconds. + * + * @param listOptions + * controls the number or type of results requested + * @see ListOptions + */ + @GET + @XMLResponseParser(AccountNameEnumerationResultsHandler.class) + @Path("/") + @QueryParams(keys = "comp", values = "list") + BoundedSortedSet listContainers(ListOptions... listOptions); + + /** + * The Create Container operation creates a new container under the specified account. If the + * container with the same name already exists, the operation fails. + *

+ * The container resource includes metadata and properties for that container. It does not + * include a list of the blobs contained by the container. + * + * @see CreateContainerOptions + * + */ + @PUT + @Path("{container}") + @ExceptionParser(ReturnTrueIfContainerAlreadyExists.class) + @QueryParams(keys = "restype", values = "container") + Future createContainer(@PathParam("container") String container, + CreateContainerOptions... options); + + /** + * The Get Container Properties operation returns all user-defined metadata and system properties + * for the specified container. The data returned does not include the container's list of blobs. + */ + @HEAD + @Path("{container}") + @QueryParams(keys = "restype", values = "container") + @ResponseParser(ParseContainerMetadataFromHeaders.class) + ContainerMetadata getContainerProperties(@PathParam("container") String container); + + /** + * The Set Container Metadata operation sets one or more user-defined name/value pairs for the + * specified container.

Remarks

+ * + * + * Calling the Set Container Metadata operation overwrites all existing metadata that is + * associated with the container. It's not possible to modify an individual name/value pair. + *

+ * You may also set metadata for a container at the time it is created. + *

+ * Calling Set Container Metadata updates the ETag for the container. + */ + @PUT + @Path("{container}") + @QueryParams(keys = { "restype", "comp" }, values = { "container", "metadata" }) + void setContainerMetadata(@PathParam("container") String container, + @EntityParam(UserMetadataBinder.class) Multimap metadata); + + /** + * The Delete Container operation marks the specified container for deletion. The container and + * any blobs contained within it are later deleted during garbage collection. + *

+ * When a container is deleted, a container with the same name cannot be created for at least 30 + * seconds; the container may not be available for more than 30 seconds if the service is still + * processing the request. While the container is being deleted, attempts to create a container + * of the same name will fail with status code 409 (Conflict), with the service returning + * additional error information indicating that the container is being deleted. All other + * operations, including operations on any blobs under the container, will fail with status code + * 404 (Not Found) while the container is being deleted. + * + */ + @DELETE + @Path("{container}") + @ExceptionParser(ReturnVoidOnNotFoundOr404.class) + @QueryParams(keys = "restype", values = "container") + Future deleteContainer(@PathParam("container") String container); + + /** + * The root container is a default container that may be inferred from a URL requesting a blob + * resource. The root container makes it possible to reference a blob from the top level of the + * storage account hierarchy, without referencing the container name. + *

+ * The container resource includes metadata and properties for that container. It does not + * include a list of the blobs contained by the container. + * + * @see CreateContainerOptions + * + */ + @PUT + @Path("$root") + @ExceptionParser(ReturnTrueIfContainerAlreadyExists.class) + @QueryParams(keys = "restype", values = "container") + Future createRootContainer(CreateContainerOptions... options); + + /** + * The Delete Container operation marks the specified container for deletion. The container and + * any blobs contained within it are later deleted during garbage collection.

Remarks

+ * When a container is deleted, a container with the same name cannot be created for at least 30 + * seconds; the container may not be available for more than 30 seconds if the service is still + * processing the request. While the container is being deleted, attempts to create a container + * of the same name will fail with status code 409 (Conflict), with the service returning + * additional error information indicating that the container is being deleted. All other + * operations, including operations on any blobs under the container, will fail with status code + * 404 (Not Found) while the container is being deleted. + * + * @see deleteContainer(String) + * @see createRootContainer(CreateContainerOptions) + */ + @DELETE + @Path("$root") + @ExceptionParser(ReturnTrueOn404.class) + @QueryParams(keys = "restype", values = "container") + Future deleteRootContainer(); + + /** + * The List Blobs operation enumerates the list of blobs under the specified container. + *

+ *

Authorization

+ * + * If the container's access control list (ACL) is set to allow anonymous access, any client may + * call this operation. + *

Remarks

+ * + * If you specify a value for the maxresults parameter and the number of blobs to return exceeds + * this value, or exceeds the default value for maxresults, the response body will contain a + * NextMarker element that indicates the next blob to return on a subsequent request. To return + * the next set of items, specify the value of NextMarker as the marker parameter on the URI for + * the subsequent request. + *

+ * Note that the value of NextMarker should be treated as opaque. + *

+ * The delimiter parameter enables the caller to traverse the blob namespace by using a + * user-configured delimiter. The delimiter may be a single character or a string. When the + * request includes this parameter, the operation returns a BlobPrefix element. The BlobPrefix + * element is returned in place of all blobs whose names begin with the same substring up to the + * appearance of the delimiter character. The value of the BlobPrefix element is + * substring+delimiter, where substring is the common substring that begins one or more blob + * names, and delimiter is the value of the delimiter parameter. + *

+ * You can use the value of BlobPrefix to make a subsequent call to list the blobs that begin + * with this prefix, by specifying the value of BlobPrefix for the prefix parameter on the + * request URI. In this way, you can traverse a virtual hierarchy of blobs as though it were a + * file system. + *

+ * Note that each BlobPrefix element returned counts toward the maximum result, just as each Blob + * element does. + *

+ * Blobs are listed in alphabetical order in the response body. + */ + @GET + @XMLResponseParser(ContainerNameEnumerationResultsHandler.class) + @Path("{container}") + @QueryParams(keys = { "restype", "comp" }, values = { "container", "list" }) + Future listBlobs(@PathParam("container") String container, + ListBlobsOptions... options); + + @GET + @XMLResponseParser(ContainerNameEnumerationResultsHandler.class) + @Path("$root") + @QueryParams(keys = { "restype", "comp" }, values = { "container", "list" }) + Future listBlobs(ListBlobsOptions... options); + + /** + * The Put Blob operation creates a new blob or updates the content of an existing blob. + *

+ * Updating an existing blob overwrites any existing metadata on the blob. Partial updates are + * not supported; the content of the existing blob is overwritten with the content of the new + * blob. + *

+ *

Remarks

+ * The maximum upload size for a blob is 64 MB. If your blob is larger than 64 MB, you may upload + * it as a set of blocks. For more information, see the Put Block and Put Block List operations. + *

+ * If you attempt to upload a blob that is larger than 64 MB, the service returns status code 413 + * (Request Entity Too Large). The Blob service also returns additional information about the + * error in the response, including the maximum blob size permitted in bytes. + *

+ * A Put Blob operation is permitted 10 minutes per MB to complete. If the operation is taking + * longer than 10 minutes per MB on average, the operation will timeout. + */ + @PUT + @Path("{container}/{key}") + @ResponseParser(ParseETagHeader.class) + Future putBlob(@PathParam("container") String container, + @PathParam("key") @ParamParser(BlobKey.class) @EntityParam(BlobBinder.class) Blob object); + + /** + * The Get Blob operation reads or downloads a blob from the system, including its metadata and + * properties. + */ + @GET + @ResponseParser(ParseBlobFromHeadersAndHttpContent.class) + @ExceptionParser(ThrowKeyNotFoundOn404.class) + @Path("{container}/{key}") + Future getBlob(@PathParam("container") String container, @PathParam("key") String key, + GetOptions... options); + + /** + * The Get Blob Properties operation returns all user-defined metadata, standard HTTP properties, + * and system properties for the blob. It does not return the content of the blob. + */ + @GET + @Headers(keys = "Range", values = "bytes=0-0") + // should use HEAD, this is a hack per http://code.google.com/p/jclouds/issues/detail?id=92 + @ResponseParser(ParseBlobMetadataFromHeaders.class) + @ExceptionParser(ThrowKeyNotFoundOn404.class) + @Path("{container}/{key}") + BlobMetadata getBlobProperties(@PathParam("container") String container, + @PathParam("key") String key); + + @PUT + @Path("{container}/{key}") + @QueryParams(keys = { "comp" }, values = { "metadata" }) + void setBlobMetadata(@PathParam("container") String container, @PathParam("key") String key, + @EntityParam(UserMetadataBinder.class) Multimap metadata); + + /** + * The Delete Blob operation marks the specified blob for deletion. The blob is later deleted + * during garbage collection. + */ + @DELETE + @ExceptionParser(ReturnVoidOnNotFoundOr404.class) + @Path("{container}/{key}") + Future deleteBlob(@PathParam("container") String container, @PathParam("key") String key); + +} diff --git a/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/AzureBlobContext.java b/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/AzureBlobContext.java index eb121e66e6..75270c373e 100644 --- a/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/AzureBlobContext.java +++ b/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/AzureBlobContext.java @@ -39,6 +39,6 @@ import org.jclouds.cloud.CloudContext; * */ public interface AzureBlobContext extends - BlobStoreContext { + BlobStoreContext { } \ No newline at end of file diff --git a/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/AzureBlobContextBuilder.java b/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/AzureBlobContextBuilder.java index 3eeed66eef..75b33f16b4 100755 --- a/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/AzureBlobContextBuilder.java +++ b/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/AzureBlobContextBuilder.java @@ -60,7 +60,7 @@ import com.google.inject.TypeLiteral; * @see AzureBlobContext */ public class AzureBlobContextBuilder extends - BlobStoreContextBuilder { + BlobStoreContextBuilder { @Override public AzureBlobContext buildContext() { @@ -68,7 +68,7 @@ public class AzureBlobContextBuilder extends } public AzureBlobContextBuilder(Properties props) { - super(new TypeLiteral() { + super(new TypeLiteral() { }, new TypeLiteral() { }, new TypeLiteral() { }, new TypeLiteral() { @@ -78,6 +78,12 @@ public class AzureBlobContextBuilder extends "https://{account}.blob.core.windows.net"); } +// @Override +// protected void addBlobStoreModule(List modules) { +// modules.add(BlobStoreMapsModule.Builder.newBuilder(containerMetadataType, blobMetadataType, +// blobType).withClearContainerStrategy(RecreateClearContainerStrategy.class).build()); +// } + public AzureBlobContextBuilder(String id, String secret) { this(new Properties()); properties.setProperty(AzureStorageConstants.PROPERTY_AZURESTORAGE_ACCOUNT, checkNotNull(id, @@ -152,7 +158,7 @@ public class AzureBlobContextBuilder extends @Override public AzureBlobContextBuilder withSaxDebug() { - return (AzureBlobContextBuilder) (AzureBlobContextBuilder) super.withSaxDebug(); + return (AzureBlobContextBuilder) super.withSaxDebug(); } @Override diff --git a/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/AzureBlobStore.java b/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/AzureBlobStore.java index d87f769222..34a70ce2c2 100644 --- a/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/AzureBlobStore.java +++ b/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/AzureBlobStore.java @@ -39,24 +39,22 @@ import org.jclouds.azure.storage.blob.domain.Blob; import org.jclouds.azure.storage.blob.domain.BlobMetadata; import org.jclouds.azure.storage.blob.domain.ContainerMetadata; import org.jclouds.azure.storage.blob.domain.ListBlobsResponse; +import org.jclouds.azure.storage.blob.functions.MD5EnforcingBlobBinder; import org.jclouds.azure.storage.blob.functions.ParseBlobFromHeadersAndHttpContent; import org.jclouds.azure.storage.blob.functions.ParseBlobMetadataFromHeaders; import org.jclouds.azure.storage.blob.functions.ReturnTrueIfContainerAlreadyExists; import org.jclouds.azure.storage.blob.options.CreateContainerOptions; import org.jclouds.azure.storage.blob.xml.AccountNameEnumerationResultsHandler; -import org.jclouds.azure.storage.blob.xml.ContainerNameEnumerationResultsHandler; -import org.jclouds.azure.storage.domain.BoundedSortedSet; +import org.jclouds.azure.storage.blob.xml.AddMD5ToListBlobsResponse; import org.jclouds.azure.storage.filters.SharedKeyAuthentication; -import org.jclouds.azure.storage.options.CreateOptions; import org.jclouds.azure.storage.options.ListOptions; import org.jclouds.azure.storage.reference.AzureStorageHeaders; import org.jclouds.blobstore.BlobStore; -import org.jclouds.blobstore.binders.BlobBinder; import org.jclouds.blobstore.functions.BlobKey; +import org.jclouds.blobstore.functions.ReturnVoidOnNotFoundOr404; import org.jclouds.blobstore.functions.ThrowKeyNotFoundOn404; import org.jclouds.http.functions.ParseETagHeader; import org.jclouds.http.functions.ReturnFalseOn404; -import org.jclouds.http.functions.ReturnTrueOn404; import org.jclouds.http.options.GetOptions; import org.jclouds.rest.Endpoint; import org.jclouds.rest.EntityParam; @@ -99,12 +97,6 @@ public interface AzureBlobStore extends BlobStore listContainers(); - @GET - @XMLResponseParser(AccountNameEnumerationResultsHandler.class) - @Path("/") - @QueryParams(keys = "comp", values = "list") - BoundedSortedSet listContainers(ListOptions listOptions); - @HEAD @Path("{container}") @ExceptionParser(ReturnFalseOn404.class) @@ -127,13 +119,6 @@ public interface AzureBlobStore extends BlobStore createContainer(@PathParam("container") String container); - @PUT - @Path("{container}") - @QueryParams(keys = "restype", values = "container") - @ExceptionParser(ReturnTrueIfContainerAlreadyExists.class) - Future createContainer(@PathParam("container") String container, - CreateContainerOptions options); - /** * The Delete Container operation marks the specified container for deletion. The container and * any blobs contained within it are later deleted during garbage collection. @@ -149,68 +134,22 @@ public interface AzureBlobStore extends BlobStore deleteContainer(@PathParam("container") String container); - - /** - * The root container is a default container that may be inferred from a URL requesting a blob - * resource. The root container makes it possible to reference a blob from the top level of the - * storage account hierarchy, without referencing the container name. - *

- * The container resource includes metadata and properties for that container. It does not - * include a list of the blobs contained by the container. - * - * @see CreateContainerOptions - * - */ - @PUT - @Path("$root") - @ExceptionParser(ReturnTrueIfContainerAlreadyExists.class) - @QueryParams(keys = "restype", values = "container") - Future createRootContainer(); - - @PUT - @Path("$root") - @ExceptionParser(ReturnTrueIfContainerAlreadyExists.class) - @QueryParams(keys = "restype", values = "container") - Future createRootContainer(CreateOptions options); - - /** - * - * @see deleteContainer(String) - * @see createRootContainer(CreateContainerOptions) - */ - @DELETE - @Path("$root") - @ExceptionParser(ReturnTrueOn404.class) - @QueryParams(keys = "restype", values = "container") - Future deleteRootContainer(); + Future deleteContainer(@PathParam("container") String container); @GET - @XMLResponseParser(ContainerNameEnumerationResultsHandler.class) + @XMLResponseParser(AddMD5ToListBlobsResponse.class) @Path("{container}") @QueryParams(keys = { "restype", "comp" }, values = { "container", "list" }) Future listBlobs(@PathParam("container") String container); - @GET - @XMLResponseParser(ContainerNameEnumerationResultsHandler.class) - @Path("$root") - @QueryParams(keys = { "restype", "comp" }, values = { "container", "list" }) - Future listBlobs(); - - // @GET - // @XMLResponseParser(ContainerNameEnumerationResultsHandler.class) - // @Path("{container}") - // @QueryParams(keys = { "restype", "comp" }, values = { "container", "list" }) - // Future listBlobs(@PathParam("container") String container, - // ListBlobsOptions options); - @PUT @Path("{container}/{key}") @ResponseParser(ParseETagHeader.class) - Future putBlob(@PathParam("container") String container, - @PathParam("key") @ParamParser(BlobKey.class) @EntityParam(BlobBinder.class) Blob object); + Future putBlob( + @PathParam("container") String container, + @PathParam("key") @ParamParser(BlobKey.class) @EntityParam(MD5EnforcingBlobBinder.class) Blob object); @GET @ResponseParser(ParseBlobFromHeadersAndHttpContent.class) @@ -234,8 +173,8 @@ public interface AzureBlobStore extends BlobStore removeBlob(@PathParam("container") String container, @PathParam("key") String key); + Future removeBlob(@PathParam("container") String container, @PathParam("key") String key); } diff --git a/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/AzureBlobUtil.java b/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/AzureBlobUtil.java new file mode 100644 index 0000000000..38d8e686b4 --- /dev/null +++ b/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/AzureBlobUtil.java @@ -0,0 +1,63 @@ +/** + * + * Copyright (C) 2009 Global Cloud Specialists, Inc. + * + * ==================================================================== + * 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.azure.storage.blob; + +import java.net.URI; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; + +import org.jclouds.azure.storage.filters.SharedKeyAuthentication; +import org.jclouds.azure.storage.reference.AzureStorageHeaders; +import org.jclouds.blobstore.functions.ThrowKeyNotFoundOn404; +import org.jclouds.http.functions.ParseContentMD5FromHeaders; +import org.jclouds.rest.Endpoint; +import org.jclouds.rest.ExceptionParser; +import org.jclouds.rest.Headers; +import org.jclouds.rest.RequestFilters; +import org.jclouds.rest.ResponseParser; +import org.jclouds.rest.SkipEncoding; + +/** + * Helper functions needed to to derive BlobStore values + *

+ * + * @see + * @author Adrian Cole + */ +@SkipEncoding('/') +@RequestFilters(SharedKeyAuthentication.class) +@Headers(keys = AzureStorageHeaders.VERSION, values = "2009-07-17") +public interface AzureBlobUtil { + + @GET + @Headers(keys = "Range", values = "bytes=0-0") + // should use HEAD, this is a hack per http://code.google.com/p/jclouds/issues/detail?id=92 + @ResponseParser(ParseContentMD5FromHeaders.class) + @ExceptionParser(ThrowKeyNotFoundOn404.class) + @Path("{key}") + byte[] getMD5(@Endpoint URI container, @PathParam("key") String key); + +} diff --git a/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/binders/BlobBinder.java b/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/binders/BlobBinder.java index b89450d11e..7fa0f4808f 100644 --- a/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/binders/BlobBinder.java +++ b/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/binders/BlobBinder.java @@ -44,7 +44,8 @@ public class BlobBinder extends org.jclouds.blobstore.binders.BlobBinder { public void addEntityToRequest(Object entity, HttpRequest request) { Blob object = (Blob) entity; checkArgument(object.getMetadata().getSize() >= 0, "size must be set"); - + checkArgument(object.getContentLength() <= 64 * 1024 * 1024, + "maximum size for put Blob is 64MB"); if (object.getMetadata() instanceof BlobMetadata) { BlobMetadata md = (BlobMetadata) object.getMetadata(); diff --git a/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/config/AzureBlobContextModule.java b/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/config/AzureBlobContextModule.java index 33f66f185f..605bcef0bd 100644 --- a/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/config/AzureBlobContextModule.java +++ b/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/config/AzureBlobContextModule.java @@ -30,12 +30,14 @@ import javax.inject.Named; import javax.inject.Provider; import org.jclouds.azure.storage.AzureBlob; +import org.jclouds.azure.storage.blob.AzureBlobConnection; import org.jclouds.azure.storage.blob.AzureBlobContext; import org.jclouds.azure.storage.blob.AzureBlobStore; import org.jclouds.azure.storage.blob.domain.Blob; import org.jclouds.azure.storage.blob.domain.BlobMetadata; import org.jclouds.azure.storage.blob.domain.ContainerMetadata; import org.jclouds.azure.storage.reference.AzureStorageConstants; +import org.jclouds.blobstore.BlobStore; import org.jclouds.blobstore.BlobStoreContextImpl; import org.jclouds.blobstore.BlobMap.Factory; import org.jclouds.lifecycle.Closer; @@ -55,16 +57,17 @@ public class AzureBlobContextModule extends AbstractModule { } public static class AzureBlobContextImpl extends - BlobStoreContextImpl implements - AzureBlobContext { + BlobStoreContextImpl + implements AzureBlobContext { @Inject AzureBlobContextImpl(Factory blobMapFactory, org.jclouds.blobstore.InputStreamMap.Factory inputStreamMapFactory, - Closer closer, Provider blobProvider, AzureBlobStore defaultApi, - @AzureBlob URI endPoint, + Closer closer, Provider blobProvider, + BlobStore blobStore, + AzureBlobConnection defaultApi, @AzureBlob URI endPoint, @Named(AzureStorageConstants.PROPERTY_AZURESTORAGE_ACCOUNT) String account) { - super(blobMapFactory, inputStreamMapFactory, closer, blobProvider, defaultApi, endPoint, - account); + super(blobMapFactory, inputStreamMapFactory, closer, blobProvider, blobStore, defaultApi, + endPoint, account); } } diff --git a/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/config/RestAzureBlobStoreModule.java b/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/config/RestAzureBlobStoreModule.java index 6b6b4d6275..2b2b0be6a7 100644 --- a/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/config/RestAzureBlobStoreModule.java +++ b/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/config/RestAzureBlobStoreModule.java @@ -29,11 +29,20 @@ import javax.inject.Named; import javax.inject.Singleton; import org.jclouds.azure.storage.AzureBlob; +import org.jclouds.azure.storage.blob.AzureBlobConnection; import org.jclouds.azure.storage.blob.AzureBlobStore; +import org.jclouds.azure.storage.blob.AzureBlobUtil; +import org.jclouds.azure.storage.blob.domain.Blob; +import org.jclouds.azure.storage.blob.domain.BlobMetadata; +import org.jclouds.azure.storage.blob.domain.ContainerMetadata; +import org.jclouds.azure.storage.blob.handlers.AzureBlobClientErrorRetryHandler; import org.jclouds.azure.storage.blob.reference.AzureBlobConstants; import org.jclouds.azure.storage.config.RestAzureStorageConnectionModule; +import org.jclouds.blobstore.BlobStore; import org.jclouds.cloud.ConfiguresCloudConnection; +import org.jclouds.http.HttpRetryHandler; import org.jclouds.http.RequiresHttp; +import org.jclouds.http.annotation.ClientError; import org.jclouds.rest.RestClientFactory; import com.google.inject.Provides; @@ -62,8 +71,26 @@ public class RestAzureBlobStoreModule extends RestAzureStorageConnectionModule { @Provides @Singleton - protected AzureBlobStore provideAzureBlobStore(RestClientFactory factory) { + protected AzureBlobUtil provideAzureBlobUtil(RestClientFactory factory) { + return factory.create(AzureBlobUtil.class); + } + + @Provides + @Singleton + protected AzureBlobConnection provideAzureBlobConnection(RestClientFactory factory) { + return factory.create(AzureBlobConnection.class); + } + + @Provides + @Singleton + protected BlobStore provideAzureBlobStore( + RestClientFactory factory) { return factory.create(AzureBlobStore.class); } + @Override + protected void bindRetryHandlers() { + bind(HttpRetryHandler.class).annotatedWith(ClientError.class).to( + AzureBlobClientErrorRetryHandler.class); + } } \ No newline at end of file diff --git a/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/domain/ContainerMetadata.java b/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/domain/ContainerMetadata.java index 31b9a8219f..56cc8f65fc 100755 --- a/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/domain/ContainerMetadata.java +++ b/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/domain/ContainerMetadata.java @@ -28,6 +28,9 @@ import java.util.Arrays; import org.joda.time.DateTime; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimap; + /** * * @author Adrian Cole @@ -37,21 +40,22 @@ public class ContainerMetadata extends org.jclouds.blobstore.domain.ContainerMet private URI url; private DateTime lastModified; private byte[] eTag; + private Multimap userMetadata = HashMultimap.create(); @Override public int hashCode() { final int prime = 31; int result = super.hashCode(); result = prime * result + Arrays.hashCode(eTag); - result = prime * result + ((lastModified == null) ? 0 : lastModified.hashCode()); - result = prime * result + ((url == null) ? 0 : url.hashCode()); + result = prime * result + ((getLastModified() == null) ? 0 : getLastModified().hashCode()); + result = prime * result + ((getUrl() == null) ? 0 : getUrl().hashCode()); return result; } @Override public String toString() { - return "ContainerMetadata [eTag=" + Arrays.toString(eTag) + ", lastModified=" + lastModified - + ", url=" + url + ", name=" + name + "]"; + return "ContainerMetadata [eTag=" + Arrays.toString(eTag) + ", lastModified=" + + getLastModified() + ", url=" + getUrl() + ", name=" + name + "]"; } @Override @@ -65,22 +69,22 @@ public class ContainerMetadata extends org.jclouds.blobstore.domain.ContainerMet ContainerMetadata other = (ContainerMetadata) obj; if (!Arrays.equals(eTag, other.eTag)) return false; - if (lastModified == null) { - if (other.lastModified != null) + if (getLastModified() == null) { + if (other.getLastModified() != null) return false; - } else if (!lastModified.equals(other.lastModified)) + } else if (!getLastModified().equals(other.getLastModified())) return false; - if (url == null) { - if (other.url != null) + if (getUrl() == null) { + if (other.getUrl() != null) return false; - } else if (!url.equals(other.url)) + } else if (!getUrl().equals(other.getUrl())) return false; return true; } public ContainerMetadata(URI url, DateTime lastModified, byte[] eTag) { super(url.getPath().substring(1)); - this.url = url; + this.setUrl(url); this.lastModified = lastModified; this.eTag = eTag; } @@ -105,4 +109,24 @@ public class ContainerMetadata extends org.jclouds.blobstore.domain.ContainerMet return eTag; } + public void setUserMetadata(Multimap userMetadata) { + this.userMetadata = userMetadata; + } + + public Multimap getUserMetadata() { + return userMetadata; + } + + public void setLastModified(DateTime lastModified) { + this.lastModified = lastModified; + } + + public void setETag(byte[] eTag) { + this.eTag = eTag; + } + + public void setUrl(URI url) { + this.url = url; + } + } diff --git a/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/functions/MD5EnforcingBlobBinder.java b/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/functions/MD5EnforcingBlobBinder.java new file mode 100644 index 0000000000..117c7f1fc2 --- /dev/null +++ b/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/functions/MD5EnforcingBlobBinder.java @@ -0,0 +1,55 @@ +/** + * + * Copyright (C) 2009 Global Cloud Specialists, Inc. + * + * ==================================================================== + * 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.azure.storage.blob.functions; + +import static org.jclouds.blobstore.reference.BlobStoreConstants.PROPERTY_USER_METADATA_PREFIX; + +import java.io.IOException; + +import javax.inject.Inject; +import javax.inject.Named; + +import org.jclouds.azure.storage.blob.binders.BlobBinder; +import org.jclouds.blobstore.domain.Blob; +import org.jclouds.http.HttpRequest; + +public class MD5EnforcingBlobBinder extends BlobBinder { + + @Inject + public MD5EnforcingBlobBinder(@Named(PROPERTY_USER_METADATA_PREFIX) String metadataPrefix) { + super(metadataPrefix); + } + + public void addEntityToRequest(Object entity, HttpRequest request) { + Blob object = (Blob) entity; + if (object.getMetadata().getContentMD5() == null) { + try { + object.generateMD5(); + } catch (IOException e) { + throw new RuntimeException("Could not generate MD5 for " + object.getKey(), e); + } + } + super.addEntityToRequest(entity, request); + } +} diff --git a/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/functions/ParseContainerMetadataFromHeaders.java b/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/functions/ParseContainerMetadataFromHeaders.java new file mode 100644 index 0000000000..0d232a5345 --- /dev/null +++ b/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/functions/ParseContainerMetadataFromHeaders.java @@ -0,0 +1,120 @@ +/** + * + * Copyright (C) 2009 Global Cloud Specialists, Inc. + * + * ==================================================================== + * 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.azure.storage.blob.functions; + +import static org.jclouds.blobstore.reference.BlobStoreConstants.PROPERTY_USER_METADATA_PREFIX; + +import java.util.Map.Entry; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.ws.rs.core.HttpHeaders; + +import org.jclouds.azure.storage.blob.domain.ContainerMetadata; +import org.jclouds.http.HttpException; +import org.jclouds.http.HttpRequest; +import org.jclouds.http.HttpResponse; +import org.jclouds.http.HttpUtils; +import org.jclouds.rest.RestContext; +import org.jclouds.util.DateService; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Function; + +/** + * This parses @{link {@link org.jclouds.azure.storage.blob.domain.ContainerMetadata} from HTTP + * headers. + * + * + * @author Adrian Cole + */ +public class ParseContainerMetadataFromHeaders implements + Function, RestContext { + + private final DateService dateParser; + private final String metadataPrefix; + private HttpRequest request; + private Object[] args; + + @Inject + public ParseContainerMetadataFromHeaders(DateService dateParser, + @Named(PROPERTY_USER_METADATA_PREFIX) String metadataPrefix) { + this.dateParser = dateParser; + this.metadataPrefix = metadataPrefix; + } + + public ContainerMetadata apply(HttpResponse from) { + ContainerMetadata to = new ContainerMetadata(getArgs()[0].toString()); + addUserMetadataTo(from, to); + parseLastModifiedOrThrowException(from, to); + addETagTo(from, to); + to.setUrl(getRequest().getEndpoint()); + return to; + } + + @VisibleForTesting + void addUserMetadataTo(HttpResponse from, ContainerMetadata metadata) { + for (Entry header : from.getHeaders().entries()) { + if (header.getKey() != null && header.getKey().startsWith(metadataPrefix)) + metadata.getUserMetadata().put( + (header.getKey().substring(metadataPrefix.length())).toLowerCase(), + header.getValue()); + } + } + + @VisibleForTesting + void parseLastModifiedOrThrowException(HttpResponse from, ContainerMetadata metadata) + throws HttpException { + String lastModified = from.getFirstHeaderOrNull(HttpHeaders.LAST_MODIFIED); + if (lastModified == null) + throw new HttpException(HttpHeaders.LAST_MODIFIED + " header not present in response: " + + from); + metadata.setLastModified(dateParser.rfc822DateParse(lastModified)); + if (metadata.getLastModified() == null) + throw new HttpException("could not parse: " + HttpHeaders.LAST_MODIFIED + ": " + + lastModified); + } + + @VisibleForTesting + protected void addETagTo(HttpResponse from, ContainerMetadata metadata) { + String eTag = from.getFirstHeaderOrNull(HttpHeaders.ETAG); + if (metadata.getETag() == null && eTag != null) { + metadata.setETag(HttpUtils.fromHexString(eTag.replaceAll("\"", ""))); + } + } + + public Object[] getArgs() { + return args; + } + + public HttpRequest getRequest() { + return request; + } + + public void setContext(HttpRequest request, Object[] args) { + this.request = request; + this.args = args; + } + +} \ No newline at end of file diff --git a/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/handlers/AzureBlobClientErrorRetryHandler.java b/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/handlers/AzureBlobClientErrorRetryHandler.java new file mode 100644 index 0000000000..fc3d7e2174 --- /dev/null +++ b/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/handlers/AzureBlobClientErrorRetryHandler.java @@ -0,0 +1,92 @@ +/** + * + * Copyright (C) 2009 Global Cloud Specialists, Inc. + * + * ==================================================================== + * 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.azure.storage.blob.handlers; + +import javax.annotation.Resource; +import javax.inject.Inject; +import javax.inject.Named; + +import org.jclouds.azure.storage.domain.AzureStorageError; +import org.jclouds.azure.storage.util.AzureStorageUtils; +import org.jclouds.http.HttpCommand; +import org.jclouds.http.HttpConstants; +import org.jclouds.http.HttpException; +import org.jclouds.http.HttpResponse; +import org.jclouds.http.HttpRetryHandler; +import org.jclouds.http.handlers.BackoffLimitedRetryHandler; +import org.jclouds.logging.Logger; +import org.jclouds.util.Utils; + +/** + * Handles Retryable responses with error codes in the 4xx range + * + * @author Adrian Cole + */ +public class AzureBlobClientErrorRetryHandler implements HttpRetryHandler { + + @Inject(optional = true) + @Named(HttpConstants.PROPERTY_HTTP_MAX_RETRIES) + private int retryCountLimit = 5; + + private final AzureStorageUtils utils; + private final BackoffLimitedRetryHandler backoffHandler; + + @Resource + protected Logger logger = Logger.NULL; + + @Inject + public AzureBlobClientErrorRetryHandler(BackoffLimitedRetryHandler backoffHandler, + AzureStorageUtils utils) { + this.backoffHandler = backoffHandler; + this.utils = utils; + } + + public boolean shouldRetryRequest(HttpCommand command, HttpResponse response) { + byte[] content = Utils.closeConnectionButKeepContentStream(response); + command.incrementFailureCount(); + if (!command.isReplayable()) { + logger.warn("Cannot retry after server error, command is not replayable: %1$s", command); + return false; + } else if (command.getFailureCount() > retryCountLimit) { + logger.warn( + "Cannot retry after server error, command has exceeded retry limit %1$d: %2$s", + retryCountLimit, command); + return false; + } else if (response.getStatusCode() == 409) { + try { + AzureStorageError error = utils.parseAzureStorageErrorFromContent(command, response, + new String(content)); + if ("ContainerBeingDeleted".equals(error.getCode())) { + backoffHandler.imposeBackoffExponentialDelay(100L, 3, command.getFailureCount(), + command.toString()); + return true; + } + } catch (HttpException e) { + logger.warn(e, "error parsing response: %s", new String(content)); + } + } + return false; + } + +} diff --git a/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/xml/AddMD5ToListBlobsResponse.java b/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/xml/AddMD5ToListBlobsResponse.java new file mode 100644 index 0000000000..3f8c023e68 --- /dev/null +++ b/azure/storage/blob/core/src/main/java/org/jclouds/azure/storage/blob/xml/AddMD5ToListBlobsResponse.java @@ -0,0 +1,62 @@ +/** + * + * Copyright (C) 2009 Global Cloud Specialists, Inc. + * + * ==================================================================== + * 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.azure.storage.blob.xml; + +import static com.google.common.base.Preconditions.checkNotNull; + +import javax.inject.Inject; + +import org.jclouds.azure.storage.blob.AzureBlobUtil; +import org.jclouds.azure.storage.blob.domain.BlobMetadata; +import org.jclouds.azure.storage.blob.domain.ListBlobsResponse; +import org.jclouds.util.DateService; + +/** + * adds the Content-MD5 value to the blob metadata. + * + * @see + * @author Adrian Cole + */ +public class AddMD5ToListBlobsResponse extends ContainerNameEnumerationResultsHandler { + + private final AzureBlobUtil util; + + @Inject + public AddMD5ToListBlobsResponse(AzureBlobUtil util, DateService dateParser) { + super(dateParser); + this.util = util; + } + + @Override + public ListBlobsResponse getResult() { + ListBlobsResponse response = super.getResult(); + checkNotNull(response.getContainerUrl(), "containerUrl"); + for (BlobMetadata md : response) { + checkNotNull(md.getKey(), "key"); + md.setContentMD5(util.getMD5(response.getContainerUrl(), md.getKey())); + } + return response; + } + +} diff --git a/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/AzureBlobStoreLiveTest.java b/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/AzureBlobConnectionLiveTest.java similarity index 94% rename from azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/AzureBlobStoreLiveTest.java rename to azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/AzureBlobConnectionLiveTest.java index 0d208865c1..d22c51c10f 100644 --- a/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/AzureBlobStoreLiveTest.java +++ b/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/AzureBlobConnectionLiveTest.java @@ -61,10 +61,10 @@ import com.google.common.collect.Iterables; * * @author Adrian Cole */ -@Test(groups = "live", sequential = true, testName = "cloudservers.AzureBlobStoreLiveTest") -public class AzureBlobStoreLiveTest { +@Test(groups = "live", sequential = true, testName = "azureblob.AzureBlobConnectionLiveTest") +public class AzureBlobConnectionLiveTest { - protected AzureBlobStore connection; + protected AzureBlobConnection connection; private String containerPrefix = System.getProperty("user.name") + "-azureblob"; @@ -215,14 +215,14 @@ public class AzureBlobStoreLiveTest { @Test public void testDeleteOneContainer() throws Exception { - assertTrue(connection.deleteContainer("does-not-exist").get(10, TimeUnit.SECONDS)); + connection.deleteContainer("does-not-exist").get(10, TimeUnit.SECONDS); } @Test(timeOut = 5 * 60 * 1000, dependsOnMethods = { "testListOwnedContainers", "testObjectOperations" }) public void testDeleteContainer() throws Exception { - assert connection.deleteContainer(privateContainer).get(10, TimeUnit.SECONDS); - assert connection.deleteContainer(publicContainer).get(10, TimeUnit.SECONDS); + connection.deleteContainer(privateContainer).get(10, TimeUnit.SECONDS); + connection.deleteContainer(publicContainer).get(10, TimeUnit.SECONDS); // TODO loop for up to 30 seconds checking if they are really gone } @@ -245,14 +245,14 @@ public class AzureBlobStoreLiveTest { // Test HEAD of missing object try { - connection.blobMetadata(privateContainer, "non-existent-object"); + connection.getBlobProperties(privateContainer, "non-existent-object"); assert false; } catch (Exception e) { e.printStackTrace(); } // Test HEAD of object - BlobMetadata metadata = connection.blobMetadata(privateContainer, object.getKey()); + BlobMetadata metadata = connection.getBlobProperties(privateContainer, object.getKey()); // TODO assertEquals(metadata.getKey(), object.getKey()); // we can't check this while hacking around lack of content-md5, as GET of the first byte will // show incorrect length 1, the returned size, as opposed to the real length. This is an ok @@ -344,9 +344,8 @@ public class AzureBlobStoreLiveTest { // TimeUnit.SECONDS); // assertEquals(IOUtils.toString((InputStream) getBlob.getData()), data.substring(8)); - assertTrue(connection.removeBlob(privateContainer, "object").get(10, TimeUnit.SECONDS)); - assertTrue(connection.removeBlob(privateContainer, "chunked-object") - .get(10, TimeUnit.SECONDS)); + connection.deleteBlob(privateContainer, "object").get(10, TimeUnit.SECONDS); + connection.deleteBlob(privateContainer, "chunked-object").get(10, TimeUnit.SECONDS); } } diff --git a/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/AzureBlobConnectionTest.java b/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/AzureBlobConnectionTest.java new file mode 100644 index 0000000000..df4835d5f6 --- /dev/null +++ b/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/AzureBlobConnectionTest.java @@ -0,0 +1,357 @@ +/** + * + * Copyright (C) 2009 Global Cloud Specialists, Inc. + * + * ==================================================================== + * 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.azure.storage.blob; + +import static org.jclouds.azure.storage.blob.options.CreateContainerOptions.Builder.withPublicAcl; +import static org.jclouds.azure.storage.options.ListOptions.Builder.maxResults; +import static org.testng.Assert.assertEquals; + +import java.lang.reflect.Array; +import java.lang.reflect.Method; +import java.net.URI; +import java.util.Collections; + +import javax.ws.rs.HttpMethod; + +import org.jclouds.azure.storage.AzureBlob; +import org.jclouds.azure.storage.blob.functions.ParseContainerMetadataFromHeaders; +import org.jclouds.azure.storage.blob.functions.ReturnTrueIfContainerAlreadyExists; +import org.jclouds.azure.storage.blob.options.CreateContainerOptions; +import org.jclouds.azure.storage.blob.options.ListBlobsOptions; +import org.jclouds.azure.storage.options.ListOptions; +import org.jclouds.azure.storage.reference.AzureStorageConstants; +import org.jclouds.blobstore.functions.ReturnVoidOnNotFoundOr404; +import org.jclouds.blobstore.reference.BlobStoreConstants; +import org.jclouds.concurrent.WithinThreadExecutorService; +import org.jclouds.concurrent.config.ExecutorServiceModule; +import org.jclouds.http.HttpRequest; +import org.jclouds.http.HttpUtils; +import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule; +import org.jclouds.http.functions.ParseSax; +import org.jclouds.http.functions.ReturnTrueIf2xx; +import org.jclouds.http.functions.ReturnTrueOn404; +import org.jclouds.http.functions.ReturnVoidIf2xx; +import org.jclouds.rest.JaxrsAnnotationProcessor; +import org.jclouds.rest.config.JaxrsModule; +import org.jclouds.util.Jsr330; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableMultimap; +import com.google.common.collect.Multimap; +import com.google.inject.AbstractModule; +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.google.inject.Key; +import com.google.inject.TypeLiteral; + +/** + * Tests behavior of {@code AzureBlobConnection} + * + * @author Adrian Cole + */ +@Test(groups = "unit", testName = "azureblob.AzureBlobConnectionTest") +public class AzureBlobConnectionTest { + + public void testListContainers() throws SecurityException, NoSuchMethodException { + Method method = AzureBlobConnection.class.getMethod("listContainers", Array.newInstance( + ListOptions.class, 0).getClass()); + + HttpRequest httpMethod = processor.createRequest(method, new Object[] {}); + assertEquals(httpMethod.getEndpoint().getHost(), "myaccount.blob.core.windows.net"); + assertEquals(httpMethod.getEndpoint().getPath(), "/"); + assertEquals(httpMethod.getEndpoint().getQuery(), "comp=list"); + assertEquals(httpMethod.getMethod(), HttpMethod.GET); + assertEquals(httpMethod.getHeaders().size(), 1); + assertEquals(httpMethod.getHeaders().get("x-ms-version"), Collections + .singletonList("2009-07-17")); + assertEquals(processor.createResponseParser(method, httpMethod, null).getClass(), + ParseSax.class); + // TODO check generic type of response parser + assertEquals(processor.createExceptionParserOrNullIfNotFound(method), null); + } + + public void testListContainersOptions() throws SecurityException, NoSuchMethodException { + Method method = AzureBlobConnection.class.getMethod("listContainers", Array.newInstance( + ListOptions.class, 0).getClass()); + + HttpRequest httpMethod = processor.createRequest(method, new Object[] { maxResults(1).marker( + "marker").prefix("prefix") }); + assertEquals(httpMethod.getEndpoint().getHost(), "myaccount.blob.core.windows.net"); + assertEquals(httpMethod.getEndpoint().getPath(), "/"); + assert httpMethod.getEndpoint().getQuery().contains("comp=list"); + assert httpMethod.getEndpoint().getQuery().contains("marker=marker"); + assert httpMethod.getEndpoint().getQuery().contains("maxresults=1"); + assert httpMethod.getEndpoint().getQuery().contains("prefix=prefix"); + assertEquals(httpMethod.getMethod(), HttpMethod.GET); + assertEquals(httpMethod.getHeaders().size(), 1); + assertEquals(httpMethod.getHeaders().get("x-ms-version"), Collections + .singletonList("2009-07-17")); + assertEquals(processor.createResponseParser(method, httpMethod, null).getClass(), + ParseSax.class); + // TODO check generic type of response parser + assertEquals(processor.createExceptionParserOrNullIfNotFound(method), null); + } + + public void testCreateContainer() throws SecurityException, NoSuchMethodException { + Method method = AzureBlobConnection.class.getMethod("createContainer", String.class, Array + .newInstance(CreateContainerOptions.class, 0).getClass()); + + HttpRequest httpMethod = processor.createRequest(method, new Object[] { "container" }); + assertEquals(httpMethod.getEndpoint().getHost(), "myaccount.blob.core.windows.net"); + assertEquals(httpMethod.getEndpoint().getPath(), "/container"); + assertEquals(httpMethod.getEndpoint().getQuery(), "restype=container"); + assertEquals(httpMethod.getMethod(), HttpMethod.PUT); + assertEquals(httpMethod.getHeaders().size(), 2); + assertEquals(httpMethod.getHeaders().get("x-ms-version"), Collections + .singletonList("2009-07-17")); + assertEquals(httpMethod.getHeaders().get("Content-Length"), Collections.singletonList("0")); + assertEquals(processor.createResponseParser(method, httpMethod, null).getClass(), + ReturnTrueIf2xx.class); + // TODO check generic type of response parser + assertEquals(processor.createExceptionParserOrNullIfNotFound(method).getClass(), + ReturnTrueIfContainerAlreadyExists.class); + } + + public void testDeleteContainer() throws SecurityException, NoSuchMethodException { + Method method = AzureBlobConnection.class.getMethod("deleteContainer", String.class); + + HttpRequest httpMethod = processor.createRequest(method, new Object[] { "container" }); + assertEquals(httpMethod.getEndpoint().getHost(), "myaccount.blob.core.windows.net"); + assertEquals(httpMethod.getEndpoint().getPath(), "/container"); + assertEquals(httpMethod.getEndpoint().getQuery(), "restype=container"); + assertEquals(httpMethod.getMethod(), HttpMethod.DELETE); + assertEquals(httpMethod.getHeaders().size(), 1); + assertEquals(httpMethod.getHeaders().get("x-ms-version"), Collections + .singletonList("2009-07-17")); + assertEquals(processor.createResponseParser(method, httpMethod, null).getClass(), + ReturnVoidIf2xx.class); + // TODO check generic type of response parser + assertEquals(processor.createExceptionParserOrNullIfNotFound(method).getClass(), + ReturnVoidOnNotFoundOr404.class); + } + + public void testCreateContainerOptions() throws SecurityException, NoSuchMethodException { + Method method = AzureBlobConnection.class.getMethod("createContainer", String.class, Array + .newInstance(CreateContainerOptions.class, 0).getClass()); + + HttpRequest httpMethod = processor.createRequest(method, new Object[] { "container", + withPublicAcl().withMetadata(ImmutableMultimap.of("foo", "bar")) }); + assertEquals(httpMethod.getEndpoint().getHost(), "myaccount.blob.core.windows.net"); + assertEquals(httpMethod.getEndpoint().getPath(), "/container"); + assertEquals(httpMethod.getEndpoint().getQuery(), "restype=container"); + assertEquals(httpMethod.getMethod(), HttpMethod.PUT); + assertEquals(httpMethod.getHeaders().size(), 4); + assertEquals(httpMethod.getHeaders().get("x-ms-version"), Collections + .singletonList("2009-07-17")); + assertEquals(httpMethod.getHeaders().get("x-ms-meta-foo"), Collections.singletonList("bar")); + assertEquals(httpMethod.getHeaders().get("x-ms-prop-publicaccess"), Collections + .singletonList("true")); + assertEquals(httpMethod.getHeaders().get("Content-Length"), Collections.singletonList("0")); + assertEquals(processor.createResponseParser(method, httpMethod, null).getClass(), + ReturnTrueIf2xx.class); + // TODO check generic type of response parser + assertEquals(processor.createExceptionParserOrNullIfNotFound(method).getClass(), + ReturnTrueIfContainerAlreadyExists.class); + } + + public void testCreateRootContainer() throws SecurityException, NoSuchMethodException { + Method method = AzureBlobConnection.class.getMethod("createRootContainer", Array.newInstance( + CreateContainerOptions.class, 0).getClass()); + + HttpRequest httpMethod = processor.createRequest(method, new Object[] {}); + assertEquals(httpMethod.getEndpoint().getHost(), "myaccount.blob.core.windows.net"); + assertEquals(httpMethod.getEndpoint().getPath(), "/$root"); + assertEquals(httpMethod.getEndpoint().getQuery(), "restype=container"); + assertEquals(httpMethod.getMethod(), HttpMethod.PUT); + assertEquals(httpMethod.getHeaders().size(), 2); + assertEquals(httpMethod.getHeaders().get("x-ms-version"), Collections + .singletonList("2009-07-17")); + assertEquals(httpMethod.getHeaders().get("Content-Length"), Collections.singletonList("0")); + assertEquals(processor.createResponseParser(method, httpMethod, null).getClass(), + ReturnTrueIf2xx.class); + // TODO check generic type of response parser + assertEquals(processor.createExceptionParserOrNullIfNotFound(method).getClass(), + ReturnTrueIfContainerAlreadyExists.class); + } + + public void testDeleteRootContainer() throws SecurityException, NoSuchMethodException { + Method method = AzureBlobConnection.class.getMethod("deleteRootContainer"); + + HttpRequest httpMethod = processor.createRequest(method, new Object[] {}); + assertEquals(httpMethod.getEndpoint().getHost(), "myaccount.blob.core.windows.net"); + assertEquals(httpMethod.getEndpoint().getPath(), "/$root"); + assertEquals(httpMethod.getEndpoint().getQuery(), "restype=container"); + assertEquals(httpMethod.getMethod(), HttpMethod.DELETE); + assertEquals(httpMethod.getHeaders().size(), 1); + assertEquals(httpMethod.getHeaders().get("x-ms-version"), Collections + .singletonList("2009-07-17")); + assertEquals(processor.createResponseParser(method, httpMethod, null).getClass(), + ReturnTrueIf2xx.class); + // TODO check generic type of response parser + assertEquals(processor.createExceptionParserOrNullIfNotFound(method).getClass(), + ReturnTrueOn404.class); + } + + public void testCreateRootContainerOptions() throws SecurityException, NoSuchMethodException { + Method method = AzureBlobConnection.class.getMethod("createRootContainer", Array.newInstance( + CreateContainerOptions.class, 0).getClass()); + + HttpRequest httpMethod = processor.createRequest(method, new Object[] { withPublicAcl() + .withMetadata(ImmutableMultimap.of("foo", "bar")) }); + assertEquals(httpMethod.getEndpoint().getHost(), "myaccount.blob.core.windows.net"); + assertEquals(httpMethod.getEndpoint().getPath(), "/$root"); + assertEquals(httpMethod.getEndpoint().getQuery(), "restype=container"); + assertEquals(httpMethod.getMethod(), HttpMethod.PUT); + assertEquals(httpMethod.getHeaders().size(), 4); + assertEquals(httpMethod.getHeaders().get("x-ms-version"), Collections + .singletonList("2009-07-17")); + assertEquals(httpMethod.getHeaders().get("x-ms-meta-foo"), Collections.singletonList("bar")); + assertEquals(httpMethod.getHeaders().get("x-ms-prop-publicaccess"), Collections + .singletonList("true")); + assertEquals(httpMethod.getHeaders().get("Content-Length"), Collections.singletonList("0")); + assertEquals(processor.createResponseParser(method, httpMethod, null).getClass(), + ReturnTrueIf2xx.class); + // TODO check generic type of response parser + assertEquals(processor.createExceptionParserOrNullIfNotFound(method).getClass(), + ReturnTrueIfContainerAlreadyExists.class); + } + + public void testListBlobs() throws SecurityException, NoSuchMethodException { + Method method = AzureBlobConnection.class.getMethod("listBlobs", String.class, Array + .newInstance(ListBlobsOptions.class, 0).getClass()); + + HttpRequest httpMethod = processor.createRequest(method, new Object[] { "container" }); + assertEquals(httpMethod.getEndpoint().getHost(), "myaccount.blob.core.windows.net"); + assertEquals(httpMethod.getEndpoint().getPath(), "/container"); + assertEquals(httpMethod.getEndpoint().getQuery(), "restype=container&comp=list"); + assertEquals(httpMethod.getMethod(), HttpMethod.GET); + assertEquals(httpMethod.getHeaders().size(), 1); + assertEquals(httpMethod.getHeaders().get("x-ms-version"), Collections + .singletonList("2009-07-17")); + assertEquals(processor.createResponseParser(method, httpMethod, null).getClass(), + ParseSax.class); + // TODO check generic type of response parser + assertEquals(processor.createExceptionParserOrNullIfNotFound(method), null); + } + + public void testListRootBlobs() throws SecurityException, NoSuchMethodException { + Method method = AzureBlobConnection.class.getMethod("listBlobs", Array.newInstance( + ListBlobsOptions.class, 0).getClass()); + + HttpRequest httpMethod = processor.createRequest(method, new Object[] {}); + assertEquals(httpMethod.getEndpoint().getHost(), "myaccount.blob.core.windows.net"); + assertEquals(httpMethod.getEndpoint().getPath(), "/$root"); + assertEquals(httpMethod.getEndpoint().getQuery(), "restype=container&comp=list"); + assertEquals(httpMethod.getMethod(), HttpMethod.GET); + assertEquals(httpMethod.getHeaders().size(), 1); + assertEquals(httpMethod.getHeaders().get("x-ms-version"), Collections + .singletonList("2009-07-17")); + assertEquals(processor.createResponseParser(method, httpMethod, null).getClass(), + ParseSax.class); + // TODO check generic type of response parser + assertEquals(processor.createExceptionParserOrNullIfNotFound(method), null); + } + + public void testContainerProperties() throws SecurityException, NoSuchMethodException { + Method method = AzureBlobConnection.class.getMethod("getContainerProperties", String.class); + + HttpRequest httpMethod = processor.createRequest(method, new Object[] { "container" }); + assertEquals(httpMethod.getEndpoint().getHost(), "myaccount.blob.core.windows.net"); + assertEquals(httpMethod.getEndpoint().getPath(), "/container"); + assertEquals(httpMethod.getEndpoint().getQuery(), "restype=container"); + assertEquals(httpMethod.getMethod(), HttpMethod.HEAD); + assertEquals(httpMethod.getHeaders().size(), 1); + assertEquals(httpMethod.getHeaders().get("x-ms-version"), Collections + .singletonList("2009-07-17")); + assertEquals(processor.createResponseParser(method, httpMethod, null).getClass(), + ParseContainerMetadataFromHeaders.class); + assertEquals(processor.createExceptionParserOrNullIfNotFound(method), null); + } + + public void testSetContainerMetadata() throws SecurityException, NoSuchMethodException { + Method method = AzureBlobConnection.class.getMethod("setContainerMetadata", String.class, + Multimap.class); + + HttpRequest httpMethod = processor.createRequest(method, new Object[] { "container", + ImmutableMultimap.of("key", "value") }); + assertEquals(httpMethod.getEndpoint().getHost(), "myaccount.blob.core.windows.net"); + assertEquals(httpMethod.getEndpoint().getPath(), "/container"); + assertEquals(httpMethod.getEndpoint().getQuery(), "restype=container&comp=metadata"); + assertEquals(httpMethod.getMethod(), HttpMethod.PUT); + assertEquals(httpMethod.getHeaders().size(), 2); + assertEquals(httpMethod.getHeaders().get("x-ms-version"), Collections + .singletonList("2009-07-17")); + assertEquals(httpMethod.getHeaders().get("x-ms-meta-key"), Collections.singletonList("value")); + + assertEquals(processor.createResponseParser(method, httpMethod, null).getClass(), + ReturnVoidIf2xx.class); + assertEquals(processor.createExceptionParserOrNullIfNotFound(method), null); + } + + public void testSetBlobMetadata() throws SecurityException, NoSuchMethodException { + Method method = AzureBlobConnection.class.getMethod("setBlobMetadata", String.class, + String.class, Multimap.class); + HttpRequest httpMethod = processor.createRequest(method, new Object[] { "container", "blob", + ImmutableMultimap.of("key", "value") }); + assertEquals(httpMethod.getEndpoint().getHost(), "myaccount.blob.core.windows.net"); + assertEquals(httpMethod.getEndpoint().getPath(), "/container/blob"); + assertEquals(httpMethod.getEndpoint().getQuery(), "comp=metadata"); + assertEquals(httpMethod.getMethod(), HttpMethod.PUT); + assertEquals(httpMethod.getHeaders().size(), 2); + assertEquals(httpMethod.getHeaders().get("x-ms-version"), Collections + .singletonList("2009-07-17")); + assertEquals(httpMethod.getHeaders().get("x-ms-meta-key"), Collections.singletonList("value")); + + assertEquals(processor.createResponseParser(method, httpMethod, null).getClass(), + ReturnVoidIf2xx.class); + assertEquals(processor.createExceptionParserOrNullIfNotFound(method), null); + } + + @BeforeClass + void setupFactory() { + Injector injector = Guice.createInjector(new AbstractModule() { + @Override + protected void configure() { + bind(URI.class).annotatedWith(AzureBlob.class).toInstance( + URI.create("http://myaccount.blob.core.windows.net")); + bindConstant().annotatedWith( + Jsr330.named(AzureStorageConstants.PROPERTY_AZURESTORAGE_ACCOUNT)).to( + "myaccount"); + bindConstant().annotatedWith( + Jsr330.named(AzureStorageConstants.PROPERTY_AZURESTORAGE_KEY)).to( + HttpUtils.toBase64String("key".getBytes())); + bindConstant().annotatedWith( + Jsr330.named(BlobStoreConstants.PROPERTY_USER_METADATA_PREFIX)).to( + "x-ms-meta-"); + } + }, new JaxrsModule(), new ExecutorServiceModule(new WithinThreadExecutorService()), + new JavaUrlHttpCommandExecutorServiceModule()); + processor = injector.getInstance(Key + .get(new TypeLiteral>() { + })); + } + + JaxrsAnnotationProcessor processor; +} diff --git a/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/AzureBlobContextBuilderTest.java b/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/AzureBlobContextBuilderTest.java index 092b068c13..aabc0969b0 100644 --- a/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/AzureBlobContextBuilderTest.java +++ b/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/AzureBlobContextBuilderTest.java @@ -34,8 +34,10 @@ import org.jclouds.azure.storage.blob.config.AzureBlobContextModule; import org.jclouds.azure.storage.blob.config.RestAzureBlobStoreModule; import org.jclouds.azure.storage.blob.config.StubAzureBlobStoreModule; import org.jclouds.azure.storage.blob.config.AzureBlobContextModule.AzureBlobContextImpl; +import org.jclouds.azure.storage.blob.internal.StubAzureBlobConnection; import org.jclouds.azure.storage.reference.AzureStorageConstants; -import org.jclouds.cloud.CloudContext; +import org.jclouds.blobstore.integration.internal.StubBlobStore; +import org.jclouds.http.HttpUtils; import org.testng.annotations.Test; import com.google.inject.Injector; @@ -60,22 +62,26 @@ public class AzureBlobContextBuilderTest { } public void testBuildContext() { - CloudContext context = new AzureBlobContextBuilder("id", "secret") - .withModules(new StubAzureBlobStoreModule()).buildContext(); + AzureBlobContext context = new AzureBlobContextBuilder("id", HttpUtils + .toBase64String("secret".getBytes())).withModules(new StubAzureBlobStoreModule()) + .buildContext(); assertEquals(context.getClass(), AzureBlobContextImpl.class); + assertEquals(context.getApi().getClass(), StubAzureBlobConnection.class); + assertEquals(context.getBlobStore().getClass(), StubBlobStore.class); assertEquals(context.getAccount(), "id"); assertEquals(context.getEndPoint(), URI.create("https://id.blob.core.windows.net")); } public void testBuildInjector() { - Injector i = new AzureBlobContextBuilder("id", "secret").withModules( - new StubAzureBlobStoreModule()).buildInjector(); + Injector i = new AzureBlobContextBuilder("id", HttpUtils.toBase64String("secret".getBytes())) + .withModules(new StubAzureBlobStoreModule()).buildInjector(); assert i.getInstance(AzureBlobContext.class) != null; } protected void testAddContextModule() { List modules = new ArrayList(); - AzureBlobContextBuilder builder = new AzureBlobContextBuilder("id", "secret"); + AzureBlobContextBuilder builder = new AzureBlobContextBuilder("id", HttpUtils + .toBase64String("secret".getBytes())); builder.addContextModule(modules); assertEquals(modules.size(), 1); assertEquals(modules.get(0).getClass(), AzureBlobContextModule.class); @@ -83,7 +89,8 @@ public class AzureBlobContextBuilderTest { protected void addConnectionModule() { List modules = new ArrayList(); - AzureBlobContextBuilder builder = new AzureBlobContextBuilder("id", "secret"); + AzureBlobContextBuilder builder = new AzureBlobContextBuilder("id", HttpUtils + .toBase64String("secret".getBytes())); builder.addConnectionModule(modules); assertEquals(modules.size(), 1); assertEquals(modules.get(0).getClass(), RestAzureBlobStoreModule.class); diff --git a/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/AzureBlobContextModuleTest.java b/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/AzureBlobContextModuleTest.java new file mode 100644 index 0000000000..fb9a374ef1 --- /dev/null +++ b/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/AzureBlobContextModuleTest.java @@ -0,0 +1,76 @@ +/** + * + * Copyright (C) 2009 Global Cloud Specialists, Inc. + * + * ==================================================================== + * 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.azure.storage.blob; + +import static org.testng.Assert.assertEquals; + +import org.jclouds.azure.storage.blob.config.AzureBlobContextModule; +import org.jclouds.azure.storage.blob.config.StubAzureBlobStoreModule; +import org.jclouds.azure.storage.blob.config.AzureBlobContextModule.AzureBlobContextImpl; +import org.jclouds.azure.storage.blob.domain.Blob; +import org.jclouds.azure.storage.blob.domain.BlobMetadata; +import org.jclouds.azure.storage.blob.domain.ContainerMetadata; +import org.jclouds.azure.storage.blob.reference.AzureBlobConstants; +import org.jclouds.blobstore.BlobStoreMapsModule; +import org.jclouds.util.Jsr330; +import org.testng.annotations.Test; + +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.google.inject.TypeLiteral; + +/** + * @author Adrian Cole + */ +@Test(groups = "unit", testName = "azureblob.AzureBlobContextModuleTest") +public class AzureBlobContextModuleTest { + + Injector createInjector() { + return Guice.createInjector(new StubAzureBlobStoreModule(), BlobStoreMapsModule.Builder + .newBuilder(new TypeLiteral() { + }, new TypeLiteral() { + }, new TypeLiteral() { + }).build(), new AzureBlobContextModule() { + @Override + protected void configure() { + bindConstant().annotatedWith( + Jsr330.named(AzureBlobConstants.PROPERTY_AZURESTORAGE_ACCOUNT)).to("user"); + bindConstant() + .annotatedWith(Jsr330.named(AzureBlobConstants.PROPERTY_AZURESTORAGE_KEY)).to( + "key"); + bindConstant().annotatedWith( + Jsr330.named(AzureBlobConstants.PROPERTY_AZUREBLOB_ENDPOINT)).to( + "http://localhost"); + super.configure(); + } + }); + } + + @Test + void testContextImpl() { + AzureBlobContext handler = createInjector().getInstance(AzureBlobContext.class); + assertEquals(handler.getClass(), AzureBlobContextImpl.class); + } + +} \ No newline at end of file diff --git a/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/AzureBlobStoreTest.java b/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/AzureBlobStoreTest.java index fd417f51da..cf6dcd79ef 100644 --- a/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/AzureBlobStoreTest.java +++ b/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/AzureBlobStoreTest.java @@ -23,41 +23,43 @@ */ package org.jclouds.azure.storage.blob; -import static org.jclouds.azure.storage.blob.options.CreateContainerOptions.Builder.withPublicAcl; -import static org.jclouds.azure.storage.options.ListOptions.Builder.maxResults; import static org.testng.Assert.assertEquals; +import java.io.IOException; import java.lang.reflect.Method; import java.net.URI; import java.util.Collections; +import javax.inject.Singleton; import javax.ws.rs.HttpMethod; +import javax.ws.rs.core.HttpHeaders; import org.jclouds.azure.storage.AzureBlob; +import org.jclouds.azure.storage.blob.domain.Blob; import org.jclouds.azure.storage.blob.functions.ReturnTrueIfContainerAlreadyExists; -import org.jclouds.azure.storage.blob.options.CreateContainerOptions; -import org.jclouds.azure.storage.options.CreateOptions; -import org.jclouds.azure.storage.options.ListOptions; import org.jclouds.azure.storage.reference.AzureStorageConstants; +import org.jclouds.blobstore.functions.ReturnVoidOnNotFoundOr404; +import org.jclouds.blobstore.reference.BlobStoreConstants; import org.jclouds.concurrent.WithinThreadExecutorService; import org.jclouds.concurrent.config.ExecutorServiceModule; import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpUtils; import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule; +import org.jclouds.http.functions.ParseETagHeader; import org.jclouds.http.functions.ParseSax; import org.jclouds.http.functions.ReturnTrueIf2xx; -import org.jclouds.http.functions.ReturnTrueOn404; +import org.jclouds.http.functions.ReturnVoidIf2xx; import org.jclouds.rest.JaxrsAnnotationProcessor; import org.jclouds.rest.config.JaxrsModule; import org.jclouds.util.Jsr330; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; -import com.google.common.collect.ImmutableMultimap; import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Injector; import com.google.inject.Key; +import com.google.inject.Provides; import com.google.inject.TypeLiteral; /** @@ -65,7 +67,7 @@ import com.google.inject.TypeLiteral; * * @author Adrian Cole */ -@Test(groups = "unit", testName = "cloudservers.AzureBlobStoreTest") +@Test(groups = "unit", testName = "azureblob.AzureBlobStoreTest") public class AzureBlobStoreTest { public void testListContainers() throws SecurityException, NoSuchMethodException { @@ -85,27 +87,6 @@ public class AzureBlobStoreTest { assertEquals(processor.createExceptionParserOrNullIfNotFound(method), null); } - public void testListContainersOptions() throws SecurityException, NoSuchMethodException { - Method method = AzureBlobStore.class.getMethod("listContainers", ListOptions.class); - - HttpRequest httpMethod = processor.createRequest(method, new Object[] { maxResults(1).marker( - "marker").prefix("prefix") }); - assertEquals(httpMethod.getEndpoint().getHost(), "myaccount.blob.core.windows.net"); - assertEquals(httpMethod.getEndpoint().getPath(), "/"); - assert httpMethod.getEndpoint().getQuery().contains("comp=list"); - assert httpMethod.getEndpoint().getQuery().contains("marker=marker"); - assert httpMethod.getEndpoint().getQuery().contains("maxresults=1"); - assert httpMethod.getEndpoint().getQuery().contains("prefix=prefix"); - assertEquals(httpMethod.getMethod(), HttpMethod.GET); - assertEquals(httpMethod.getHeaders().size(), 1); - assertEquals(httpMethod.getHeaders().get("x-ms-version"), Collections - .singletonList("2009-07-17")); - assertEquals(processor.createResponseParser(method, httpMethod, null).getClass(), - ParseSax.class); - // TODO check generic type of response parser - assertEquals(processor.createExceptionParserOrNullIfNotFound(method), null); - } - public void testCreateContainer() throws SecurityException, NoSuchMethodException { Method method = AzureBlobStore.class.getMethod("createContainer", String.class); @@ -137,94 +118,10 @@ public class AzureBlobStoreTest { assertEquals(httpMethod.getHeaders().get("x-ms-version"), Collections .singletonList("2009-07-17")); assertEquals(processor.createResponseParser(method, httpMethod, null).getClass(), - ReturnTrueIf2xx.class); + ReturnVoidIf2xx.class); // TODO check generic type of response parser assertEquals(processor.createExceptionParserOrNullIfNotFound(method).getClass(), - ReturnTrueOn404.class); - } - - public void testCreateContainerOptions() throws SecurityException, NoSuchMethodException { - Method method = AzureBlobStore.class.getMethod("createContainer", String.class, - CreateContainerOptions.class); - - HttpRequest httpMethod = processor.createRequest(method, new Object[] { "container", - withPublicAcl().withMetadata(ImmutableMultimap.of("foo", "bar")) }); - assertEquals(httpMethod.getEndpoint().getHost(), "myaccount.blob.core.windows.net"); - assertEquals(httpMethod.getEndpoint().getPath(), "/container"); - assertEquals(httpMethod.getEndpoint().getQuery(), "restype=container"); - assertEquals(httpMethod.getMethod(), HttpMethod.PUT); - assertEquals(httpMethod.getHeaders().size(), 4); - assertEquals(httpMethod.getHeaders().get("x-ms-version"), Collections - .singletonList("2009-07-17")); - assertEquals(httpMethod.getHeaders().get("x-ms-meta-foo"), Collections.singletonList("bar")); - assertEquals(httpMethod.getHeaders().get("x-ms-prop-publicaccess"), Collections - .singletonList("true")); - assertEquals(httpMethod.getHeaders().get("Content-Length"), Collections.singletonList("0")); - assertEquals(processor.createResponseParser(method, httpMethod, null).getClass(), - ReturnTrueIf2xx.class); - // TODO check generic type of response parser - assertEquals(processor.createExceptionParserOrNullIfNotFound(method).getClass(), - ReturnTrueIfContainerAlreadyExists.class); - } - - public void testCreateRootContainer() throws SecurityException, NoSuchMethodException { - Method method = AzureBlobStore.class.getMethod("createRootContainer"); - - HttpRequest httpMethod = processor.createRequest(method, new Object[] {}); - assertEquals(httpMethod.getEndpoint().getHost(), "myaccount.blob.core.windows.net"); - assertEquals(httpMethod.getEndpoint().getPath(), "/$root"); - assertEquals(httpMethod.getEndpoint().getQuery(), "restype=container"); - assertEquals(httpMethod.getMethod(), HttpMethod.PUT); - assertEquals(httpMethod.getHeaders().size(), 2); - assertEquals(httpMethod.getHeaders().get("x-ms-version"), Collections - .singletonList("2009-07-17")); - assertEquals(httpMethod.getHeaders().get("Content-Length"), Collections.singletonList("0")); - assertEquals(processor.createResponseParser(method, httpMethod, null).getClass(), - ReturnTrueIf2xx.class); - // TODO check generic type of response parser - assertEquals(processor.createExceptionParserOrNullIfNotFound(method).getClass(), - ReturnTrueIfContainerAlreadyExists.class); - } - - public void testDeleteRootContainer() throws SecurityException, NoSuchMethodException { - Method method = AzureBlobStore.class.getMethod("deleteRootContainer"); - - HttpRequest httpMethod = processor.createRequest(method, new Object[] {}); - assertEquals(httpMethod.getEndpoint().getHost(), "myaccount.blob.core.windows.net"); - assertEquals(httpMethod.getEndpoint().getPath(), "/$root"); - assertEquals(httpMethod.getEndpoint().getQuery(), "restype=container"); - assertEquals(httpMethod.getMethod(), HttpMethod.DELETE); - assertEquals(httpMethod.getHeaders().size(), 1); - assertEquals(httpMethod.getHeaders().get("x-ms-version"), Collections - .singletonList("2009-07-17")); - assertEquals(processor.createResponseParser(method, httpMethod, null).getClass(), - ReturnTrueIf2xx.class); - // TODO check generic type of response parser - assertEquals(processor.createExceptionParserOrNullIfNotFound(method).getClass(), - ReturnTrueOn404.class); - } - - public void testCreateRootContainerOptions() throws SecurityException, NoSuchMethodException { - Method method = AzureBlobStore.class.getMethod("createRootContainer", CreateOptions.class); - - HttpRequest httpMethod = processor.createRequest(method, new Object[] { withPublicAcl() - .withMetadata(ImmutableMultimap.of("foo", "bar")) }); - assertEquals(httpMethod.getEndpoint().getHost(), "myaccount.blob.core.windows.net"); - assertEquals(httpMethod.getEndpoint().getPath(), "/$root"); - assertEquals(httpMethod.getEndpoint().getQuery(), "restype=container"); - assertEquals(httpMethod.getMethod(), HttpMethod.PUT); - assertEquals(httpMethod.getHeaders().size(), 4); - assertEquals(httpMethod.getHeaders().get("x-ms-version"), Collections - .singletonList("2009-07-17")); - assertEquals(httpMethod.getHeaders().get("x-ms-meta-foo"), Collections.singletonList("bar")); - assertEquals(httpMethod.getHeaders().get("x-ms-prop-publicaccess"), Collections - .singletonList("true")); - assertEquals(httpMethod.getHeaders().get("Content-Length"), Collections.singletonList("0")); - assertEquals(processor.createResponseParser(method, httpMethod, null).getClass(), - ReturnTrueIf2xx.class); - // TODO check generic type of response parser - assertEquals(processor.createExceptionParserOrNullIfNotFound(method).getClass(), - ReturnTrueIfContainerAlreadyExists.class); + ReturnVoidOnNotFoundOr404.class); } public void testListBlobs() throws SecurityException, NoSuchMethodException { @@ -244,21 +141,29 @@ public class AzureBlobStoreTest { assertEquals(processor.createExceptionParserOrNullIfNotFound(method), null); } - public void testListRootBlobs() throws SecurityException, NoSuchMethodException { - Method method = AzureBlobStore.class.getMethod("listBlobs"); + public void testPutBlob() throws SecurityException, NoSuchMethodException, IOException { + Method method = AzureBlobStore.class.getMethod("putBlob", String.class, Blob.class); - HttpRequest httpMethod = processor.createRequest(method, new Object[] {}); + Blob blob = new Blob("test"); + blob.setData("test"); + HttpRequest httpMethod = processor + .createRequest(method, new Object[] { "mycontainer", blob }); assertEquals(httpMethod.getEndpoint().getHost(), "myaccount.blob.core.windows.net"); - assertEquals(httpMethod.getEndpoint().getPath(), "/$root"); - assertEquals(httpMethod.getEndpoint().getQuery(), "restype=container&comp=list"); - assertEquals(httpMethod.getMethod(), HttpMethod.GET); - assertEquals(httpMethod.getHeaders().size(), 1); + assertEquals(httpMethod.getEndpoint().getPath(), "/mycontainer/test"); + assertEquals(httpMethod.getEndpoint().getQuery(), null); + assertEquals(httpMethod.getMethod(), HttpMethod.PUT); + assertEquals(httpMethod.getHeaders().size(), 4); assertEquals(httpMethod.getHeaders().get("x-ms-version"), Collections .singletonList("2009-07-17")); + assertEquals(httpMethod.getHeaders().get(HttpHeaders.CONTENT_LENGTH), Collections + .singletonList("test".length() + "")); + assertEquals(httpMethod.getHeaders().get(HttpHeaders.CONTENT_TYPE), Collections + .singletonList("application/octet-stream")); + assertEquals(httpMethod.getHeaders().get("Content-MD5"), Collections.singletonList(HttpUtils + .toBase64String(HttpUtils.md5("test")))); + assertEquals(httpMethod.getEntity(), "test"); assertEquals(processor.createResponseParser(method, httpMethod, null).getClass(), - ParseSax.class); - // TODO check generic type of response parser - assertEquals(processor.createExceptionParserOrNullIfNotFound(method), null); + ParseETagHeader.class); } @BeforeClass @@ -274,7 +179,22 @@ public class AzureBlobStoreTest { bindConstant().annotatedWith( Jsr330.named(AzureStorageConstants.PROPERTY_AZURESTORAGE_KEY)).to( HttpUtils.toBase64String("key".getBytes())); + bindConstant().annotatedWith( + Jsr330.named(BlobStoreConstants.PROPERTY_USER_METADATA_PREFIX)).to("prefix"); } + + @SuppressWarnings("unused") + @Provides + @Singleton + AzureBlobUtil getAzureBlobUtil() { + return new AzureBlobUtil() { + + public byte[] getMD5(URI container, String key) { + return HttpUtils.fromHexString("01"); + } + }; + } + }, new JaxrsModule(), new ExecutorServiceModule(new WithinThreadExecutorService()), new JavaUrlHttpCommandExecutorServiceModule()); processor = injector.getInstance(Key diff --git a/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/config/RestAzureBlobConnectionModuleTest.java b/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/config/RestAzureBlobConnectionModuleTest.java new file mode 100644 index 0000000000..ba37e4a6e9 --- /dev/null +++ b/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/config/RestAzureBlobConnectionModuleTest.java @@ -0,0 +1,94 @@ +/** + * + * Copyright (C) 2009 Global Cloud Specialists, Inc. + * + * ==================================================================== + * 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.azure.storage.blob.config; + +import static org.testng.Assert.assertEquals; + +import org.jclouds.azure.storage.blob.handlers.AzureBlobClientErrorRetryHandler; +import org.jclouds.azure.storage.blob.reference.AzureBlobConstants; +import org.jclouds.azure.storage.handlers.ParseAzureStorageErrorFromXmlContent; +import org.jclouds.http.HttpUtils; +import org.jclouds.http.functions.config.ParserModule; +import org.jclouds.http.handlers.DelegatingErrorHandler; +import org.jclouds.http.handlers.DelegatingRetryHandler; +import org.jclouds.http.handlers.RedirectionRetryHandler; +import org.jclouds.util.Jsr330; +import org.testng.annotations.Test; + +import com.google.inject.AbstractModule; +import com.google.inject.Guice; +import com.google.inject.Injector; + +/** + * @author Adrian Cole + */ +@Test(groups = "unit", testName = "s3.RestAzureBlobConnectionModuleTest") +public class RestAzureBlobConnectionModuleTest { + + Injector createInjector() { + return Guice.createInjector(new RestAzureBlobStoreModule(), new ParserModule(), + new AbstractModule() { + @Override + protected void configure() { + bindConstant().annotatedWith( + Jsr330.named(AzureBlobConstants.PROPERTY_AZURESTORAGE_ACCOUNT)).to( + "user"); + bindConstant().annotatedWith( + Jsr330.named(AzureBlobConstants.PROPERTY_AZURESTORAGE_KEY)).to( + HttpUtils.toBase64String("secret".getBytes())); + bindConstant().annotatedWith( + Jsr330.named(AzureBlobConstants.PROPERTY_AZUREBLOB_ENDPOINT)).to( + "http://localhost"); + } + }); + } + + @Test + void testServerErrorHandler() { + DelegatingErrorHandler handler = createInjector().getInstance(DelegatingErrorHandler.class); + assertEquals(handler.getServerErrorHandler().getClass(), + ParseAzureStorageErrorFromXmlContent.class); + } + + @Test + void testClientErrorHandler() { + DelegatingErrorHandler handler = createInjector().getInstance(DelegatingErrorHandler.class); + assertEquals(handler.getClientErrorHandler().getClass(), + ParseAzureStorageErrorFromXmlContent.class); + } + + @Test + void testClientRetryHandler() { + DelegatingRetryHandler handler = createInjector().getInstance(DelegatingRetryHandler.class); + assertEquals(handler.getClientErrorRetryHandler().getClass(), + AzureBlobClientErrorRetryHandler.class); + } + + @Test + void testRedirectionRetryHandler() { + DelegatingRetryHandler handler = createInjector().getInstance(DelegatingRetryHandler.class); + assertEquals(handler.getRedirectionRetryHandler().getClass(), RedirectionRetryHandler.class); + } + +} \ No newline at end of file diff --git a/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/config/StubAzureBlobStoreModule.java b/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/config/StubAzureBlobStoreModule.java index 4fe9dd4140..0e4efbd3b6 100644 --- a/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/config/StubAzureBlobStoreModule.java +++ b/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/config/StubAzureBlobStoreModule.java @@ -28,9 +28,13 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.jclouds.azure.storage.AzureBlob; -import org.jclouds.azure.storage.blob.AzureBlobStore; +import org.jclouds.azure.storage.blob.AzureBlobConnection; import org.jclouds.azure.storage.blob.domain.Blob; -import org.jclouds.azure.storage.blob.internal.StubAzureBlobStore; +import org.jclouds.azure.storage.blob.domain.BlobMetadata; +import org.jclouds.azure.storage.blob.domain.ContainerMetadata; +import org.jclouds.azure.storage.blob.internal.StubAzureBlobConnection; +import org.jclouds.blobstore.BlobStore; +import org.jclouds.blobstore.integration.internal.StubBlobStore; import org.jclouds.cloud.ConfiguresCloudConnection; import org.jclouds.http.functions.config.ParserModule; @@ -51,7 +55,10 @@ public class StubAzureBlobStoreModule extends AbstractModule { install(new ParserModule()); bind(new TypeLiteral>>() { }).toInstance(map); - bind(AzureBlobStore.class).to(StubAzureBlobStore.class).asEagerSingleton(); + bind(new TypeLiteral>() { + }).to(new TypeLiteral>() { + }).asEagerSingleton(); + bind(AzureBlobConnection.class).to(StubAzureBlobConnection.class).asEagerSingleton(); bind(URI.class).annotatedWith(AzureBlob.class).toInstance( URI.create("https://id.blob.core.windows.net")); } diff --git a/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/integration/AzureBlobContainerIntegrationTest.java b/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/integration/AzureBlobContainerIntegrationTest.java index a95bfe68dc..68618f7cb1 100755 --- a/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/integration/AzureBlobContainerIntegrationTest.java +++ b/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/integration/AzureBlobContainerIntegrationTest.java @@ -23,7 +23,7 @@ */ package org.jclouds.azure.storage.blob.integration; -import org.jclouds.azure.storage.blob.AzureBlobStore; +import org.jclouds.azure.storage.blob.AzureBlobConnection; import org.jclouds.azure.storage.blob.domain.Blob; import org.jclouds.azure.storage.blob.domain.BlobMetadata; import org.jclouds.azure.storage.blob.domain.ContainerMetadata; @@ -33,11 +33,8 @@ import org.testng.annotations.Test; /** * @author Adrian Cole */ -@Test(groups = { "integration", "live" }, testName = "cloudfiles.AzureBlobContainerIntegrationTest") +@Test(groups = { "integration", "live" }, testName = "azureblob.AzureBlobContainerIntegrationTest") public class AzureBlobContainerIntegrationTest extends - BaseContainerIntegrationTest { - @Test(groups = { "integration", "live" }, enabled = false) - public void deleteContainerIfEmptyButHasContents() throws Exception { - // azure recursively deletes containers in the background. - } + BaseContainerIntegrationTest { + } \ No newline at end of file diff --git a/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/integration/AzureBlobContainerLiveTest.java b/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/integration/AzureBlobContainerLiveTest.java index 197e990f65..c45e059ac0 100644 --- a/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/integration/AzureBlobContainerLiveTest.java +++ b/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/integration/AzureBlobContainerLiveTest.java @@ -23,7 +23,7 @@ */ package org.jclouds.azure.storage.blob.integration; -import org.jclouds.azure.storage.blob.AzureBlobStore; +import org.jclouds.azure.storage.blob.AzureBlobConnection; import org.jclouds.azure.storage.blob.domain.Blob; import org.jclouds.azure.storage.blob.domain.BlobMetadata; import org.jclouds.azure.storage.blob.domain.ContainerMetadata; @@ -33,8 +33,8 @@ import org.testng.annotations.Test; /** * @author Adrian Cole */ -@Test(groups = { "live" }, testName = "cloudfiles.AzureBlobContainerLiveTest") +@Test(groups = { "live" }, testName = "azureblob.AzureBlobContainerLiveTest") public class AzureBlobContainerLiveTest extends - BaseContainerLiveTest { + BaseContainerLiveTest { } \ No newline at end of file diff --git a/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/integration/AzureBlobInputStreamMapIntegrationTest.java b/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/integration/AzureBlobInputStreamMapIntegrationTest.java index 3a2766b9ba..23cd95ae25 100644 --- a/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/integration/AzureBlobInputStreamMapIntegrationTest.java +++ b/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/integration/AzureBlobInputStreamMapIntegrationTest.java @@ -23,10 +23,7 @@ */ package org.jclouds.azure.storage.blob.integration; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeoutException; - -import org.jclouds.azure.storage.blob.AzureBlobStore; +import org.jclouds.azure.storage.blob.AzureBlobConnection; import org.jclouds.azure.storage.blob.domain.Blob; import org.jclouds.azure.storage.blob.domain.BlobMetadata; import org.jclouds.azure.storage.blob.domain.ContainerMetadata; @@ -36,31 +33,9 @@ import org.testng.annotations.Test; /** * @author Adrian Cole */ -@Test(groups = { "integration", "live" }, testName = "cloudfiles.AzureBlobInputStreamMapIntegrationTest") -public class AzureBlobInputStreamMapIntegrationTest extends - BaseInputStreamMapIntegrationTest { +@Test(groups = { "integration", "live" }, testName = "azureblob.AzureBlobInputStreamMapIntegrationTest") +public class AzureBlobInputStreamMapIntegrationTest + extends + BaseInputStreamMapIntegrationTest { - @Override - public void testContainsBytesValue() throws InterruptedException, ExecutionException, - TimeoutException { - // http://code.google.com/p/jclouds/issues/detail?id=90 - } - - @Override - public void testContainsFileValue() throws InterruptedException, ExecutionException, - TimeoutException { - // http://code.google.com/p/jclouds/issues/detail?id=90 - } - - @Override - public void testContainsInputStreamValue() throws InterruptedException, ExecutionException, - TimeoutException { - // http://code.google.com/p/jclouds/issues/detail?id=90 - } - - @Override - public void testContainsStringValue() throws InterruptedException, ExecutionException, - TimeoutException { - // http://code.google.com/p/jclouds/issues/detail?id=90 - } } \ No newline at end of file diff --git a/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/integration/AzureBlobIntegrationTest.java b/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/integration/AzureBlobIntegrationTest.java index 8cdd95eed8..265cb79ed6 100755 --- a/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/integration/AzureBlobIntegrationTest.java +++ b/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/integration/AzureBlobIntegrationTest.java @@ -30,7 +30,7 @@ import java.util.Collections; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; -import org.jclouds.azure.storage.blob.AzureBlobStore; +import org.jclouds.azure.storage.blob.AzureBlobConnection; import org.jclouds.azure.storage.blob.domain.Blob; import org.jclouds.azure.storage.blob.domain.BlobMetadata; import org.jclouds.azure.storage.blob.domain.ContainerMetadata; @@ -41,9 +41,9 @@ import org.testng.annotations.Test; /** * @author Adrian Cole */ -@Test(groups = { "integration", "live" }, testName = "cloudfiles.AzureBlobIntegrationTest") +@Test(groups = { "integration", "live" }, testName = "azureblob.AzureBlobIntegrationTest") public class AzureBlobIntegrationTest extends - BaseBlobIntegrationTest { + BaseBlobIntegrationTest { @Override @Test(enabled = false) @@ -68,7 +68,7 @@ public class AzureBlobIntegrationTest extends protected void validateMetadata(BlobMetadata metadata) { assertEquals(metadata.getContentType(), "text/plain"); - // we can't check this while hacking around lack of content-md5, as GET of the first byte will + // we can't check this while hacking around HEAD being broken, as GET of the first byte will // show incorrect length 1, the returned size, as opposed to the real length. This is an ok // tradeoff, as a container list will contain the correct size of the objects in an // inexpensive fashion diff --git a/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/integration/AzureBlobLiveTest.java b/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/integration/AzureBlobLiveTest.java index 0c8629cac2..18657513df 100644 --- a/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/integration/AzureBlobLiveTest.java +++ b/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/integration/AzureBlobLiveTest.java @@ -23,7 +23,7 @@ */ package org.jclouds.azure.storage.blob.integration; -import org.jclouds.azure.storage.blob.AzureBlobStore; +import org.jclouds.azure.storage.blob.AzureBlobConnection; import org.jclouds.azure.storage.blob.domain.Blob; import org.jclouds.azure.storage.blob.domain.BlobMetadata; import org.jclouds.azure.storage.blob.domain.ContainerMetadata; @@ -33,8 +33,8 @@ import org.testng.annotations.Test; /** * @author Adrian Cole */ -@Test(groups = { "live" }, testName = "cloudfiles.AzureBlobLiveTest") +@Test(groups = { "live" }, testName = "azureblob.AzureBlobLiveTest") public class AzureBlobLiveTest extends - BaseBlobLiveTest { + BaseBlobLiveTest { } \ No newline at end of file diff --git a/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/integration/AzureBlobMapIntegrationTest.java b/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/integration/AzureBlobMapIntegrationTest.java index 2539741eaa..5ad66ff7eb 100644 --- a/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/integration/AzureBlobMapIntegrationTest.java +++ b/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/integration/AzureBlobMapIntegrationTest.java @@ -23,10 +23,7 @@ */ package org.jclouds.azure.storage.blob.integration; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeoutException; - -import org.jclouds.azure.storage.blob.AzureBlobStore; +import org.jclouds.azure.storage.blob.AzureBlobConnection; import org.jclouds.azure.storage.blob.domain.Blob; import org.jclouds.azure.storage.blob.domain.BlobMetadata; import org.jclouds.azure.storage.blob.domain.ContainerMetadata; @@ -36,12 +33,8 @@ import org.testng.annotations.Test; /** * @author Adrian Cole */ -@Test(groups = { "integration", "live" }, testName = "cloudfiles.AzureBlobMapIntegrationTest") +@Test(groups = { "integration", "live" }, testName = "azureblob.AzureBlobMapIntegrationTest") public class AzureBlobMapIntegrationTest extends - BaseBlobMapIntegrationTest { + BaseBlobMapIntegrationTest { - @Override - public void testContains() throws InterruptedException, ExecutionException, TimeoutException { - // http://code.google.com/p/jclouds/issues/detail?id=90 - } } \ No newline at end of file diff --git a/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/integration/AzureBlobServiceIntegrationTest.java b/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/integration/AzureBlobServiceIntegrationTest.java index 1065056199..a09a8c94c2 100644 --- a/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/integration/AzureBlobServiceIntegrationTest.java +++ b/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/integration/AzureBlobServiceIntegrationTest.java @@ -23,7 +23,7 @@ */ package org.jclouds.azure.storage.blob.integration; -import org.jclouds.azure.storage.blob.AzureBlobStore; +import org.jclouds.azure.storage.blob.AzureBlobConnection; import org.jclouds.azure.storage.blob.domain.Blob; import org.jclouds.azure.storage.blob.domain.BlobMetadata; import org.jclouds.azure.storage.blob.domain.ContainerMetadata; @@ -33,8 +33,8 @@ import org.testng.annotations.Test; /** * @author Adrian Cole */ -@Test(groups = { "integration", "live" }, testName = "cloudfiles.AzureBlobServiceIntegrationTest") +@Test(groups = { "integration", "live" }, testName = "azureblob.AzureBlobServiceIntegrationTest") public class AzureBlobServiceIntegrationTest extends - BaseServiceIntegrationTest { + BaseServiceIntegrationTest { } \ No newline at end of file diff --git a/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/integration/AzureBlobTestInitializer.java b/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/integration/AzureBlobTestInitializer.java index e6ef5e40cd..b35489223d 100644 --- a/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/integration/AzureBlobTestInitializer.java +++ b/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/integration/AzureBlobTestInitializer.java @@ -23,9 +23,9 @@ */ package org.jclouds.azure.storage.blob.integration; +import org.jclouds.azure.storage.blob.AzureBlobConnection; import org.jclouds.azure.storage.blob.AzureBlobContextBuilder; import org.jclouds.azure.storage.blob.AzureBlobContextFactory; -import org.jclouds.azure.storage.blob.AzureBlobStore; import org.jclouds.azure.storage.blob.config.StubAzureBlobStoreModule; import org.jclouds.azure.storage.blob.domain.Blob; import org.jclouds.azure.storage.blob.domain.BlobMetadata; @@ -41,17 +41,17 @@ import com.google.inject.Module; * @author Adrian Cole */ public class AzureBlobTestInitializer extends - BaseTestInitializer { + BaseTestInitializer { @Override - protected BlobStoreContext createLiveContext( + protected BlobStoreContext createLiveContext( Module configurationModule, String url, String app, String account, String key) { return new AzureBlobContextBuilder(account, key).withSaxDebug().relaxSSLHostname() .withModules(configurationModule, new Log4JLoggingModule()).buildContext(); } @Override - protected BlobStoreContext createStubContext() { + protected BlobStoreContext createStubContext() { return AzureBlobContextFactory.createContext("user", "pass", new StubAzureBlobStoreModule()); } } \ No newline at end of file diff --git a/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/internal/StubAzureBlobStore.java b/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/internal/StubAzureBlobConnection.java similarity index 65% rename from azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/internal/StubAzureBlobStore.java rename to azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/internal/StubAzureBlobConnection.java index 21ce72884a..37f1f745d5 100644 --- a/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/internal/StubAzureBlobStore.java +++ b/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/internal/StubAzureBlobConnection.java @@ -32,6 +32,7 @@ import java.util.concurrent.Future; import javax.inject.Inject; import javax.inject.Provider; +import org.jclouds.azure.storage.blob.AzureBlobConnection; import org.jclouds.azure.storage.blob.AzureBlobStore; import org.jclouds.azure.storage.blob.domain.Blob; import org.jclouds.azure.storage.blob.domain.BlobMetadata; @@ -39,15 +40,17 @@ import org.jclouds.azure.storage.blob.domain.ContainerMetadata; import org.jclouds.azure.storage.blob.domain.ListBlobsResponse; import org.jclouds.azure.storage.blob.domain.TreeSetListBlobsResponse; import org.jclouds.azure.storage.blob.options.CreateContainerOptions; +import org.jclouds.azure.storage.blob.options.ListBlobsOptions; import org.jclouds.azure.storage.domain.BoundedSortedSet; -import org.jclouds.azure.storage.options.CreateOptions; import org.jclouds.azure.storage.options.ListOptions; import org.jclouds.blobstore.ContainerNotFoundException; import org.jclouds.blobstore.integration.internal.StubBlobStore; +import org.jclouds.http.options.GetOptions; import org.jclouds.util.DateService; import com.google.common.base.Function; import com.google.common.collect.Iterables; +import com.google.common.collect.Multimap; import com.google.common.collect.Sets; /** @@ -55,40 +58,16 @@ import com.google.common.collect.Sets; * * @author Adrian Cole */ -public class StubAzureBlobStore extends StubBlobStore - implements AzureBlobStore { +public class StubAzureBlobConnection extends StubBlobStore + implements AzureBlobConnection { @Inject - protected StubAzureBlobStore(Map> containerToBlobs, + protected StubAzureBlobConnection(Map> containerToBlobs, DateService dateService, Provider containerMetaProvider, Provider blobProvider) { super(containerToBlobs, dateService, containerMetaProvider, blobProvider); } - public BoundedSortedSet listContainers(ListOptions options) { - return null; - } - - public Future createContainer(String container, CreateContainerOptions options) { - return null; - } - - public Future createRootContainer() { - return null; - } - - public Future createRootContainer(CreateOptions options) { - return null; - } - - public Future deleteRootContainer() { - return null; - } - - public Future listBlobs() { - return null; - } - public Future listBlobs(final String name) { return new FutureBase() { public ListBlobsResponse get() throws InterruptedException, ExecutionException { @@ -109,4 +88,51 @@ public class StubAzureBlobStore extends StubBlobStore createContainer(String container, CreateContainerOptions... options) { + throw new UnsupportedOperationException(); + } + + public Future deleteBlob(String container, String key) { + throw new UnsupportedOperationException(); + } + + public Future deleteRootContainer() { + throw new UnsupportedOperationException(); + } + + public Future getBlob(String container, String key, GetOptions... options) { + throw new UnsupportedOperationException(); + } + + public BlobMetadata getBlobProperties(String container, String key) { + throw new UnsupportedOperationException(); + } + + public Future listBlobs(String container, ListBlobsOptions... options) { + throw new UnsupportedOperationException(); + } + + public Future listBlobs(ListBlobsOptions... options) { + throw new UnsupportedOperationException(); + } + + public BoundedSortedSet listContainers(ListOptions... listOptions) { + throw new UnsupportedOperationException(); + } + + public Future createRootContainer(CreateContainerOptions... options) { + throw new UnsupportedOperationException(); + } + + public ContainerMetadata getContainerProperties(String container) { + throw new UnsupportedOperationException(); + } + + public void setContainerMetadata(String container, Multimap metadata) { + throw new UnsupportedOperationException(); + } + + public void setBlobMetadata(String container, String key, Multimap metadata) { + throw new UnsupportedOperationException(); + } } diff --git a/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/xml/AddMD5ToListBlobsResponseTest.java b/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/xml/AddMD5ToListBlobsResponseTest.java new file mode 100644 index 0000000000..f89c9ff3df --- /dev/null +++ b/azure/storage/blob/core/src/test/java/org/jclouds/azure/storage/blob/xml/AddMD5ToListBlobsResponseTest.java @@ -0,0 +1,133 @@ +/** + * + * Copyright (C) 2009 Global Cloud Specialists, Inc. + * + * ==================================================================== + * 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.azure.storage.blob.xml; + +import static org.testng.Assert.assertEquals; + +import java.io.InputStream; +import java.net.URI; + +import javax.inject.Singleton; + +import org.jclouds.azure.storage.blob.AzureBlobUtil; +import org.jclouds.azure.storage.blob.domain.BlobMetadata; +import org.jclouds.azure.storage.blob.domain.ListBlobsResponse; +import org.jclouds.azure.storage.blob.domain.TreeSetListBlobsResponse; +import org.jclouds.azure.storage.domain.BoundedSortedSet; +import org.jclouds.http.HttpUtils; +import org.jclouds.http.functions.BaseHandlerTest; +import org.jclouds.util.DateService; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableSortedSet; +import com.google.inject.AbstractModule; +import com.google.inject.Provides; + +/** + * Tests behavior of {@code AddMD5ToListBlobsResponse} + * + * @author Adrian Cole + */ +@Test(groups = "unit", testName = "azureblob.AddMD5ToListBlobsResponseTest") +public class AddMD5ToListBlobsResponseTest extends BaseHandlerTest { + private DateService dateService; + + @BeforeTest + @Override + protected void setUpInjector() { + super.setUpInjector(); + injector = injector.createChildInjector(new AbstractModule() { + + @Override + protected void configure() { + } + + @SuppressWarnings("unused") + @Provides + @Singleton + AzureBlobUtil getAzureBlobUtil() { + return new AzureBlobUtil() { + + public byte[] getMD5(URI container, String key) { + if (key.equals("blob1.txt")) { + return HttpUtils.fromHexString("01"); + } else if (key.equals("blob2.txt")) { + return HttpUtils.fromHexString("02"); + } else if (key.equals("newblob1.txt")) { + return HttpUtils.fromHexString("03"); + } + return null; + } + }; + } + }); + dateService = injector.getInstance(DateService.class); + assert dateService != null; + } + + @SuppressWarnings("unchecked") + public void testApplyInputStream() { + InputStream is = getClass().getResourceAsStream("/test_list_blobs.xml"); + ListBlobsResponse list = new TreeSetListBlobsResponse( + URI.create("http://myaccount.blob.core.windows.net/mycontainer"), + ImmutableSortedSet + .of( + new BlobMetadata( + "blob1.txt", + URI + .create("http://myaccount.blob.core.windows.net/mycontainer/blob1.txt"), + dateService + .rfc822DateParse("Thu, 18 Sep 2008 18:41:57 GMT"), + HttpUtils.fromHexString("0x8CAE7D55D050B8B"), 8, + "text/plain; charset=UTF-8", HttpUtils + .fromHexString("01"), null, null), + new BlobMetadata( + "blob2.txt", + URI + .create("http://myaccount.blob.core.windows.net/mycontainer/blob2.txt"), + dateService + .rfc822DateParse("Thu, 18 Sep 2008 18:41:57 GMT"), + HttpUtils.fromHexString("0x8CAE7D55CF6C339"), 14, + "text/plain; charset=UTF-8", HttpUtils + .fromHexString("02"), null, null), + new BlobMetadata( + "newblob1.txt", + URI + .create("http://myaccount.blob.core.windows.net/mycontainer/newblob1.txt"), + dateService + .rfc822DateParse("Thu, 18 Sep 2008 18:41:57 GMT"), + HttpUtils.fromHexString("0x8CAE7D55CF6C339"), 25, + "text/plain; charset=UTF-8", HttpUtils + .fromHexString("03"), null, null) + + ), null, null, 4, "newblob2.txt", null, "myfolder/"); + + BoundedSortedSet result = (BoundedSortedSet) factory + .create(injector.getInstance(ContainerNameEnumerationResultsHandler.class)) + .parse(is); + + assertEquals(result, list); + } +} diff --git a/azure/storage/queue/core/src/test/java/org/jclouds/azure/storage/queue/AzureQueueConnectionLiveTest.java b/azure/storage/queue/core/src/test/java/org/jclouds/azure/storage/queue/AzureQueueConnectionLiveTest.java index e42f3197fd..579602a553 100644 --- a/azure/storage/queue/core/src/test/java/org/jclouds/azure/storage/queue/AzureQueueConnectionLiveTest.java +++ b/azure/storage/queue/core/src/test/java/org/jclouds/azure/storage/queue/AzureQueueConnectionLiveTest.java @@ -84,9 +84,11 @@ public class AzureQueueConnectionLiveTest { created = connection.createQueue(privateQueue, CreateOptions.Builder .withMetadata(ImmutableMultimap.of("foo", "bar"))); } catch (UndeclaredThrowableException e) { - HttpResponseException htpe = (HttpResponseException) e.getCause().getCause(); - if (htpe.getResponse().getStatusCode() == 409) - continue; + if (e.getCause().getCause() instanceof HttpResponseException) { + HttpResponseException htpe = (HttpResponseException) e.getCause().getCause(); + if (htpe.getResponse().getStatusCode() == 409) + continue; + } throw e; } } diff --git a/blobstore/core/src/main/java/org/jclouds/blobstore/BlobStore.java b/blobstore/core/src/main/java/org/jclouds/blobstore/BlobStore.java index a29ec08567..cd32593191 100644 --- a/blobstore/core/src/main/java/org/jclouds/blobstore/BlobStore.java +++ b/blobstore/core/src/main/java/org/jclouds/blobstore/BlobStore.java @@ -42,13 +42,11 @@ public interface BlobStore createContainer(String container); /** - * if supported, this will delete a container recursively. Otherwise, it will return false, if - * the container could not be deleted because it is not empty. + * This will delete a container recursively. * * @param container - * @return false if container cannot be deleted because it is not empty */ - Future deleteContainer(String container); + Future deleteContainer(String container); Future> listBlobs(String container); @@ -60,6 +58,6 @@ public interface BlobStore removeBlob(String container, String key); + Future removeBlob(String container, String key); } diff --git a/blobstore/core/src/main/java/org/jclouds/blobstore/BlobStoreContext.java b/blobstore/core/src/main/java/org/jclouds/blobstore/BlobStoreContext.java index 76f047580f..3c7f272982 100644 --- a/blobstore/core/src/main/java/org/jclouds/blobstore/BlobStoreContext.java +++ b/blobstore/core/src/main/java/org/jclouds/blobstore/BlobStoreContext.java @@ -38,7 +38,7 @@ import com.google.inject.ImplementedBy; * */ @ImplementedBy(BlobStoreContextImpl.class) -public interface BlobStoreContext, C extends ContainerMetadata, M extends BlobMetadata, B extends Blob> +public interface BlobStoreContext> extends CloudContext { /** @@ -55,5 +55,7 @@ public interface BlobStoreContext, C extends Contai */ BlobMap createBlobMap(String container); + BlobStore getBlobStore(); + B newBlob(String key); } \ No newline at end of file diff --git a/blobstore/core/src/main/java/org/jclouds/blobstore/BlobStoreContextBuilder.java b/blobstore/core/src/main/java/org/jclouds/blobstore/BlobStoreContextBuilder.java index 28c3ba6ffe..880e4e2f76 100644 --- a/blobstore/core/src/main/java/org/jclouds/blobstore/BlobStoreContextBuilder.java +++ b/blobstore/core/src/main/java/org/jclouds/blobstore/BlobStoreContextBuilder.java @@ -23,6 +23,7 @@ */ package org.jclouds.blobstore; +import java.util.List; import java.util.Properties; import java.util.concurrent.ExecutorService; @@ -39,7 +40,7 @@ import com.google.inject.util.Types; /** * @author Adrian Cole */ -public abstract class BlobStoreContextBuilder, C extends ContainerMetadata, M extends BlobMetadata, B extends Blob> +public abstract class BlobStoreContextBuilder> extends CloudContextBuilder { @SuppressWarnings("unchecked") @Override @@ -141,8 +142,12 @@ public abstract class BlobStoreContextBuilder, C ex this.containerMetadataType = containerMetadataType; this.blobMetadataType = blobMetadataType; this.blobType = blobType; - modules.add(BlobStoreMapsModule.Builder.newBuilder(connectionType, - containerMetadataType, blobMetadataType, blobType).build()); + addBlobStoreModule(modules); + } + + protected void addBlobStoreModule(List modules) { + modules.add(BlobStoreMapsModule.Builder.newBuilder(containerMetadataType, blobMetadataType, + blobType).build()); } @SuppressWarnings("unchecked") diff --git a/blobstore/core/src/main/java/org/jclouds/blobstore/BlobStoreContextImpl.java b/blobstore/core/src/main/java/org/jclouds/blobstore/BlobStoreContextImpl.java index fc66272b12..1b5f49f410 100644 --- a/blobstore/core/src/main/java/org/jclouds/blobstore/BlobStoreContextImpl.java +++ b/blobstore/core/src/main/java/org/jclouds/blobstore/BlobStoreContextImpl.java @@ -36,19 +36,22 @@ import org.jclouds.lifecycle.Closer; /** * @author Adrian Cole */ -public class BlobStoreContextImpl, C extends ContainerMetadata, M extends BlobMetadata, B extends Blob> +public class BlobStoreContextImpl> extends CloudContextImpl implements BlobStoreContext { private final BlobMap.Factory blobMapFactory; private final InputStreamMap.Factory inputStreamMapFactory; private final Provider blobProvider; + private final BlobStore blobStore; public BlobStoreContextImpl(BlobMap.Factory blobMapFactory, InputStreamMap.Factory inputStreamMapFactory, Closer closer, - Provider blobProvider, S defaultApi, URI endPoint, String account) { + Provider blobProvider, BlobStore blobStore, S defaultApi, URI endPoint, + String account) { super(closer, defaultApi, endPoint, account); this.blobMapFactory = blobMapFactory; this.inputStreamMapFactory = inputStreamMapFactory; this.blobProvider = blobProvider; + this.blobStore = blobStore; } public BlobMap createBlobMap(String container) { @@ -66,4 +69,8 @@ public class BlobStoreContextImpl, C extends Contai blob.getMetadata().setKey(key); return blob; } + + public BlobStore getBlobStore() { + return blobStore; + } } diff --git a/blobstore/core/src/main/java/org/jclouds/blobstore/BlobStoreMapsModule.java b/blobstore/core/src/main/java/org/jclouds/blobstore/BlobStoreMapsModule.java index 28f4b88bdb..065c4e54b7 100644 --- a/blobstore/core/src/main/java/org/jclouds/blobstore/BlobStoreMapsModule.java +++ b/blobstore/core/src/main/java/org/jclouds/blobstore/BlobStoreMapsModule.java @@ -23,16 +23,26 @@ */ package org.jclouds.blobstore; +import java.util.Map; +import java.util.Map.Entry; + import org.jclouds.blobstore.domain.Blob; import org.jclouds.blobstore.domain.BlobMetadata; import org.jclouds.blobstore.domain.ContainerMetadata; import org.jclouds.blobstore.internal.BlobMapImpl; import org.jclouds.blobstore.internal.InputStreamMapImpl; +import org.jclouds.blobstore.strategy.ClearContainerStrategy; +import org.jclouds.blobstore.strategy.ContainerCountStrategy; +import org.jclouds.blobstore.strategy.ContainsValueStrategy; import org.jclouds.blobstore.strategy.GetAllBlobMetadataStrategy; import org.jclouds.blobstore.strategy.GetAllBlobsStrategy; import org.jclouds.blobstore.strategy.internal.ContainerListGetAllBlobMetadataStrategy; +import org.jclouds.blobstore.strategy.internal.ContentMD5ContainsValueStrategy; +import org.jclouds.blobstore.strategy.internal.DeleteAllKeysClearContainerStrategy; +import org.jclouds.blobstore.strategy.internal.KeyCountStrategy; import org.jclouds.blobstore.strategy.internal.RetryOnNotFoundGetAllBlobsStrategy; +import com.google.common.collect.Maps; import com.google.inject.AbstractModule; import com.google.inject.Scopes; import com.google.inject.TypeLiteral; @@ -47,17 +57,12 @@ public class BlobStoreMapsModule extends AbstractModule { @SuppressWarnings("unchecked") private BlobStoreMapsModule(TypeLiteral blobMapFactoryType, TypeLiteral blobMapImplType, TypeLiteral inputStreamMapFactoryType, TypeLiteral inputStreamMapImplType, - TypeLiteral getAllBlobsStrategyType, TypeLiteral getAllBlobsStrategyImplType, - TypeLiteral getAllBlobMetadataStrategyType, - TypeLiteral getAllBlobMetadataStrategyImplType) { + Map strategyImplMap) { this.blobMapFactoryType = blobMapFactoryType; this.blobMapImplType = blobMapImplType; this.inputStreamMapFactoryType = inputStreamMapFactoryType; this.inputStreamMapImplType = inputStreamMapImplType; - this.getAllBlobsStrategyType = getAllBlobsStrategyType; - this.getAllBlobsStrategyImplType = getAllBlobsStrategyImplType; - this.getAllBlobMetadataStrategyType = getAllBlobMetadataStrategyType; - this.getAllBlobMetadataStrategyImplType = getAllBlobMetadataStrategyImplType; + this.strategyImplMap = strategyImplMap; } // code is unchecked here as we are getting types at runtime. Due to type erasure, we cannot pass @@ -71,17 +76,9 @@ public class BlobStoreMapsModule extends AbstractModule { @SuppressWarnings("unchecked") protected final TypeLiteral inputStreamMapImplType; @SuppressWarnings("unchecked") - protected final TypeLiteral getAllBlobsStrategyType; - @SuppressWarnings("unchecked") - protected final TypeLiteral getAllBlobsStrategyImplType; - @SuppressWarnings("unchecked") - protected final TypeLiteral getAllBlobMetadataStrategyType; - @SuppressWarnings("unchecked") - protected final TypeLiteral getAllBlobMetadataStrategyImplType; + protected final Map strategyImplMap; - public static class Builder, C extends ContainerMetadata, M extends BlobMetadata, B extends Blob> { - @SuppressWarnings("unused") - private final TypeLiteral connectionType; + public static class Builder> { private final TypeLiteral containerMetadataType; private final TypeLiteral blobMetadataType; private final TypeLiteral blobType; @@ -101,23 +98,33 @@ public class BlobStoreMapsModule extends AbstractModule { private TypeLiteral getAllBlobMetadataStrategyType; @SuppressWarnings("unchecked") private TypeLiteral getAllBlobMetadataStrategyImplType; + @SuppressWarnings("unchecked") + private TypeLiteral containsValueStrategyType; + @SuppressWarnings("unchecked") + private TypeLiteral containsValueStrategyImplType; + @SuppressWarnings("unchecked") + private TypeLiteral clearContainerStrategyType; + @SuppressWarnings("unchecked") + private TypeLiteral clearContainerStrategyImplType; + @SuppressWarnings("unchecked") + private TypeLiteral containerCountStrategyType; + @SuppressWarnings("unchecked") + private TypeLiteral containerCountStrategyImplType; - private Builder(TypeLiteral connectionType, TypeLiteral containerMetadataType, - TypeLiteral blobMetadataType, TypeLiteral blobType) { - this.connectionType = connectionType; + private Builder(TypeLiteral containerMetadataType, TypeLiteral blobMetadataType, + TypeLiteral blobType) { this.containerMetadataType = containerMetadataType; this.blobMetadataType = blobMetadataType; this.blobType = blobType; blobMapFactoryType = TypeLiteral.get(Types.newParameterizedTypeWithOwner(BlobMap.class, BlobMap.Factory.class, blobMetadataType.getType(), blobType.getType())); blobMapImplType = TypeLiteral.get(Types.newParameterizedType(BlobMapImpl.class, - connectionType.getType(), containerMetadataType.getType(), blobMetadataType - .getType(), blobType.getType())); + containerMetadataType.getType(), blobMetadataType.getType(), blobType.getType())); inputStreamMapFactoryType = TypeLiteral.get(Types.newParameterizedTypeWithOwner( InputStreamMap.class, InputStreamMap.Factory.class, blobMetadataType.getType())); inputStreamMapImplType = TypeLiteral.get(Types.newParameterizedType( - InputStreamMapImpl.class, connectionType.getType(), containerMetadataType - .getType(), blobMetadataType.getType(), blobType.getType())); + InputStreamMapImpl.class, containerMetadataType.getType(), blobMetadataType + .getType(), blobType.getType())); getAllBlobsStrategyType = TypeLiteral.get(Types.newParameterizedType( GetAllBlobsStrategy.class, containerMetadataType.getType(), blobMetadataType .getType(), blobType.getType())); @@ -126,35 +133,95 @@ public class BlobStoreMapsModule extends AbstractModule { getAllBlobMetadataStrategyType = TypeLiteral.get(Types.newParameterizedType( GetAllBlobMetadataStrategy.class, containerMetadataType.getType(), blobMetadataType.getType(), blobType.getType())); - getAllBlobMetadataStrategyImplType = TypeLiteral.get(Types.newParameterizedType( - ContainerListGetAllBlobMetadataStrategy.class, containerMetadataType.getType(), - blobMetadataType.getType(), blobType.getType())); + setGetAllBlobMetadataStrategyImpl(ContainerListGetAllBlobMetadataStrategy.class); + + containsValueStrategyType = TypeLiteral.get(Types.newParameterizedType( + ContainsValueStrategy.class, containerMetadataType.getType(), blobMetadataType + .getType(), blobType.getType())); + setContainsValueStrategyImpl(ContentMD5ContainsValueStrategy.class); + + clearContainerStrategyType = TypeLiteral.get(Types.newParameterizedType( + ClearContainerStrategy.class, containerMetadataType.getType(), blobMetadataType + .getType(), blobType.getType())); + setClearContainerStrategyImpl(DeleteAllKeysClearContainerStrategy.class); + containerCountStrategyType = TypeLiteral.get(Types.newParameterizedType( + ContainerCountStrategy.class, containerMetadataType.getType(), blobMetadataType + .getType(), blobType.getType())); + setContainerCountStrategyImpl(KeyCountStrategy.class); } - Builder withGetAllBlobsStrategy(Class getAllBlobsStrategyImplClass) { + public Builder withGetAllBlobsStrategy(Class getAllBlobsStrategyImplClass) { setGetAllBlobsStrategyImpl(getAllBlobsStrategyImplClass); return this; } + public Builder withGetAllBlobMetadataStrategy( + Class getAllBlobMetadataStrategyImplClass) { + setGetAllBlobMetadataStrategyImpl(getAllBlobMetadataStrategyImplClass); + return this; + } + + public Builder withContainsValueStrategy(Class containsValueStrategyImplClass) { + setContainsValueStrategyImpl(containsValueStrategyImplClass); + return this; + } + + public Builder withClearContainerStrategy(Class clearContainerStrategyImplClass) { + setClearContainerStrategyImpl(clearContainerStrategyImplClass); + return this; + } + + public Builder withContainerCountStrategy(Class containerCountStrategyImplClass) { + setContainerCountStrategyImpl(containerCountStrategyImplClass); + return this; + } + private void setGetAllBlobsStrategyImpl(Class getAllBlobsStrategyImplClass) { getAllBlobsStrategyImplType = TypeLiteral.get(Types.newParameterizedType( getAllBlobsStrategyImplClass, containerMetadataType.getType(), blobMetadataType .getType(), blobType.getType())); } - public static , C extends ContainerMetadata, M extends BlobMetadata, B extends Blob> Builder newBuilder( - TypeLiteral connectionType, TypeLiteral containerMetadataType, - TypeLiteral blobMetadataType, TypeLiteral blobType) { - return new Builder(connectionType, containerMetadataType, blobMetadataType, - blobType); + private void setGetAllBlobMetadataStrategyImpl(Class getAllBlobMetadataStrategyImplClass) { + getAllBlobMetadataStrategyImplType = TypeLiteral.get(Types.newParameterizedType( + getAllBlobMetadataStrategyImplClass, containerMetadataType.getType(), + blobMetadataType.getType(), blobType.getType())); } - public BlobStoreMapsModule build() { + private void setContainsValueStrategyImpl(Class containsValueStrategyClass) { + containsValueStrategyImplType = TypeLiteral.get(Types.newParameterizedType( + containsValueStrategyClass, containerMetadataType.getType(), blobMetadataType + .getType(), blobType.getType())); + } + private void setClearContainerStrategyImpl(Class clearContainerStrategyClass) { + clearContainerStrategyImplType = TypeLiteral.get(Types.newParameterizedType( + clearContainerStrategyClass, containerMetadataType.getType(), blobMetadataType + .getType(), blobType.getType())); + } + + private void setContainerCountStrategyImpl(Class containerCountStrategyClass) { + containerCountStrategyImplType = TypeLiteral.get(Types.newParameterizedType( + containerCountStrategyClass, containerMetadataType.getType(), blobMetadataType + .getType(), blobType.getType())); + } + + public static > Builder newBuilder( + TypeLiteral containerMetadataType, TypeLiteral blobMetadataType, + TypeLiteral blobType) { + return new Builder(containerMetadataType, blobMetadataType, blobType); + } + + @SuppressWarnings("unchecked") + public BlobStoreMapsModule build() { + Map strategyImplMap = Maps.newHashMap(); + strategyImplMap.put(getAllBlobsStrategyType, getAllBlobsStrategyImplType); + strategyImplMap.put(getAllBlobMetadataStrategyType, getAllBlobMetadataStrategyImplType); + strategyImplMap.put(containsValueStrategyType, containsValueStrategyImplType); + strategyImplMap.put(clearContainerStrategyType, clearContainerStrategyImplType); + strategyImplMap.put(containerCountStrategyType, containerCountStrategyImplType); return new BlobStoreMapsModule(blobMapFactoryType, blobMapImplType, - inputStreamMapFactoryType, inputStreamMapImplType, getAllBlobsStrategyType, - getAllBlobsStrategyImplType, getAllBlobMetadataStrategyType, - getAllBlobMetadataStrategyImplType); + inputStreamMapFactoryType, inputStreamMapImplType, strategyImplMap); } } @@ -167,9 +234,9 @@ public class BlobStoreMapsModule extends AbstractModule { bind(inputStreamMapFactoryType).toProvider( FactoryProvider.newFactory(inputStreamMapFactoryType, inputStreamMapImplType)).in( Scopes.SINGLETON); - bind(getAllBlobsStrategyType).to(getAllBlobsStrategyImplType).in(Scopes.SINGLETON); - bind(getAllBlobMetadataStrategyType).to(getAllBlobMetadataStrategyImplType).in( - Scopes.SINGLETON); + for (Entry entry : strategyImplMap.entrySet()) { + bind(entry.getKey()).to(entry.getValue()).in(Scopes.SINGLETON); + } } } \ No newline at end of file diff --git a/blobstore/core/src/main/java/org/jclouds/blobstore/binders/BlobBinder.java b/blobstore/core/src/main/java/org/jclouds/blobstore/binders/BlobBinder.java index bcd8f328df..6a591f310d 100644 --- a/blobstore/core/src/main/java/org/jclouds/blobstore/binders/BlobBinder.java +++ b/blobstore/core/src/main/java/org/jclouds/blobstore/binders/BlobBinder.java @@ -23,7 +23,7 @@ */ package org.jclouds.blobstore.binders; -import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.*; import static org.jclouds.blobstore.reference.BlobStoreConstants.PROPERTY_USER_METADATA_PREFIX; import javax.inject.Inject; @@ -45,6 +45,7 @@ public class BlobBinder implements EntityBinder { public void addEntityToRequest(Object entity, HttpRequest request) { Blob object = (Blob) entity; + for (String key : object.getMetadata().getUserMetadata().keySet()) { request.getHeaders().putAll(key.startsWith(metadataPrefix) ? key : metadataPrefix + key, object.getMetadata().getUserMetadata().get(key)); diff --git a/blobstore/core/src/main/java/org/jclouds/blobstore/functions/ClearAndDeleteIfNotEmpty.java b/blobstore/core/src/main/java/org/jclouds/blobstore/functions/ClearAndDeleteIfNotEmpty.java new file mode 100644 index 0000000000..2a96bc7d33 --- /dev/null +++ b/blobstore/core/src/main/java/org/jclouds/blobstore/functions/ClearAndDeleteIfNotEmpty.java @@ -0,0 +1,111 @@ +/** + * + * Copyright (C) 2009 Global Cloud Specialists, Inc. + * + * ==================================================================== + * 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.blobstore.functions; + +import java.lang.reflect.Constructor; +import java.util.concurrent.TimeUnit; + +import javax.inject.Inject; +import javax.inject.Named; + +import org.jclouds.blobstore.BlobStore; +import org.jclouds.blobstore.domain.Blob; +import org.jclouds.blobstore.domain.BlobMetadata; +import org.jclouds.blobstore.domain.ContainerMetadata; +import org.jclouds.blobstore.internal.BlobRuntimeException; +import org.jclouds.blobstore.reference.BlobStoreConstants; +import org.jclouds.blobstore.strategy.ClearContainerStrategy; +import org.jclouds.http.HttpRequest; +import org.jclouds.http.HttpResponseException; +import org.jclouds.rest.RestContext; +import org.jclouds.util.Utils; + +import com.google.common.base.Function; + +public class ClearAndDeleteIfNotEmpty> + implements Function, RestContext { + static final Void v; + static { + Constructor cv; + try { + cv = Void.class.getDeclaredConstructor(); + cv.setAccessible(true); + v = cv.newInstance(); + } catch (Exception e) { + throw new Error("Error setting up class", e); + } + } + /** + * maximum duration of an blob Request + */ + @Inject(optional = true) + @Named(BlobStoreConstants.PROPERTY_BLOBSTORE_TIMEOUT) + protected long requestTimeoutMilliseconds = 30000; + private Object[] args; + private HttpRequest request; + + private final ClearContainerStrategy clear; + private final BlobStore connection; + + @Inject + protected + ClearAndDeleteIfNotEmpty(ClearContainerStrategy clear, BlobStore connection) { + this.clear = clear; + this.connection = connection; + } + + public Void apply(Exception from) { + if (from instanceof HttpResponseException) { + HttpResponseException responseException = (HttpResponseException) from; + if (responseException.getResponse().getStatusCode() == 404) { + return v; + } else if (responseException.getResponse().getStatusCode() == 409) { + clear.execute(connection, args[0].toString()); + try { + connection.deleteContainer(args[0].toString()).get(requestTimeoutMilliseconds, + TimeUnit.MILLISECONDS); + return v; + } catch (Exception e) { + Utils. rethrowIfRuntimeOrSameType(e); + throw new BlobRuntimeException("Error deleting container: " + args[0].toString(), e); + } + } + } + return null; + } + + public Object[] getArgs() { + return args; + } + + public HttpRequest getRequest() { + return request; + } + + public void setContext(HttpRequest request, Object[] args) { + this.request = request; + this.args = args; + } + +} diff --git a/blobstore/core/src/main/java/org/jclouds/blobstore/functions/ObjectMD5.java b/blobstore/core/src/main/java/org/jclouds/blobstore/functions/ObjectMD5.java new file mode 100644 index 0000000000..55fd70f8e1 --- /dev/null +++ b/blobstore/core/src/main/java/org/jclouds/blobstore/functions/ObjectMD5.java @@ -0,0 +1,64 @@ +/** + * + * Copyright (C) 2009 Global Cloud Specialists, Inc. + * + * ==================================================================== + * 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.blobstore.functions; + +import java.io.IOException; + +import javax.inject.Inject; +import javax.inject.Provider; + +import org.jclouds.blobstore.domain.Blob; +import org.jclouds.blobstore.domain.BlobMetadata; +import org.jclouds.blobstore.internal.BlobRuntimeException; + +import com.google.common.base.Function; + +public class ObjectMD5> implements + Function { + + protected final Provider blobFactory; + + @Inject + ObjectMD5(Provider blobFactory) { + this.blobFactory = blobFactory; + } + + public byte[] apply(Object from) { + Blob object; + if (from instanceof Blob) { + object = (Blob) from; + } else { + object = blobFactory.get(); + object.setData(from); + } + if (object.getMetadata().getContentMD5() == null) + try { + object.generateMD5(); + } catch (IOException e) { + throw new BlobRuntimeException("couldn't get MD5 for: " + from, e); + } + return object.getMetadata().getContentMD5(); + } + +} diff --git a/blobstore/core/src/main/java/org/jclouds/blobstore/functions/ReturnTrueOnNotFoundOr404.java b/blobstore/core/src/main/java/org/jclouds/blobstore/functions/ReturnVoidOnNotFoundOr404.java similarity index 69% rename from blobstore/core/src/main/java/org/jclouds/blobstore/functions/ReturnTrueOnNotFoundOr404.java rename to blobstore/core/src/main/java/org/jclouds/blobstore/functions/ReturnVoidOnNotFoundOr404.java index 490a1b2c9a..48295fa5f7 100644 --- a/blobstore/core/src/main/java/org/jclouds/blobstore/functions/ReturnTrueOnNotFoundOr404.java +++ b/blobstore/core/src/main/java/org/jclouds/blobstore/functions/ReturnVoidOnNotFoundOr404.java @@ -23,20 +23,35 @@ */ package org.jclouds.blobstore.functions; +import java.lang.reflect.Constructor; + import org.jclouds.blobstore.ContainerNotFoundException; import org.jclouds.blobstore.KeyNotFoundException; import org.jclouds.http.functions.ReturnTrueOn404; import com.google.common.base.Function; -public class ReturnTrueOnNotFoundOr404 implements Function { +public class ReturnVoidOnNotFoundOr404 implements Function { + + static final Void v; + static { + Constructor cv; + try { + cv = Void.class.getDeclaredConstructor(); + cv.setAccessible(true); + v = cv.newInstance(); + } catch (Exception e) { + throw new Error("Error setting up class", e); + } + } + ReturnTrueOn404 rto404 = new ReturnTrueOn404(); - public Boolean apply(Exception from) { - if (from instanceof KeyNotFoundException||from instanceof ContainerNotFoundException) { - return true; + public Void apply(Exception from) { + if (from instanceof KeyNotFoundException || from instanceof ContainerNotFoundException) { + return v; } else { - return rto404.apply(from); + return rto404.apply(from) ? v : null; } } diff --git a/blobstore/core/src/main/java/org/jclouds/blobstore/internal/BaseBlobMap.java b/blobstore/core/src/main/java/org/jclouds/blobstore/internal/BaseBlobMap.java index ce22298c8f..8c4e5c7b9a 100755 --- a/blobstore/core/src/main/java/org/jclouds/blobstore/internal/BaseBlobMap.java +++ b/blobstore/core/src/main/java/org/jclouds/blobstore/internal/BaseBlobMap.java @@ -26,15 +26,8 @@ package org.jclouds.blobstore.internal; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.util.Arrays; import java.util.Set; import java.util.SortedSet; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; import javax.inject.Inject; import javax.inject.Named; @@ -46,6 +39,9 @@ import org.jclouds.blobstore.domain.Blob; import org.jclouds.blobstore.domain.BlobMetadata; import org.jclouds.blobstore.domain.ContainerMetadata; import org.jclouds.blobstore.reference.BlobStoreConstants; +import org.jclouds.blobstore.strategy.ClearContainerStrategy; +import org.jclouds.blobstore.strategy.ContainerCountStrategy; +import org.jclouds.blobstore.strategy.ContainsValueStrategy; import org.jclouds.blobstore.strategy.GetAllBlobMetadataStrategy; import org.jclouds.blobstore.strategy.GetAllBlobsStrategy; import org.jclouds.rest.BoundedSortedSet; @@ -71,6 +67,9 @@ public abstract class BaseBlobMap blobFactory; protected final GetAllBlobsStrategy getAllBlobs; protected final GetAllBlobMetadataStrategy getAllBlobMetadata; + protected final ContainsValueStrategy containsValueStrategy; + protected final ClearContainerStrategy clearContainerStrategy; + protected final ContainerCountStrategy containerCountStrategy; /** * maximum duration of an blob Request @@ -89,12 +88,18 @@ public abstract class BaseBlobMap connection, Provider blobFactory, GetAllBlobsStrategy getAllBlobs, - GetAllBlobMetadataStrategy getAllBlobMetadata, @Assisted String containerName) { + GetAllBlobMetadataStrategy getAllBlobMetadata, + ContainsValueStrategy containsValueStrategy, + ClearContainerStrategy clearContainerStrategy, + ContainerCountStrategy containerCountStrategy, @Assisted String containerName) { this.connection = checkNotNull(connection, "connection"); this.containerName = checkNotNull(containerName, "container"); this.blobFactory = checkNotNull(blobFactory, "blobFactory"); this.getAllBlobs = checkNotNull(getAllBlobs, "getAllBlobs"); this.getAllBlobMetadata = checkNotNull(getAllBlobMetadata, "getAllBlobMetadata"); + this.containsValueStrategy = checkNotNull(containsValueStrategy, "containsValueStrategy"); + this.clearContainerStrategy = checkNotNull(clearContainerStrategy, "clearContainerStrategy"); + this.containerCountStrategy = checkNotNull(containerCountStrategy, "containerCountStrategy"); checkArgument(!containerName.equals(""), "container name must not be a blank string!"); } @@ -106,30 +111,7 @@ public abstract class BaseBlobMap object; - if (value instanceof Blob) { - object = (Blob) value; - } else { - object = blobFactory.get(); - object.setData(value); - } - if (object.getMetadata().getContentMD5() == null) - object.generateMD5(); - return object.getMetadata().getContentMD5(); + return (int) containerCountStrategy.execute(connection, containerName); } /** @@ -138,9 +120,7 @@ public abstract class BaseBlobMap getAllBlobs() { - return getAllBlobs.execute(connection, containerName); - } /** @@ -150,49 +130,11 @@ public abstract class BaseBlobMap rethrowIfRuntimeOrSameType(e); - throw new BlobRuntimeException(String.format( - "Error searching for ETAG of value: [%2$s] in container:%1$s", containerName, - value), e); - } - } - - public static class BlobRuntimeException extends RuntimeException { - private static final long serialVersionUID = 1L; - - BlobRuntimeException(String s) { - super(s); - } - - public BlobRuntimeException(String s, Throwable throwable) { - super(s, throwable); - } + return containsValueStrategy.execute(connection, containerName, value); } public void clear() { - Set> deletes = Sets.newHashSet(); - for (M md : getAllBlobMetadata.execute(connection, containerName)) { - deletes.add(connection.removeBlob(containerName, md.getKey())); - } - for (Future isdeleted : deletes) { - try { - if (!isdeleted.get(requestTimeoutMilliseconds, TimeUnit.MILLISECONDS)) { - throw new BlobRuntimeException("Failed to delete blob in container: " - + containerName); - } - } catch (Exception e) { - Utils. rethrowIfRuntimeOrSameType(e); - throw new BlobRuntimeException("Error deleting blob in container: " + containerName, e); - } - } + clearContainerStrategy.execute(connection, containerName); } public Set keySet() { @@ -217,7 +159,7 @@ public abstract class BaseBlobMap listContainer() { return getAllBlobMetadata.execute(connection, containerName); } diff --git a/blobstore/core/src/main/java/org/jclouds/blobstore/internal/BlobMapImpl.java b/blobstore/core/src/main/java/org/jclouds/blobstore/internal/BlobMapImpl.java index 2ddf3f8af0..62adc34d94 100755 --- a/blobstore/core/src/main/java/org/jclouds/blobstore/internal/BlobMapImpl.java +++ b/blobstore/core/src/main/java/org/jclouds/blobstore/internal/BlobMapImpl.java @@ -39,6 +39,9 @@ import org.jclouds.blobstore.KeyNotFoundException; import org.jclouds.blobstore.domain.Blob; import org.jclouds.blobstore.domain.BlobMetadata; import org.jclouds.blobstore.domain.ContainerMetadata; +import org.jclouds.blobstore.strategy.ClearContainerStrategy; +import org.jclouds.blobstore.strategy.ContainerCountStrategy; +import org.jclouds.blobstore.strategy.ContainsValueStrategy; import org.jclouds.blobstore.strategy.GetAllBlobMetadataStrategy; import org.jclouds.blobstore.strategy.GetAllBlobsStrategy; import org.jclouds.util.Utils; @@ -54,14 +57,18 @@ import com.google.inject.assistedinject.Assisted; * * @author Adrian Cole */ -public class BlobMapImpl, C extends ContainerMetadata, M extends BlobMetadata, B extends Blob> +public class BlobMapImpl> extends BaseBlobMap implements BlobMap { @Inject - public BlobMapImpl(S connection, Provider blobFactory, + public BlobMapImpl(BlobStore connection, Provider blobFactory, GetAllBlobsStrategy getAllBlobs, - GetAllBlobMetadataStrategy getAllBlobMetadata, @Assisted String containerName) { - super(connection, blobFactory, getAllBlobs, getAllBlobMetadata, containerName); + GetAllBlobMetadataStrategy getAllBlobMetadata, + ContainsValueStrategy containsValueStrategy, + ClearContainerStrategy clearContainerStrategy, + ContainerCountStrategy containerCountStrategy, @Assisted String containerName) { + super(connection, blobFactory, getAllBlobs, getAllBlobMetadata, containsValueStrategy, + clearContainerStrategy, containerCountStrategy, containerName); } /** diff --git a/blobstore/core/src/main/java/org/jclouds/blobstore/internal/BlobRuntimeException.java b/blobstore/core/src/main/java/org/jclouds/blobstore/internal/BlobRuntimeException.java new file mode 100644 index 0000000000..308aa570b1 --- /dev/null +++ b/blobstore/core/src/main/java/org/jclouds/blobstore/internal/BlobRuntimeException.java @@ -0,0 +1,36 @@ +/** + * + * Copyright (C) 2009 Global Cloud Specialists, Inc. + * + * ==================================================================== + * 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.blobstore.internal; + +public class BlobRuntimeException extends RuntimeException { + private static final long serialVersionUID = 1L; + + public BlobRuntimeException(String s) { + super(s); + } + + public BlobRuntimeException(String s, Throwable throwable) { + super(s, throwable); + } +} \ No newline at end of file diff --git a/blobstore/core/src/main/java/org/jclouds/blobstore/internal/InputStreamMapImpl.java b/blobstore/core/src/main/java/org/jclouds/blobstore/internal/InputStreamMapImpl.java index dbcf5c9507..d5a915e4a0 100755 --- a/blobstore/core/src/main/java/org/jclouds/blobstore/internal/InputStreamMapImpl.java +++ b/blobstore/core/src/main/java/org/jclouds/blobstore/internal/InputStreamMapImpl.java @@ -42,6 +42,9 @@ import org.jclouds.blobstore.KeyNotFoundException; import org.jclouds.blobstore.domain.Blob; import org.jclouds.blobstore.domain.BlobMetadata; import org.jclouds.blobstore.domain.ContainerMetadata; +import org.jclouds.blobstore.strategy.ClearContainerStrategy; +import org.jclouds.blobstore.strategy.ContainerCountStrategy; +import org.jclouds.blobstore.strategy.ContainsValueStrategy; import org.jclouds.blobstore.strategy.GetAllBlobMetadataStrategy; import org.jclouds.blobstore.strategy.GetAllBlobsStrategy; import org.jclouds.util.Utils; @@ -59,14 +62,18 @@ import com.google.inject.assistedinject.Assisted; * @see InputStreamMap * @see BaseBlobMap */ -public class InputStreamMapImpl, C extends ContainerMetadata, M extends BlobMetadata, B extends Blob> +public class InputStreamMapImpl> extends BaseBlobMap implements InputStreamMap { @Inject - public InputStreamMapImpl(S connection, Provider blobFactory, + public InputStreamMapImpl(BlobStore connection, Provider blobFactory, GetAllBlobsStrategy getAllBlobs, - GetAllBlobMetadataStrategy getAllBlobMetadata, @Assisted String containerName) { - super(connection, blobFactory, getAllBlobs, getAllBlobMetadata, containerName); + GetAllBlobMetadataStrategy getAllBlobMetadata, + ContainsValueStrategy containsValueStrategy, + ClearContainerStrategy clearContainerStrategy, + ContainerCountStrategy containerCountStrategy, @Assisted String containerName) { + super(connection, blobFactory, getAllBlobs, getAllBlobMetadata, containsValueStrategy, + clearContainerStrategy, containerCountStrategy, containerName); } /** diff --git a/blobstore/core/src/main/java/org/jclouds/blobstore/strategy/ClearContainerStrategy.java b/blobstore/core/src/main/java/org/jclouds/blobstore/strategy/ClearContainerStrategy.java new file mode 100644 index 0000000000..022e4acb88 --- /dev/null +++ b/blobstore/core/src/main/java/org/jclouds/blobstore/strategy/ClearContainerStrategy.java @@ -0,0 +1,40 @@ +/** + * + * Copyright (C) 2009 Global Cloud Specialists, Inc. + * + * ==================================================================== + * 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.blobstore.strategy; + +import org.jclouds.blobstore.BlobStore; +import org.jclouds.blobstore.domain.Blob; +import org.jclouds.blobstore.domain.BlobMetadata; +import org.jclouds.blobstore.domain.ContainerMetadata; + +/** + * Clears a container + * + * @author Adrian Cole + */ +public interface ClearContainerStrategy> { + + void execute(BlobStore connection, String containerName); + +} \ No newline at end of file diff --git a/blobstore/core/src/main/java/org/jclouds/blobstore/strategy/ContainerCountStrategy.java b/blobstore/core/src/main/java/org/jclouds/blobstore/strategy/ContainerCountStrategy.java new file mode 100644 index 0000000000..7e8997f430 --- /dev/null +++ b/blobstore/core/src/main/java/org/jclouds/blobstore/strategy/ContainerCountStrategy.java @@ -0,0 +1,40 @@ +/** + * + * Copyright (C) 2009 Global Cloud Specialists, Inc. + * + * ==================================================================== + * 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.blobstore.strategy; + +import org.jclouds.blobstore.BlobStore; +import org.jclouds.blobstore.domain.Blob; +import org.jclouds.blobstore.domain.BlobMetadata; +import org.jclouds.blobstore.domain.ContainerMetadata; + +/** + * Number of objects in a container + * + * @author Adrian Cole + */ +public interface ContainerCountStrategy> { + + long execute(BlobStore connection, String containerName); + +} \ No newline at end of file diff --git a/blobstore/core/src/main/java/org/jclouds/blobstore/strategy/ContainsValueStrategy.java b/blobstore/core/src/main/java/org/jclouds/blobstore/strategy/ContainsValueStrategy.java new file mode 100644 index 0000000000..c58cc92071 --- /dev/null +++ b/blobstore/core/src/main/java/org/jclouds/blobstore/strategy/ContainsValueStrategy.java @@ -0,0 +1,40 @@ +/** + * + * Copyright (C) 2009 Global Cloud Specialists, Inc. + * + * ==================================================================== + * 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.blobstore.strategy; + +import org.jclouds.blobstore.BlobStore; +import org.jclouds.blobstore.domain.Blob; +import org.jclouds.blobstore.domain.BlobMetadata; +import org.jclouds.blobstore.domain.ContainerMetadata; + +/** + * Determines whether a value exists in the store + * + * @author Adrian Cole + */ +public interface ContainsValueStrategy> { + + boolean execute(BlobStore connection, String containerName, Object value); + +} \ No newline at end of file diff --git a/blobstore/core/src/main/java/org/jclouds/blobstore/strategy/internal/ContainerListGetAllBlobMetadataStrategy.java b/blobstore/core/src/main/java/org/jclouds/blobstore/strategy/internal/ContainerListGetAllBlobMetadataStrategy.java index aaf74df5cf..e469c8e676 100644 --- a/blobstore/core/src/main/java/org/jclouds/blobstore/strategy/internal/ContainerListGetAllBlobMetadataStrategy.java +++ b/blobstore/core/src/main/java/org/jclouds/blobstore/strategy/internal/ContainerListGetAllBlobMetadataStrategy.java @@ -33,7 +33,7 @@ import org.jclouds.blobstore.BlobStore; import org.jclouds.blobstore.domain.Blob; import org.jclouds.blobstore.domain.BlobMetadata; import org.jclouds.blobstore.domain.ContainerMetadata; -import org.jclouds.blobstore.internal.BaseBlobMap.BlobRuntimeException; +import org.jclouds.blobstore.internal.BlobRuntimeException; import org.jclouds.blobstore.reference.BlobStoreConstants; import org.jclouds.blobstore.strategy.GetAllBlobMetadataStrategy; import org.jclouds.util.Utils; diff --git a/blobstore/core/src/main/java/org/jclouds/blobstore/strategy/internal/ContentMD5ContainsValueStrategy.java b/blobstore/core/src/main/java/org/jclouds/blobstore/strategy/internal/ContentMD5ContainsValueStrategy.java new file mode 100644 index 0000000000..8027240d10 --- /dev/null +++ b/blobstore/core/src/main/java/org/jclouds/blobstore/strategy/internal/ContentMD5ContainsValueStrategy.java @@ -0,0 +1,74 @@ +/** + * + * Copyright (C) 2009 Global Cloud Specialists, Inc. + * + * ==================================================================== + * 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.blobstore.strategy.internal; + +import java.util.Arrays; + +import javax.inject.Inject; + +import org.jclouds.blobstore.BlobStore; +import org.jclouds.blobstore.domain.Blob; +import org.jclouds.blobstore.domain.BlobMetadata; +import org.jclouds.blobstore.domain.ContainerMetadata; +import org.jclouds.blobstore.functions.ObjectMD5; +import org.jclouds.blobstore.internal.BlobRuntimeException; +import org.jclouds.blobstore.strategy.ContainsValueStrategy; +import org.jclouds.blobstore.strategy.GetAllBlobMetadataStrategy; +import org.jclouds.util.Utils; + +/** + * Searches Content-MD5 tag for the value associated with the value + * + * @author Adrian Cole + */ +public class ContentMD5ContainsValueStrategy> + implements ContainsValueStrategy { + + protected final ObjectMD5 objectMD5; + protected final GetAllBlobMetadataStrategy getAllBlobMetadata; + + @Inject + private ContentMD5ContainsValueStrategy(ObjectMD5 objectMD5, + GetAllBlobMetadataStrategy getAllBlobMetadata) { + this.objectMD5 = objectMD5; + this.getAllBlobMetadata = getAllBlobMetadata; + } + + public boolean execute(BlobStore connection, String containerName, Object value) { + try { + byte[] toSearch = objectMD5.apply(value); + for (BlobMetadata metadata : getAllBlobMetadata.execute(connection, containerName)) { + if (Arrays.equals(toSearch, metadata.getContentMD5())) + return true; + } + return false; + } catch (Exception e) { + Utils. rethrowIfRuntimeOrSameType(e); + throw new BlobRuntimeException(String.format( + "Error searching for ETAG of value: [%2$s] in container:%1$s", containerName, + value), e); + } + } + +} \ No newline at end of file diff --git a/blobstore/core/src/main/java/org/jclouds/blobstore/strategy/internal/DeleteAllKeysClearContainerStrategy.java b/blobstore/core/src/main/java/org/jclouds/blobstore/strategy/internal/DeleteAllKeysClearContainerStrategy.java new file mode 100644 index 0000000000..f29e1bd6fe --- /dev/null +++ b/blobstore/core/src/main/java/org/jclouds/blobstore/strategy/internal/DeleteAllKeysClearContainerStrategy.java @@ -0,0 +1,80 @@ +/** + * + * Copyright (C) 2009 Global Cloud Specialists, Inc. + * + * ==================================================================== + * 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.blobstore.strategy.internal; + +import java.util.Set; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +import javax.inject.Inject; +import javax.inject.Named; + +import org.jclouds.blobstore.BlobStore; +import org.jclouds.blobstore.domain.Blob; +import org.jclouds.blobstore.domain.BlobMetadata; +import org.jclouds.blobstore.domain.ContainerMetadata; +import org.jclouds.blobstore.internal.BlobRuntimeException; +import org.jclouds.blobstore.reference.BlobStoreConstants; +import org.jclouds.blobstore.strategy.ClearContainerStrategy; +import org.jclouds.blobstore.strategy.GetAllBlobMetadataStrategy; +import org.jclouds.util.Utils; + +import com.google.common.collect.Sets; + +/** + * Deletes all keys in the container + * + * @author Adrian Cole + */ +public class DeleteAllKeysClearContainerStrategy> + implements ClearContainerStrategy { + /** + * maximum duration of an blob Request + */ + @Inject(optional = true) + @Named(BlobStoreConstants.PROPERTY_BLOBSTORE_TIMEOUT) + protected long requestTimeoutMilliseconds = 30000; + protected final GetAllBlobMetadataStrategy getAllBlobMetadata; + + @Inject + DeleteAllKeysClearContainerStrategy(GetAllBlobMetadataStrategy getAllBlobMetadata) { + this.getAllBlobMetadata = getAllBlobMetadata; + } + + public void execute(BlobStore connection, final String containerName) { + Set> deletes = Sets.newHashSet(); + for (M md : getAllBlobMetadata.execute(connection, containerName)) { + deletes.add(connection.removeBlob(containerName, md.getKey())); + } + for (Future isdeleted : deletes) { + try { + isdeleted.get(requestTimeoutMilliseconds, TimeUnit.MILLISECONDS); + } catch (Exception e) { + Utils. rethrowIfRuntimeOrSameType(e); + throw new BlobRuntimeException("Error deleting blob in container: " + containerName, e); + } + } + } + +} \ No newline at end of file diff --git a/blobstore/core/src/main/java/org/jclouds/blobstore/strategy/internal/KeyCountStrategy.java b/blobstore/core/src/main/java/org/jclouds/blobstore/strategy/internal/KeyCountStrategy.java new file mode 100644 index 0000000000..56309e856c --- /dev/null +++ b/blobstore/core/src/main/java/org/jclouds/blobstore/strategy/internal/KeyCountStrategy.java @@ -0,0 +1,53 @@ +/** + * + * Copyright (C) 2009 Global Cloud Specialists, Inc. + * + * ==================================================================== + * 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.blobstore.strategy.internal; + +import javax.inject.Inject; + +import org.jclouds.blobstore.BlobStore; +import org.jclouds.blobstore.domain.Blob; +import org.jclouds.blobstore.domain.BlobMetadata; +import org.jclouds.blobstore.domain.ContainerMetadata; +import org.jclouds.blobstore.strategy.ContainerCountStrategy; +import org.jclouds.blobstore.strategy.GetAllBlobMetadataStrategy; + +/** + * counts all blobs in the blobstore by the most efficient means possible. + * + * @author Adrian Cole + */ +public class KeyCountStrategy> + implements ContainerCountStrategy { + protected final GetAllBlobMetadataStrategy getAllBlobMetadata; + + @Inject + KeyCountStrategy(GetAllBlobMetadataStrategy getAllBlobMetadata) { + this.getAllBlobMetadata = getAllBlobMetadata; + } + + public long execute(BlobStore connection, String container) { + return getAllBlobMetadata.execute(connection, container).size(); + } + +} \ No newline at end of file diff --git a/blobstore/core/src/main/java/org/jclouds/blobstore/strategy/internal/RetryOnNotFoundGetAllBlobsStrategy.java b/blobstore/core/src/main/java/org/jclouds/blobstore/strategy/internal/RetryOnNotFoundGetAllBlobsStrategy.java index 89f0889321..05f38f434a 100644 --- a/blobstore/core/src/main/java/org/jclouds/blobstore/strategy/internal/RetryOnNotFoundGetAllBlobsStrategy.java +++ b/blobstore/core/src/main/java/org/jclouds/blobstore/strategy/internal/RetryOnNotFoundGetAllBlobsStrategy.java @@ -40,7 +40,7 @@ import org.jclouds.blobstore.KeyNotFoundException; import org.jclouds.blobstore.domain.Blob; import org.jclouds.blobstore.domain.BlobMetadata; import org.jclouds.blobstore.domain.ContainerMetadata; -import org.jclouds.blobstore.internal.BaseBlobMap.BlobRuntimeException; +import org.jclouds.blobstore.internal.BlobRuntimeException; import org.jclouds.blobstore.reference.BlobStoreConstants; import org.jclouds.blobstore.strategy.GetAllBlobMetadataStrategy; import org.jclouds.blobstore.strategy.GetAllBlobsStrategy; diff --git a/blobstore/core/src/test/java/org/jclouds/blobstore/BlobStoreMapsModuleTest.java b/blobstore/core/src/test/java/org/jclouds/blobstore/BlobStoreMapsModuleTest.java index 17f50e0847..8c717b86da 100644 --- a/blobstore/core/src/test/java/org/jclouds/blobstore/BlobStoreMapsModuleTest.java +++ b/blobstore/core/src/test/java/org/jclouds/blobstore/BlobStoreMapsModuleTest.java @@ -32,9 +32,15 @@ import org.jclouds.blobstore.domain.ContainerMetadata; import org.jclouds.blobstore.integration.config.StubBlobStoreConnectionModule; import org.jclouds.blobstore.internal.BlobMapImpl; import org.jclouds.blobstore.internal.InputStreamMapImpl; +import org.jclouds.blobstore.strategy.ClearContainerStrategy; +import org.jclouds.blobstore.strategy.ContainerCountStrategy; +import org.jclouds.blobstore.strategy.ContainsValueStrategy; import org.jclouds.blobstore.strategy.GetAllBlobMetadataStrategy; import org.jclouds.blobstore.strategy.GetAllBlobsStrategy; import org.jclouds.blobstore.strategy.internal.ContainerListGetAllBlobMetadataStrategy; +import org.jclouds.blobstore.strategy.internal.ContentMD5ContainsValueStrategy; +import org.jclouds.blobstore.strategy.internal.DeleteAllKeysClearContainerStrategy; +import org.jclouds.blobstore.strategy.internal.KeyCountStrategy; import org.jclouds.blobstore.strategy.internal.RetryOnNotFoundGetAllBlobsStrategy; import org.testng.annotations.Test; @@ -48,54 +54,61 @@ public class BlobStoreMapsModuleTest { public void testBuilderBuild() { BlobStoreMapsModule module = BlobStoreMapsModule.Builder.newBuilder( - new TypeLiteral>>() { - }, new TypeLiteral() { + new TypeLiteral() { }, new TypeLiteral() { }, new TypeLiteral>() { }).build(); assertEquals(module.blobMapFactoryType, new TypeLiteral>>() { }); - assertEquals( - module.blobMapImplType, - new TypeLiteral>, ContainerMetadata, BlobMetadata, Blob>>() { + assertEquals(module.blobMapImplType, + new TypeLiteral>>() { }); assertEquals(module.inputStreamMapFactoryType, new TypeLiteral>() { }); assertEquals( module.inputStreamMapImplType, - new TypeLiteral>, ContainerMetadata, BlobMetadata, Blob>>() { + new TypeLiteral>>() { }); assertEquals( - module.getAllBlobsStrategyType, - new TypeLiteral>>() { - }); - assertEquals( - module.getAllBlobsStrategyImplType, + module.strategyImplMap + .get(new TypeLiteral>>() { + }), new TypeLiteral>>() { }); assertEquals( - module.getAllBlobMetadataStrategyType, - new TypeLiteral>>() { + module.strategyImplMap + .get(new TypeLiteral>>() { + }), + new TypeLiteral>>() { }); assertEquals( - module.getAllBlobMetadataStrategyImplType, - new TypeLiteral>>() { + module.strategyImplMap + .get(new TypeLiteral>>() { + }), + new TypeLiteral>>() { + }); + assertEquals( + module.strategyImplMap + .get(new TypeLiteral>>() { + }), + new TypeLiteral>>() { + }); + assertEquals( + module.strategyImplMap + .get(new TypeLiteral>>() { + }), + new TypeLiteral>>() { }); } public void testInject() { - Injector i = Guice - .createInjector( - new StubBlobStoreConnectionModule(), - BlobStoreMapsModule.Builder - .newBuilder( - new TypeLiteral>>() { - }, new TypeLiteral() { - }, new TypeLiteral() { - }, new TypeLiteral>() { - }).build()); + Injector i = Guice.createInjector(new StubBlobStoreConnectionModule(), + BlobStoreMapsModule.Builder.newBuilder(new TypeLiteral() { + }, new TypeLiteral() { + }, new TypeLiteral>() { + }).build()); assertNotNull(i.getInstance(Key .get(new TypeLiteral>>() { }))); diff --git a/blobstore/core/src/test/java/org/jclouds/blobstore/integration/StubBlobStoreContextBuilder.java b/blobstore/core/src/test/java/org/jclouds/blobstore/integration/StubBlobStoreContextBuilder.java index 693760db26..c003c878cf 100644 --- a/blobstore/core/src/test/java/org/jclouds/blobstore/integration/StubBlobStoreContextBuilder.java +++ b/blobstore/core/src/test/java/org/jclouds/blobstore/integration/StubBlobStoreContextBuilder.java @@ -79,7 +79,7 @@ public class StubBlobStoreContextBuilder Provider> blobProvider, BlobStore> api) { return new BlobStoreContextImpl>, ContainerMetadata, BlobMetadata, Blob>( - blobMapFactory, inputStreamMapFactory, closer, blobProvider, api, URI + blobMapFactory, inputStreamMapFactory, closer, blobProvider, api, api, URI .create("http://localhost/blobstub"), "foo"); } diff --git a/blobstore/core/src/test/java/org/jclouds/blobstore/integration/internal/BaseBlobIntegrationTest.java b/blobstore/core/src/test/java/org/jclouds/blobstore/integration/internal/BaseBlobIntegrationTest.java index f6de16baca..7d5ed0584b 100755 --- a/blobstore/core/src/test/java/org/jclouds/blobstore/integration/internal/BaseBlobIntegrationTest.java +++ b/blobstore/core/src/test/java/org/jclouds/blobstore/integration/internal/BaseBlobIntegrationTest.java @@ -44,7 +44,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.apache.commons.io.IOUtils; -import org.jclouds.blobstore.BlobStore; import org.jclouds.blobstore.ContainerNotFoundException; import org.jclouds.blobstore.domain.Blob; import org.jclouds.blobstore.domain.BlobMetadata; @@ -59,7 +58,7 @@ import org.testng.annotations.Test; /** * @author Adrian Cole */ -public class BaseBlobIntegrationTest, C extends ContainerMetadata, M extends BlobMetadata, B extends Blob> +public class BaseBlobIntegrationTest> extends BaseBlobStoreIntegrationTest { @Test(groups = { "integration", "live" }) @@ -76,12 +75,12 @@ public class BaseBlobIntegrationTest, C extends Con addObjectAndValidateContent(containerName, key); DateTime after = new DateTime().plusSeconds(1); - context.getApi().getBlob(containerName, key, ifModifiedSince(before)).get(10, + context.getBlobStore().getBlob(containerName, key, ifModifiedSince(before)).get(10, TimeUnit.SECONDS); validateContent(containerName, key); try { - context.getApi().getBlob(containerName, key, ifModifiedSince(after)).get(10, + context.getBlobStore().getBlob(containerName, key, ifModifiedSince(after)).get(10, TimeUnit.SECONDS); validateContent(containerName, key); } catch (ExecutionException e) { @@ -112,12 +111,12 @@ public class BaseBlobIntegrationTest, C extends Con addObjectAndValidateContent(containerName, key); DateTime after = new DateTime().plusSeconds(1); - context.getApi().getBlob(containerName, key, ifUnmodifiedSince(after)).get(10, + context.getBlobStore().getBlob(containerName, key, ifUnmodifiedSince(after)).get(10, TimeUnit.SECONDS); validateContent(containerName, key); try { - context.getApi().getBlob(containerName, key, ifUnmodifiedSince(before)).get(10, + context.getBlobStore().getBlob(containerName, key, ifUnmodifiedSince(before)).get(10, TimeUnit.SECONDS); validateContent(containerName, key); } catch (ExecutionException e) { @@ -145,12 +144,12 @@ public class BaseBlobIntegrationTest, C extends Con addObjectAndValidateContent(containerName, key); - context.getApi().getBlob(containerName, key, ifETagMatches(goodETag)).get(10, + context.getBlobStore().getBlob(containerName, key, ifETagMatches(goodETag)).get(10, TimeUnit.SECONDS); validateContent(containerName, key); try { - context.getApi().getBlob(containerName, key, ifETagMatches(badETag)).get(10, + context.getBlobStore().getBlob(containerName, key, ifETagMatches(badETag)).get(10, TimeUnit.SECONDS); validateContent(containerName, key); } catch (ExecutionException e) { @@ -178,12 +177,12 @@ public class BaseBlobIntegrationTest, C extends Con addObjectAndValidateContent(containerName, key); - context.getApi().getBlob(containerName, key, ifETagDoesntMatch(badETag)).get(10, + context.getBlobStore().getBlob(containerName, key, ifETagDoesntMatch(badETag)).get(10, TimeUnit.SECONDS); validateContent(containerName, key); try { - context.getApi().getBlob(containerName, key, ifETagDoesntMatch(goodETag)).get(10, + context.getBlobStore().getBlob(containerName, key, ifETagDoesntMatch(goodETag)).get(10, TimeUnit.SECONDS); validateContent(containerName, key); } catch (ExecutionException e) { @@ -208,13 +207,13 @@ public class BaseBlobIntegrationTest, C extends Con String key = "apples"; addObjectAndValidateContent(containerName, key); - B object1 = context.getApi().getBlob(containerName, key, range(0, 5)).get(10, + B object1 = context.getBlobStore().getBlob(containerName, key, range(0, 5)).get(10, TimeUnit.SECONDS); assertEquals(BlobStoreUtils.getContentAsStringAndClose(object1), TEST_STRING.substring(0, 6)); - B object2 = context.getApi().getBlob(containerName, key, range(6, TEST_STRING.length())) - .get(10, TimeUnit.SECONDS); + B object2 = context.getBlobStore().getBlob(containerName, key, + range(6, TEST_STRING.length())).get(10, TimeUnit.SECONDS); assertEquals(BlobStoreUtils.getContentAsStringAndClose(object2), TEST_STRING.substring(6, TEST_STRING.length())); } finally { @@ -231,7 +230,7 @@ public class BaseBlobIntegrationTest, C extends Con String key = "apples"; addObjectAndValidateContent(containerName, key); - B object = context.getApi().getBlob(containerName, key, + B object = context.getBlobStore().getBlob(containerName, key, range(0, 5).range(6, TEST_STRING.length())).get(10, TimeUnit.SECONDS); assertEquals(BlobStoreUtils.getContentAsStringAndClose(object), TEST_STRING); @@ -249,7 +248,8 @@ public class BaseBlobIntegrationTest, C extends Con String key = "apples"; addObjectAndValidateContent(containerName, key); - B object = context.getApi().getBlob(containerName, key, tail(5)).get(10, TimeUnit.SECONDS); + B object = context.getBlobStore().getBlob(containerName, key, tail(5)).get(10, + TimeUnit.SECONDS); assertEquals(BlobStoreUtils.getContentAsStringAndClose(object), TEST_STRING .substring(TEST_STRING.length() - 5)); assertEquals(object.getContentLength(), 5); @@ -267,7 +267,7 @@ public class BaseBlobIntegrationTest, C extends Con String key = "apples"; addObjectAndValidateContent(containerName, key); - B object = context.getApi().getBlob(containerName, key, startAt(5)).get(10, + B object = context.getBlobStore().getBlob(containerName, key, startAt(5)).get(10, TimeUnit.SECONDS); assertEquals(BlobStoreUtils.getContentAsStringAndClose(object), TEST_STRING.substring(5, TEST_STRING.length())); @@ -289,7 +289,7 @@ public class BaseBlobIntegrationTest, C extends Con String containerName = getContainerName(); String key = "test"; try { - assert context.getApi().removeBlob(containerName, key).get(10, TimeUnit.SECONDS); + context.getBlobStore().removeBlob(containerName, key).get(10, TimeUnit.SECONDS); } finally { returnContainer(containerName); } @@ -305,7 +305,7 @@ public class BaseBlobIntegrationTest, C extends Con String containerName = getContainerName(); try { addBlobToContainer(containerName, key); - assert context.getApi().removeBlob(containerName, key).get(10, TimeUnit.SECONDS); + context.getBlobStore().removeBlob(containerName, key).get(10, TimeUnit.SECONDS); assertContainerEmptyDeleting(containerName, key); } finally { returnContainer(containerName); @@ -314,7 +314,8 @@ public class BaseBlobIntegrationTest, C extends Con private void assertContainerEmptyDeleting(String containerName, String key) throws InterruptedException, ExecutionException, TimeoutException { - SortedSet listing = context.getApi().listBlobs(containerName).get(10, TimeUnit.SECONDS); + SortedSet listing = context.getBlobStore().listBlobs(containerName).get(10, + TimeUnit.SECONDS); assertEquals(listing.size(), 0, String.format( "deleting %s, we still have %s left in container %s, using encoding %s", key, listing.size(), containerName, LOCAL_ENCODING)); @@ -323,7 +324,7 @@ public class BaseBlobIntegrationTest, C extends Con @Test(groups = { "integration", "live" }) public void deleteObjectNoContainer() throws Exception { try { - context.getApi().removeBlob("donb", "test").get(10, TimeUnit.SECONDS); + context.getBlobStore().removeBlob("donb", "test").get(10, TimeUnit.SECONDS); } catch (ExecutionException e) { assert (e.getCause() instanceof HttpResponseException || e.getCause() instanceof ContainerNotFoundException); if (e.getCause() instanceof HttpResponseException) @@ -352,12 +353,14 @@ public class BaseBlobIntegrationTest, C extends Con } String containerName = getContainerName(); try { - assertNotNull(context.getApi().putBlob(containerName, object).get(10, TimeUnit.SECONDS)); - object = context.getApi().getBlob(containerName, object.getKey()) - .get(10, TimeUnit.SECONDS); + assertNotNull(context.getBlobStore().putBlob(containerName, object).get(10, + TimeUnit.SECONDS)); + object = context.getBlobStore().getBlob(containerName, object.getKey()).get(10, + TimeUnit.SECONDS); String returnedString = BlobStoreUtils.getContentAsStringAndClose(object); assertEquals(returnedString, realObject); - assertEquals(context.getApi().listBlobs(containerName).get(10, TimeUnit.SECONDS).size(), 1); + assertEquals(context.getBlobStore().listBlobs(containerName).get(10, TimeUnit.SECONDS) + .size(), 1); } finally { returnContainer(containerName); } @@ -383,7 +386,7 @@ public class BaseBlobIntegrationTest, C extends Con M metadata = newObject.getMetadata(); validateMetadata(metadata); - validateMetadata(context.getApi().blobMetadata(containerName, key)); + validateMetadata(context.getBlobStore().blobMetadata(containerName, key)); } finally { returnContainer(containerName); } diff --git a/blobstore/core/src/test/java/org/jclouds/blobstore/integration/internal/BaseBlobLiveTest.java b/blobstore/core/src/test/java/org/jclouds/blobstore/integration/internal/BaseBlobLiveTest.java index 8f121f57e3..cae8acdff5 100755 --- a/blobstore/core/src/test/java/org/jclouds/blobstore/integration/internal/BaseBlobLiveTest.java +++ b/blobstore/core/src/test/java/org/jclouds/blobstore/integration/internal/BaseBlobLiveTest.java @@ -31,7 +31,6 @@ import java.net.URL; import java.net.URLConnection; import java.util.concurrent.TimeUnit; -import org.jclouds.blobstore.BlobStore; import org.jclouds.blobstore.domain.Blob; import org.jclouds.blobstore.domain.BlobMetadata; import org.jclouds.blobstore.domain.ContainerMetadata; @@ -48,7 +47,7 @@ import org.testng.annotations.Test; * @author Adrian Cole */ @Test(groups = { "live" }, testName = "blobstore.BlobLiveTest") -public class BaseBlobLiveTest, C extends ContainerMetadata, M extends BlobMetadata, B extends Blob> +public class BaseBlobLiveTest> extends BaseBlobStoreIntegrationTest { private static final String sysHttpStreamUrl = System @@ -82,8 +81,8 @@ public class BaseBlobLiveTest, C extends ContainerM object.getMetadata().setSize(length); String bucketName = getContainerName(); try { - context.getApi().putBlob(bucketName, object).get(180, TimeUnit.SECONDS); - assertEquals(context.getApi().blobMetadata(bucketName, key).getContentMD5(), md5); + context.getBlobStore().putBlob(bucketName, object).get(180, TimeUnit.SECONDS); + assertEquals(context.getBlobStore().blobMetadata(bucketName, key).getContentMD5(), md5); } finally { returnContainer(bucketName); } diff --git a/blobstore/core/src/test/java/org/jclouds/blobstore/integration/internal/BaseBlobMapIntegrationTest.java b/blobstore/core/src/test/java/org/jclouds/blobstore/integration/internal/BaseBlobMapIntegrationTest.java index d42481078b..32d6febce0 100755 --- a/blobstore/core/src/test/java/org/jclouds/blobstore/integration/internal/BaseBlobMapIntegrationTest.java +++ b/blobstore/core/src/test/java/org/jclouds/blobstore/integration/internal/BaseBlobMapIntegrationTest.java @@ -37,7 +37,6 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; import org.apache.commons.io.IOUtils; -import org.jclouds.blobstore.BlobStore; import org.jclouds.blobstore.BlobStoreContext; import org.jclouds.blobstore.domain.Blob; import org.jclouds.blobstore.domain.BlobMetadata; @@ -50,7 +49,7 @@ import org.testng.annotations.Test; * * @author Adrian Cole */ -public class BaseBlobMapIntegrationTest, C extends ContainerMetadata, M extends BlobMetadata, B extends Blob> +public class BaseBlobMapIntegrationTest> extends BaseMapIntegrationTest { @Override diff --git a/blobstore/core/src/test/java/org/jclouds/blobstore/integration/internal/BaseBlobStoreIntegrationTest.java b/blobstore/core/src/test/java/org/jclouds/blobstore/integration/internal/BaseBlobStoreIntegrationTest.java index 01b91b9ffc..8cfd208266 100644 --- a/blobstore/core/src/test/java/org/jclouds/blobstore/integration/internal/BaseBlobStoreIntegrationTest.java +++ b/blobstore/core/src/test/java/org/jclouds/blobstore/integration/internal/BaseBlobStoreIntegrationTest.java @@ -27,8 +27,6 @@ import static com.google.common.base.Preconditions.checkNotNull; import static org.testng.Assert.assertEquals; import java.io.IOException; -import java.io.InputStream; -import java.util.Map; import java.util.Set; import java.util.SortedSet; import java.util.concurrent.ArrayBlockingQueue; @@ -41,13 +39,11 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; -import org.jclouds.blobstore.BlobStore; import org.jclouds.blobstore.BlobStoreContext; import org.jclouds.blobstore.domain.Blob; import org.jclouds.blobstore.domain.BlobMetadata; import org.jclouds.blobstore.domain.ContainerMetadata; import org.jclouds.blobstore.util.BlobStoreUtils; -import org.jclouds.http.HttpResponseException; import org.jclouds.http.HttpUtils; import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule; import org.jclouds.util.Utils; @@ -62,7 +58,7 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Sets; import com.google.inject.Module; -public class BaseBlobStoreIntegrationTest, C extends ContainerMetadata, M extends BlobMetadata, B extends Blob> { +public class BaseBlobStoreIntegrationTest> { protected static final String LOCAL_ENCODING = System.getProperty("file.encoding"); protected static final String TEST_STRING = " "; @@ -110,10 +106,10 @@ public class BaseBlobStoreIntegrationTest, C extend protected ExecutorService exec; /** - * we are doing this at a class level, as the context.getApi() object is going to be shared for - * all methods in the class. We don't want to do this for group, as some test classes may want to - * have a different implementation of context.getApi(). For example, one class may want - * non-blocking i/o and another class google appengine. + * we are doing this at a class level, as the context.getBlobStore() object is going to be shared + * for all methods in the class. We don't want to do this for group, as some test classes may + * want to have a different implementation of context.getBlobStore(). For example, one class may + * want non-blocking i/o and another class google appengine. */ @BeforeClass(groups = { "integration", "live" }) public void setUpResourcesOnThisThread(ITestContext testContext) throws Exception { @@ -182,7 +178,7 @@ public class BaseBlobStoreIntegrationTest, C extend try { for (int i = 0; i < 2; i++) { Iterable testContainers = Iterables.filter( - (SortedSet) context.getApi().listContainers(), + (SortedSet) context.getBlobStore().listContainers(), new Predicate() { public boolean apply(ContainerMetadata input) { return input.getName().startsWith(CONTAINER_PREFIX.toLowerCase()); @@ -234,42 +230,8 @@ public class BaseBlobStoreIntegrationTest, C extend protected static void createContainerAndEnsureEmpty(BlobStoreContext context, final String containerName) throws InterruptedException, ExecutionException, TimeoutException { - attemptToCreateContainerButRetryOn409(context, containerName); - emptyContainer(context, containerName); - } - - /** - * 409 could be a resolvable conflict, ex. container delete in progress. FIXME Comment this - * - * @param context - * .getApi() - * @param containerName - * @throws InterruptedException - * @throws TimeoutException - * @throws ExecutionException - */ - private static void attemptToCreateContainerButRetryOn409( - BlobStoreContext, ?, ?, ?> context, - final String containerName) throws InterruptedException, TimeoutException, - ExecutionException { - ExecutionException error = null; - OUTER: for (int i = 0; i < 10; i++) { - try { - context.getApi().createContainer(containerName).get(10, TimeUnit.SECONDS); - break OUTER; - } catch (ExecutionException e) { - error = e; - if (e.getCause() instanceof HttpResponseException) { - HttpResponseException ex = (HttpResponseException) e.getCause(); - if (ex.getResponse().getStatusCode() != 409) { - break OUTER; - } - } - } - Thread.sleep(INCONSISTENCY_WINDOW / 10); - } - if (error != null) - throw error; + context.getBlobStore().createContainer(containerName).get(30, TimeUnit.SECONDS); + context.createInputStreamMap(containerName).clear(); } protected void createContainerAndEnsureEmpty(String containerName) throws InterruptedException, @@ -287,13 +249,13 @@ public class BaseBlobStoreIntegrationTest, C extend protected void addBlobToContainer(String sourceContainer, B object) throws InterruptedException, ExecutionException, TimeoutException, IOException { - context.getApi().putBlob(sourceContainer, object).get(10, TimeUnit.SECONDS); + context.getBlobStore().putBlob(sourceContainer, object).get(10, TimeUnit.SECONDS); } protected B validateContent(String sourceContainer, String key) throws InterruptedException, ExecutionException, TimeoutException, IOException { assertEventuallyContainerSize(sourceContainer, 1); - B newObject = context.getApi().getBlob(sourceContainer, key).get(10, TimeUnit.SECONDS); + B newObject = context.getBlobStore().getBlob(sourceContainer, key).get(10, TimeUnit.SECONDS); assert newObject != null; assertEquals(BlobStoreUtils.getContentAsStringAndClose(newObject), TEST_STRING); return newObject; @@ -304,8 +266,8 @@ public class BaseBlobStoreIntegrationTest, C extend assertEventually(new Runnable() { public void run() { try { - assertEquals(context.getApi().listBlobs(containerName).get(10, TimeUnit.SECONDS) - .size(), count); + assertEquals(context.getBlobStore().listBlobs(containerName).get(10, + TimeUnit.SECONDS).size(), count); } catch (Exception e) { Utils. rethrowIfRuntimeOrSameType(e); } @@ -317,8 +279,7 @@ public class BaseBlobStoreIntegrationTest, C extend TimeoutException { String containerName = containerJsr330.poll(30, TimeUnit.SECONDS); assert containerName != null : "unable to get a container for the test"; - if (!emptyContainer(containerName)) - this.createContainerAndEnsureEmpty(containerName); + createContainerAndEnsureEmpty(containerName); return containerName; } @@ -345,7 +306,7 @@ public class BaseBlobStoreIntegrationTest, C extend * *substantially* slow down tests on a real server over a network. */ if (SANITY_CHECK_RETURNED_BUCKET_NAME) { - if (!Iterables.any(context.getApi().listContainers(), + if (!Iterables.any(context.getBlobStore().listContainers(), new Predicate() { public boolean apply(ContainerMetadata md) { return containerName.equals(md.getName()); @@ -396,60 +357,15 @@ public class BaseBlobStoreIntegrationTest, C extend return new JavaUrlHttpCommandExecutorServiceModule(); } - protected boolean emptyContainer(String name) throws InterruptedException, ExecutionException, - TimeoutException { - return emptyContainer(context, name); - } - - /** - * Remove any objects in a container, leaving it empty. - * - * @return - */ - protected static boolean emptyContainer(final BlobStoreContext context, + protected static void deleteContainer(final BlobStoreContext context, final String name) throws InterruptedException, ExecutionException, TimeoutException { - if (context.getApi().containerExists(name)) { - // This can fail to be zero length because of stale container lists. Ex. - // context.getApi().listContainer() - // could return 9 keys, when there are 10. When all the deletions finish, one entry would - // be left in this case. Instead of failing, we will attempt this entire container deletion - // operation multiple times to ensure we can acheive a zero length container. - assertEventually(new Runnable() { - public void run() { - try { - Map map = context.createInputStreamMap(name); - Set keys = map.keySet(); - if (keys.size() > 0) { - map.clear(); - assertEquals( - map.size(), - 0, - String - .format( - "deleting %s, we still have %s left in container %s, using encoding %s", - keys, map.keySet(), name, LOCAL_ENCODING)); - } - } catch (Exception e) { - Utils. rethrowIfRuntimeOrSameType(e); - } - } - }); - return true; - } - return false; - } - - protected static void deleteContainer( - final BlobStoreContext, ?, ?, ?> context, final String name) - throws InterruptedException, ExecutionException, TimeoutException { - if (context.getApi().containerExists(name)) { + if (context.getBlobStore().containerExists(name)) { System.err.printf("*** deleting container %s...%n", name); - emptyContainer(context, name); - context.getApi().deleteContainer(name).get(10, TimeUnit.SECONDS); + context.getBlobStore().deleteContainer(name).get(10, TimeUnit.SECONDS); assertEventually(new Runnable() { public void run() { try { - assert !context.getApi().containerExists(name) : "container " + name + assert !context.getBlobStore().containerExists(name) : "container " + name + " still exists"; } catch (Exception e) { Utils. rethrowIfRuntimeOrSameType(e); diff --git a/blobstore/core/src/test/java/org/jclouds/blobstore/integration/internal/BaseContainerIntegrationTest.java b/blobstore/core/src/test/java/org/jclouds/blobstore/integration/internal/BaseContainerIntegrationTest.java index 96691df96b..110dc9e369 100755 --- a/blobstore/core/src/test/java/org/jclouds/blobstore/integration/internal/BaseContainerIntegrationTest.java +++ b/blobstore/core/src/test/java/org/jclouds/blobstore/integration/internal/BaseContainerIntegrationTest.java @@ -31,7 +31,6 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import org.jclouds.blobstore.BlobStore; import org.jclouds.blobstore.domain.Blob; import org.jclouds.blobstore.domain.BlobMetadata; import org.jclouds.blobstore.domain.ContainerMetadata; @@ -41,20 +40,20 @@ import org.testng.annotations.Test; /** * @author Adrian Cole */ -public class BaseContainerIntegrationTest, C extends ContainerMetadata, M extends BlobMetadata, B extends Blob> +public class BaseContainerIntegrationTest> extends BaseBlobStoreIntegrationTest { @Test(groups = { "integration", "live" }) public void containerDoesntExist() throws Exception { - assert !context.getApi().containerExists("forgetaboutit"); + assert !context.getBlobStore().containerExists("forgetaboutit"); } @Test(groups = { "integration", "live" }) public void testPutTwiceIsOk() throws Exception { String containerName = getContainerName(); try { - context.getApi().createContainer(containerName).get(10, TimeUnit.SECONDS); - context.getApi().createContainer(containerName).get(10, TimeUnit.SECONDS); + context.getBlobStore().createContainer(containerName).get(10, TimeUnit.SECONDS); + context.getBlobStore().createContainer(containerName).get(10, TimeUnit.SECONDS); } finally { returnContainer(containerName); } @@ -64,28 +63,21 @@ public class BaseContainerIntegrationTest, C extend public void containerExists() throws Exception { String containerName = getContainerName(); try { - assert context.getApi().containerExists(containerName); + assert context.getBlobStore().containerExists(containerName); } finally { returnContainer(containerName); } } - /** - * this method overrides containerName to ensure it isn't found - */ @Test(groups = { "integration", "live" }) - public void deleteContainerIfEmptyNotFound() throws Exception { - assert context.getApi().deleteContainer("dbienf").get(10, TimeUnit.SECONDS); - } - - @Test(groups = { "integration", "live" }) - public void deleteContainerIfEmptyButHasContents() throws Exception { + public void deleteContainerWithContents() throws Exception { String containerName = getContainerName(); try { addBlobToContainer(containerName, "test"); - assert !context.getApi().deleteContainer(containerName).get(10, TimeUnit.SECONDS); + context.getBlobStore().deleteContainer(containerName).get(10, TimeUnit.SECONDS); + assertNotExists(containerName); } finally { - returnContainer(containerName); + recycleContainer(containerName); } } @@ -93,31 +85,25 @@ public class BaseContainerIntegrationTest, C extend public void deleteContainerIfEmpty() throws Exception { final String containerName = getContainerName(); try { - assert context.getApi().deleteContainer(containerName).get(10, TimeUnit.SECONDS); - - assertEventually(new Runnable() { - public void run() { - try { - assert !context.getApi().containerExists(containerName) : "container " - + containerName + " still exists"; - } catch (Exception e) { - Utils. rethrowIfRuntimeOrSameType(e); - } - } - }); + context.getBlobStore().deleteContainer(containerName).get(10, TimeUnit.SECONDS); + assertNotExists(containerName); } finally { // this container is now deleted, so we can't reuse it directly recycleContainer(containerName); } } - protected void addAlphabetUnderRoot(String containerName) throws InterruptedException, - ExecutionException, TimeoutException { - for (char letter = 'a'; letter <= 'z'; letter++) { - B blob = context.newBlob(letter + ""); - blob.setData(letter + "content"); - context.getApi().putBlob(containerName, blob).get(10, TimeUnit.SECONDS); - } + private void assertNotExists(final String containerName) throws InterruptedException { + assertEventually(new Runnable() { + public void run() { + try { + assert !context.getBlobStore().containerExists(containerName) : "container " + + containerName + " still exists"; + } catch (Exception e) { + Utils. rethrowIfRuntimeOrSameType(e); + } + } + }); } @Test(groups = { "integration", "live" }) @@ -126,7 +112,7 @@ public class BaseContainerIntegrationTest, C extend String containerName = getContainerName(); try { add15UnderRoot(containerName); - SortedSet container = context.getApi().listBlobs(containerName).get(10, + SortedSet container = context.getBlobStore().listBlobs(containerName).get(10, TimeUnit.SECONDS); assertEquals(container.size(), 15); } finally { @@ -135,12 +121,21 @@ public class BaseContainerIntegrationTest, C extend } + protected void addAlphabetUnderRoot(String containerName) throws InterruptedException, + ExecutionException, TimeoutException { + for (char letter = 'a'; letter <= 'z'; letter++) { + B blob = context.newBlob(letter + ""); + blob.setData(letter + "content"); + context.getBlobStore().putBlob(containerName, blob).get(10, TimeUnit.SECONDS); + } + } + protected void add15UnderRoot(String containerName) throws InterruptedException, ExecutionException, TimeoutException { for (int i = 0; i < 15; i++) { B blob = context.newBlob(i + ""); blob.setData(i + "content"); - context.getApi().putBlob(containerName, blob).get(10, TimeUnit.SECONDS); + context.getBlobStore().putBlob(containerName, blob).get(10, TimeUnit.SECONDS); } } @@ -149,7 +144,7 @@ public class BaseContainerIntegrationTest, C extend for (int i = 0; i < 10; i++) { B blob = context.newBlob(prefix + "/" + i); blob.setData(i + "content"); - context.getApi().putBlob(containerName, blob).get(10, TimeUnit.SECONDS); + context.getBlobStore().putBlob(containerName, blob).get(10, TimeUnit.SECONDS); } } } \ No newline at end of file diff --git a/blobstore/core/src/test/java/org/jclouds/blobstore/integration/internal/BaseContainerLiveTest.java b/blobstore/core/src/test/java/org/jclouds/blobstore/integration/internal/BaseContainerLiveTest.java index b98c37dbc3..45b4cfc0cb 100755 --- a/blobstore/core/src/test/java/org/jclouds/blobstore/integration/internal/BaseContainerLiveTest.java +++ b/blobstore/core/src/test/java/org/jclouds/blobstore/integration/internal/BaseContainerLiveTest.java @@ -23,7 +23,6 @@ */ package org.jclouds.blobstore.integration.internal; -import org.jclouds.blobstore.BlobStore; import org.jclouds.blobstore.domain.Blob; import org.jclouds.blobstore.domain.BlobMetadata; import org.jclouds.blobstore.domain.ContainerMetadata; @@ -32,7 +31,7 @@ import org.jclouds.blobstore.domain.ContainerMetadata; * * @author Adrian Cole */ -public class BaseContainerLiveTest, C extends ContainerMetadata, M extends BlobMetadata, B extends Blob> +public class BaseContainerLiveTest> extends BaseBlobStoreIntegrationTest { } \ No newline at end of file diff --git a/blobstore/core/src/test/java/org/jclouds/blobstore/integration/internal/BaseInputStreamMapIntegrationTest.java b/blobstore/core/src/test/java/org/jclouds/blobstore/integration/internal/BaseInputStreamMapIntegrationTest.java index 4d6f4e1e5e..4c31d1d67b 100755 --- a/blobstore/core/src/test/java/org/jclouds/blobstore/integration/internal/BaseInputStreamMapIntegrationTest.java +++ b/blobstore/core/src/test/java/org/jclouds/blobstore/integration/internal/BaseInputStreamMapIntegrationTest.java @@ -37,7 +37,6 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; import org.apache.commons.io.IOUtils; -import org.jclouds.blobstore.BlobStore; import org.jclouds.blobstore.BlobStoreContext; import org.jclouds.blobstore.InputStreamMap; import org.jclouds.blobstore.domain.Blob; @@ -51,7 +50,7 @@ import org.testng.annotations.Test; * * @author Adrian Cole */ -public class BaseInputStreamMapIntegrationTest, C extends ContainerMetadata, M extends BlobMetadata, B extends Blob> +public class BaseInputStreamMapIntegrationTest> extends BaseMapIntegrationTest { @Override diff --git a/blobstore/core/src/test/java/org/jclouds/blobstore/integration/internal/BaseMapIntegrationTest.java b/blobstore/core/src/test/java/org/jclouds/blobstore/integration/internal/BaseMapIntegrationTest.java index 38d082df3c..49499ff67d 100755 --- a/blobstore/core/src/test/java/org/jclouds/blobstore/integration/internal/BaseMapIntegrationTest.java +++ b/blobstore/core/src/test/java/org/jclouds/blobstore/integration/internal/BaseMapIntegrationTest.java @@ -37,7 +37,6 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; import org.apache.commons.io.IOUtils; -import org.jclouds.blobstore.BlobStore; import org.jclouds.blobstore.BlobStoreContext; import org.jclouds.blobstore.ListableMap; import org.jclouds.blobstore.domain.Blob; @@ -51,7 +50,7 @@ import org.testng.annotations.Test; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; -public abstract class BaseMapIntegrationTest, C extends ContainerMetadata, M extends BlobMetadata, B extends Blob, V> +public abstract class BaseMapIntegrationTest, V> extends BaseBlobStoreIntegrationTest { public abstract void testPutAll() throws InterruptedException, ExecutionException, diff --git a/blobstore/core/src/test/java/org/jclouds/blobstore/integration/internal/BaseServiceIntegrationTest.java b/blobstore/core/src/test/java/org/jclouds/blobstore/integration/internal/BaseServiceIntegrationTest.java index 9b8b91f0f5..f5e81d0cd3 100755 --- a/blobstore/core/src/test/java/org/jclouds/blobstore/integration/internal/BaseServiceIntegrationTest.java +++ b/blobstore/core/src/test/java/org/jclouds/blobstore/integration/internal/BaseServiceIntegrationTest.java @@ -25,7 +25,6 @@ package org.jclouds.blobstore.integration.internal; import java.util.SortedSet; -import org.jclouds.blobstore.BlobStore; import org.jclouds.blobstore.domain.Blob; import org.jclouds.blobstore.domain.BlobMetadata; import org.jclouds.blobstore.domain.ContainerMetadata; @@ -35,12 +34,12 @@ import org.testng.annotations.Test; * * @author Adrian Cole */ -public class BaseServiceIntegrationTest, C extends ContainerMetadata, M extends BlobMetadata, B extends Blob> +public class BaseServiceIntegrationTest> extends BaseBlobStoreIntegrationTest { @Test(groups = { "integration", "live" }) void containerDoesntExist() throws Exception { - SortedSet list = context.getApi().listContainers(); + SortedSet list = context.getBlobStore().listContainers(); assert !list.contains(new ContainerMetadata("shouldntexist")); } diff --git a/blobstore/core/src/test/java/org/jclouds/blobstore/integration/internal/BaseTestInitializer.java b/blobstore/core/src/test/java/org/jclouds/blobstore/integration/internal/BaseTestInitializer.java index 85ae6ef713..0936e98c6f 100644 --- a/blobstore/core/src/test/java/org/jclouds/blobstore/integration/internal/BaseTestInitializer.java +++ b/blobstore/core/src/test/java/org/jclouds/blobstore/integration/internal/BaseTestInitializer.java @@ -23,7 +23,6 @@ */ package org.jclouds.blobstore.integration.internal; -import org.jclouds.blobstore.BlobStore; import org.jclouds.blobstore.BlobStoreContext; import org.jclouds.blobstore.domain.Blob; import org.jclouds.blobstore.domain.BlobMetadata; @@ -32,7 +31,7 @@ import org.testng.ITestContext; import com.google.inject.Module; -public abstract class BaseTestInitializer, C extends ContainerMetadata, M extends BlobMetadata, B extends Blob> { +public abstract class BaseTestInitializer> { public BlobStoreContext init(Module configurationModule, ITestContext testContext) throws Exception { diff --git a/blobstore/core/src/test/java/org/jclouds/blobstore/integration/internal/StubBlobStore.java b/blobstore/core/src/test/java/org/jclouds/blobstore/integration/internal/StubBlobStore.java index be7a6e6754..4d427aaa38 100755 --- a/blobstore/core/src/test/java/org/jclouds/blobstore/integration/internal/StubBlobStore.java +++ b/blobstore/core/src/test/java/org/jclouds/blobstore/integration/internal/StubBlobStore.java @@ -199,18 +199,29 @@ public class StubBlobStore removeBlob(final String container, final String key) { - return new FutureBase() { - public Boolean get() throws InterruptedException, ExecutionException { + public Future removeBlob(final String container, final String key) { + return new FutureBase() { + public Void get() throws InterruptedException, ExecutionException { if (getContainerToBlobs().containsKey(container)) { getContainerToBlobs().get(container).remove(key); } - return true; + return null; } }; } - public Future deleteContainer(final String container) { + public Future deleteContainer(final String container) { + return new FutureBase() { + public Void get() throws InterruptedException, ExecutionException { + if (getContainerToBlobs().containsKey(container)) { + getContainerToBlobs().remove(container); + } + return null; + } + }; + } + + public Future deleteContainerImpl(final String container) { return new FutureBase() { public Boolean get() throws InterruptedException, ExecutionException { if (getContainerToBlobs().containsKey(container)) { @@ -385,6 +396,7 @@ public class StubBlobStore>, ContainerMetadata, BlobMetadata, Blob> context; - InputStreamMapImpl>, ContainerMetadata, BlobMetadata, Blob> map; + InputStreamMapImpl> map; @SuppressWarnings("unchecked") @BeforeClass void addDefaultObjectsSoThatTestsWillPass() { context = new StubBlobStoreContextBuilder().buildContext(); - map = (InputStreamMapImpl>, ContainerMetadata, BlobMetadata, Blob>) context + map = (InputStreamMapImpl>) context .createInputStreamMap("test"); } diff --git a/core/src/main/java/org/jclouds/http/functions/ParseContentMD5FromHeaders.java b/core/src/main/java/org/jclouds/http/functions/ParseContentMD5FromHeaders.java new file mode 100644 index 0000000000..a623716cc0 --- /dev/null +++ b/core/src/main/java/org/jclouds/http/functions/ParseContentMD5FromHeaders.java @@ -0,0 +1,91 @@ +/** + * + * Copyright (C) 2009 Global Cloud Specialists, Inc. + * + * ==================================================================== + * 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.http.functions; + +import javax.annotation.Resource; + +import org.apache.commons.io.IOUtils; +import org.jclouds.http.HttpRequest; +import org.jclouds.http.HttpResponse; +import org.jclouds.http.HttpUtils; +import org.jclouds.logging.Logger; +import org.jclouds.rest.RestContext; + +import com.google.common.base.Function; + +/** + * @author Adrian Cole + */ +public class ParseContentMD5FromHeaders implements Function, RestContext { + + public static class NoContentMD5Exception extends RuntimeException { + + private static final long serialVersionUID = 1L; + private final HttpRequest request; + private final HttpResponse response; + + public NoContentMD5Exception(HttpRequest request, HttpResponse response) { + super(String.format("no MD5 returned from request: %s; response %s", request, response)); + this.request = request; + this.response = response; + } + + public HttpRequest getRequest() { + return request; + } + + public HttpResponse getResponse() { + return response; + } + + } + + @Resource + protected Logger logger = Logger.NULL; + private Object[] args; + private HttpRequest request; + + public byte[] apply(HttpResponse from) { + IOUtils.closeQuietly(from.getContent()); + String contentMD5 = from.getFirstHeaderOrNull("Content-MD5"); + if (contentMD5 != null) { + return HttpUtils.fromBase64String(contentMD5); + } + throw new NoContentMD5Exception(request, from); + } + + public Object[] getArgs() { + return args; + } + + public HttpRequest getRequest() { + return request; + } + + public void setContext(HttpRequest request, Object[] args) { + this.request = request; + this.args = args; + } + +} \ No newline at end of file diff --git a/core/src/main/java/org/jclouds/http/functions/ParseURIList.java b/core/src/main/java/org/jclouds/http/functions/ParseURIList.java new file mode 100644 index 0000000000..ee42eb5ff8 --- /dev/null +++ b/core/src/main/java/org/jclouds/http/functions/ParseURIList.java @@ -0,0 +1,56 @@ +/** + * + * Copyright (C) 2009 Global Cloud Specialists, Inc. + * + * ==================================================================== + * 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.http.functions; + +import static com.google.common.base.Preconditions.checkState; + +import java.io.IOException; +import java.net.URI; + +import javax.ws.rs.core.HttpHeaders; + +import org.jclouds.http.HttpResponse; +import org.jclouds.http.HttpResponseException; +import org.jclouds.util.Utils; + +import com.google.common.base.Function; + +/** + * parses a single URI from a list + * + * @author Adrian Cole + */ +public class ParseURIList implements Function { + public URI apply(HttpResponse from) { + try { + checkState("text/uri-list".equals(from.getFirstHeaderOrNull(HttpHeaders.CONTENT_TYPE)), + "response should have content type test/uri-list"); + + String toParse = Utils.toStringAndClose(from.getContent()); + return URI.create(toParse.trim()); + } catch (IOException e) { + throw new HttpResponseException("couldn't parse url from response", null, from, e); + } + } +} \ No newline at end of file diff --git a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/functions/ReturnTrueIfContainerNotFound.java b/core/src/main/java/org/jclouds/http/functions/ReturnInputStream.java similarity index 77% rename from mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/functions/ReturnTrueIfContainerNotFound.java rename to core/src/main/java/org/jclouds/http/functions/ReturnInputStream.java index bcbcee3bb6..12225fc7ee 100644 --- a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/functions/ReturnTrueIfContainerNotFound.java +++ b/core/src/main/java/org/jclouds/http/functions/ReturnInputStream.java @@ -21,23 +21,22 @@ * under the License. * ==================================================================== */ -package org.jclouds.mezeo.pcs2.functions; +package org.jclouds.http.functions; -import org.jclouds.blobstore.ContainerNotFoundException; +import java.io.InputStream; + +import org.jclouds.http.HttpResponse; import com.google.common.base.Function; /** + * Simply returns the InputStream of the response * * @author Adrian Cole */ -public class ReturnTrueIfContainerNotFound implements Function { +public class ReturnInputStream implements Function { - public Boolean apply(Exception from) { - if (from instanceof ContainerNotFoundException) { - return true; - } - return null; + public InputStream apply(HttpResponse from) { + return from.getContent(); } - -} +} \ No newline at end of file diff --git a/core/src/main/java/org/jclouds/http/functions/ReturnVoidIf2xx.java b/core/src/main/java/org/jclouds/http/functions/ReturnVoidIf2xx.java new file mode 100644 index 0000000000..adcb6c5990 --- /dev/null +++ b/core/src/main/java/org/jclouds/http/functions/ReturnVoidIf2xx.java @@ -0,0 +1,59 @@ +/** + * + * Copyright (C) 2009 Global Cloud Specialists, Inc. + * + * ==================================================================== + * 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.http.functions; + +import java.lang.reflect.Constructor; + +import org.apache.commons.io.IOUtils; +import org.jclouds.http.HttpResponse; + +import com.google.common.base.Function; + +/** + * Simply returns true when the http response code is in the range 200-299. + * + * @author Adrian Cole + */ +public class ReturnVoidIf2xx implements Function { + static final Void v; + static { + Constructor cv; + try { + cv = Void.class.getDeclaredConstructor(); + cv.setAccessible(true); + v = cv.newInstance(); + } catch (Exception e) { + throw new Error("Error setting up class", e); + } + } + + public Void apply(HttpResponse from) { + IOUtils.closeQuietly(from.getContent()); + int code = from.getStatusCode(); + if (code >= 300 || code < 200) { + throw new IllegalStateException("incorrect code for this operation: " + from); + } + return v; + } +} \ No newline at end of file diff --git a/core/src/main/java/org/jclouds/http/handlers/BackoffLimitedRetryHandler.java b/core/src/main/java/org/jclouds/http/handlers/BackoffLimitedRetryHandler.java index 8233a7d0f8..e036a664be 100644 --- a/core/src/main/java/org/jclouds/http/handlers/BackoffLimitedRetryHandler.java +++ b/core/src/main/java/org/jclouds/http/handlers/BackoffLimitedRetryHandler.java @@ -103,7 +103,12 @@ public class BackoffLimitedRetryHandler implements HttpRetryHandler { } public void imposeBackoffExponentialDelay(int failureCount, String commandDescription) { - long delayMs = (long) (50L * Math.pow(failureCount, 2)); + imposeBackoffExponentialDelay(50L, 2, failureCount, commandDescription); + } + + public void imposeBackoffExponentialDelay(long period, int pow, int failureCount, + String commandDescription) { + long delayMs = (long) (period * Math.pow(failureCount, pow)); logger.debug("Retry %1$d/%2$d after server error, delaying for %3$d ms: %4$s", failureCount, retryCountLimit, delayMs, commandDescription); try { diff --git a/core/src/main/java/org/jclouds/rest/JaxrsAnnotationProcessor.java b/core/src/main/java/org/jclouds/rest/JaxrsAnnotationProcessor.java index e6ceb1eaa1..c7a791d510 100644 --- a/core/src/main/java/org/jclouds/rest/JaxrsAnnotationProcessor.java +++ b/core/src/main/java/org/jclouds/rest/JaxrsAnnotationProcessor.java @@ -27,6 +27,7 @@ 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.InputStream; import java.io.UnsupportedEncodingException; import java.lang.annotation.Annotation; import java.lang.reflect.Method; @@ -56,8 +57,11 @@ import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpRequestFilter; import org.jclouds.http.HttpResponse; import org.jclouds.http.functions.ParseSax; +import org.jclouds.http.functions.ParseURIList; +import org.jclouds.http.functions.ReturnInputStream; import org.jclouds.http.functions.ReturnStringIf200; import org.jclouds.http.functions.ReturnTrueIf2xx; +import org.jclouds.http.functions.ReturnVoidIf2xx; import org.jclouds.http.functions.ParseSax.HandlerWithResult; import org.jclouds.http.options.HttpRequestOptions; import org.jclouds.logging.Logger; @@ -414,6 +418,13 @@ public class JaxrsAnnotationProcessor { public static final TypeLiteral> futureStringLiteral = new TypeLiteral>() { }; + public static final TypeLiteral> futureVoidLiteral = new TypeLiteral>() { + }; + public static final TypeLiteral> futureURILiteral = new TypeLiteral>() { + }; + public static final TypeLiteral> futureInputStreamLiteral = new TypeLiteral>() { + }; + public static Class> getParserOrThrowException(Method method) { ResponseParser annotation = method.getAnnotation(ResponseParser.class); if (annotation == null) { @@ -424,6 +435,16 @@ public class JaxrsAnnotationProcessor { } else if (method.getReturnType().equals(String.class) || TypeLiteral.get(method.getGenericReturnType()).equals(futureStringLiteral)) { return ReturnStringIf200.class; + } else if (method.getReturnType().equals(void.class) + || TypeLiteral.get(method.getGenericReturnType()).equals(futureVoidLiteral)) { + return ReturnVoidIf2xx.class; + } else if (method.getReturnType().equals(URI.class) + || TypeLiteral.get(method.getGenericReturnType()).equals(futureURILiteral)) { + return ParseURIList.class; + } else if (method.getReturnType().equals(InputStream.class) + || TypeLiteral.get(method.getGenericReturnType()) + .equals(futureInputStreamLiteral)) { + return ReturnInputStream.class; } else { throw new IllegalStateException( "You must specify a ResponseTransformer annotation on: " + method.toString()); @@ -534,10 +555,14 @@ public class JaxrsAnnotationProcessor { .iterator().next(); EntityParam entityAnnotation = (EntityParam) entry.getValue().iterator().next(); - Object entity = args[entry.getKey()]; EntityBinder binder = injector.getInstance(entityAnnotation.value()); - - binder.addEntityToRequest(entity, request); + Object entity = args[entry.getKey()]; + if (entity.getClass().isArray()) { + Object[] entityArray = (Object[]) entity; + entity = entityArray.length > 0 ? entityArray[0] : null; + } + if (entity != null) + binder.addEntityToRequest(entity, request); } else if (indexToEntityAnnotation.size() > 1) { throw new IllegalStateException("cannot have multiple @Entity annotations on " + method); diff --git a/core/src/main/java/org/jclouds/rest/RestClientProxy.java b/core/src/main/java/org/jclouds/rest/RestClientProxy.java index 17a91cea2a..f82c5e03a1 100644 --- a/core/src/main/java/org/jclouds/rest/RestClientProxy.java +++ b/core/src/main/java/org/jclouds/rest/RestClientProxy.java @@ -86,9 +86,16 @@ public class RestClientProxy implements InvocationHandler { logger.trace("%s - converting method to request", method); Function exceptionParser = util .createExceptionParserOrNullIfNotFound(method); + // in case there is an exception creating the request, we should at least pass in args + if (exceptionParser instanceof RestContext) { + ((RestContext) exceptionParser).setContext(null, args); + } HttpRequest request; try { request = util.createRequest(method, args); + if (exceptionParser instanceof RestContext) { + ((RestContext) exceptionParser).setContext(request, args); + } } catch (RuntimeException e) { if (exceptionParser != null) { final Object toReturn = exceptionParser.apply(e); diff --git a/core/src/test/java/org/jclouds/rest/JaxrsAnnotationProcessorTest.java b/core/src/test/java/org/jclouds/rest/JaxrsAnnotationProcessorTest.java index 7d4eb0b662..410b2de375 100644 --- a/core/src/test/java/org/jclouds/rest/JaxrsAnnotationProcessorTest.java +++ b/core/src/test/java/org/jclouds/rest/JaxrsAnnotationProcessorTest.java @@ -26,11 +26,13 @@ package org.jclouds.rest; import static com.google.common.base.Preconditions.checkNotNull; import static org.testng.Assert.assertEquals; +import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import java.lang.reflect.Array; import java.lang.reflect.Method; import java.net.URI; import java.net.URLEncoder; @@ -57,11 +59,15 @@ import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpRequestFilter; import org.jclouds.http.HttpResponse; import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule; +import org.jclouds.http.functions.ParseURIList; +import org.jclouds.http.functions.ReturnInputStream; import org.jclouds.http.functions.ReturnStringIf200; import org.jclouds.http.functions.ReturnTrueIf2xx; +import org.jclouds.http.functions.ReturnVoidIf2xx; import org.jclouds.http.options.BaseHttpRequestOptions; import org.jclouds.http.options.GetOptions; import org.jclouds.http.options.HttpRequestOptions; +import org.jclouds.rest.binders.HttpRequestOptionsBinder; import org.jclouds.rest.binders.JsonBinder; import org.jclouds.rest.binders.MapEntityBinder; import org.jclouds.rest.config.JaxrsModule; @@ -157,6 +163,65 @@ public class JaxrsAnnotationProcessorTest { assertEquals(httpMethod.getMethod(), "FOO"); } + @Endpoint(Localhost.class) + public class TestEntityParamVarargs { + @POST + public void varargs( + @EntityParam(HttpRequestOptionsBinder.class) HttpRequestOptions... options) { + } + + @POST + public void post(@EntityParam(HttpRequestOptionsBinder.class) HttpRequestOptions options) { + } + } + + public void testHttpRequestOptionsEntityParam() throws SecurityException, NoSuchMethodException { + Method method = TestEntityParamVarargs.class.getMethod("post", HttpRequestOptions.class); + verifyTestPostOptions(method); + } + + public void testEntityParamVarargs() throws SecurityException, NoSuchMethodException { + Method method = TestEntityParamVarargs.class.getMethod("varargs", Array.newInstance( + HttpRequestOptions.class, 0).getClass()); + verifyTestPostOptions(method); + } + + private void verifyTestPostOptions(Method method) { + HttpRequest httpMethod = factory(TestEntityParamVarargs.class).createRequest(method, + new Object[] { new HttpRequestOptions() { + + public Multimap buildMatrixParameters() { + return HashMultimap.create(); + } + + public String buildPathSuffix() { + return null; + } + + public Multimap buildQueryParameters() { + return HashMultimap.create(); + } + + public Multimap buildRequestHeaders() { + return HashMultimap.create(); + } + + public String buildStringEntity() { + return "fooya"; + } + + } }); + assertEquals(httpMethod.getEndpoint().getHost(), "localhost"); + assertEquals(httpMethod.getEndpoint().getPath(), ""); + assertEquals(httpMethod.getMethod(), HttpMethod.POST); + assertEquals(httpMethod.getHeaders().size(), 2); + assertEquals(httpMethod.getHeaders().get(HttpHeaders.CONTENT_TYPE), Collections + .singletonList("application/unknown")); + assertEquals(httpMethod.getHeaders().get(HttpHeaders.CONTENT_LENGTH), Collections + .singletonList("fooya".getBytes().length + "")); + assertEquals(httpMethod.getEntity(), "fooya"); + } + @Endpoint(Localhost.class) public class TestCustomMethod { @FOO @@ -575,21 +640,61 @@ public class JaxrsAnnotationProcessorTest { } @Endpoint(Localhost.class) - public class TestTransformers { + public interface TestTransformers { @GET - public void noTransformer() { - } + public int noTransformer(); @GET @ResponseParser(ReturnStringIf200.class) - public void oneTransformer() { - } + public void oneTransformer(); @GET @ResponseParser(ReturnStringIf200Context.class) - public void oneTransformerWithContext() { - } + public void oneTransformerWithContext(); + @GET + public InputStream inputStream(); + + @GET + public Future futureInputStream(); + + @GET + public URI uri(); + + @GET + public Future futureUri(); + } + + @SuppressWarnings("static-access") + public void testInputStream() throws SecurityException, NoSuchMethodException { + Method method = TestTransformers.class.getMethod("inputStream"); + Class> transformer = factory(TestTransformers.class) + .getParserOrThrowException(method); + assertEquals(transformer, ReturnInputStream.class); + } + + @SuppressWarnings("static-access") + public void testInputStreamFuture() throws SecurityException, NoSuchMethodException { + Method method = TestTransformers.class.getMethod("futureInputStream"); + Class> transformer = factory(TestTransformers.class) + .getParserOrThrowException(method); + assertEquals(transformer, ReturnInputStream.class); + } + + @SuppressWarnings("static-access") + public void testURI() throws SecurityException, NoSuchMethodException { + Method method = TestTransformers.class.getMethod("uri"); + Class> transformer = factory(TestTransformers.class) + .getParserOrThrowException(method); + assertEquals(transformer, ParseURIList.class); + } + + @SuppressWarnings("static-access") + public void testURIFuture() throws SecurityException, NoSuchMethodException { + Method method = TestTransformers.class.getMethod("futureUri"); + Class> transformer = factory(TestTransformers.class) + .getParserOrThrowException(method); + assertEquals(transformer, ParseURIList.class); } public static class ReturnStringIf200Context extends ReturnStringIf200 implements RestContext { @@ -615,9 +720,7 @@ public class JaxrsAnnotationProcessorTest { @Test(expectedExceptions = { RuntimeException.class }) public void testNoTransformer() throws SecurityException, NoSuchMethodException { Method method = TestTransformers.class.getMethod("noTransformer"); - Class> transformer = factory(TestTransformers.class) - .getParserOrThrowException(method); - assertEquals(transformer, ReturnStringIf200.class); + factory(TestTransformers.class).getParserOrThrowException(method); } public void oneTransformerWithContext() throws SecurityException, NoSuchMethodException { @@ -1014,19 +1117,16 @@ public class JaxrsAnnotationProcessorTest { } @Endpoint(Localhost.class) - public class TestEntity { + public interface TestEntity { @PUT - public void put(@EntityParam String content) { - } + public void put(@EntityParam String content); @PUT @Path("{foo}") - public void putWithPath(@PathParam("foo") String path, @EntityParam String content) { - } + public Future putWithPath(@PathParam("foo") String path, @EntityParam String content); @PUT - public void twoEntities(@EntityParam String entity1, @EntityParam String entity2) { - } + public void twoEntities(@EntityParam String entity1, @EntityParam String entity2); } @Test @@ -1040,6 +1140,9 @@ public class JaxrsAnnotationProcessorTest { .singletonList("application/unknown")); assertEquals(request.getHeaders().get(HttpHeaders.CONTENT_LENGTH), Collections .singletonList("test".getBytes().length + "")); + assertEquals( + factory(TestEntity.class).createResponseParser(method, request, null).getClass(), + ReturnVoidIf2xx.class); } @Test diff --git a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/PCSBlobStore.java b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/PCSBlobStore.java index dc15fdf0ed..85bbcfb5f4 100644 --- a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/PCSBlobStore.java +++ b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/PCSBlobStore.java @@ -33,7 +33,7 @@ import javax.ws.rs.Path; import javax.ws.rs.PathParam; import org.jclouds.blobstore.BlobStore; -import org.jclouds.blobstore.functions.ReturnTrueOnNotFoundOr404; +import org.jclouds.blobstore.functions.ReturnVoidOnNotFoundOr404; import org.jclouds.blobstore.functions.ThrowKeyNotFoundOn404; import org.jclouds.http.filters.BasicAuthentication; import org.jclouds.http.options.GetOptions; @@ -48,10 +48,9 @@ import org.jclouds.mezeo.pcs2.functions.AssembleBlobFromContentAndMetadataCache; import org.jclouds.mezeo.pcs2.functions.ContainerAndFileNameToResourceId; import org.jclouds.mezeo.pcs2.functions.ContainerNameToResourceId; import org.jclouds.mezeo.pcs2.functions.InvalidateContainerNameCacheAndReturnTrueIf2xx; -import org.jclouds.mezeo.pcs2.functions.InvalidatePCSKeyCacheAndReturnTrueIf2xx; +import org.jclouds.mezeo.pcs2.functions.InvalidatePCSKeyCacheAndReturnVoidIf2xx; import org.jclouds.mezeo.pcs2.functions.ReturnFalseIfContainerNotFound; import org.jclouds.mezeo.pcs2.functions.ReturnTrueIfContainerAlreadyExists; -import org.jclouds.mezeo.pcs2.functions.ReturnTrueIfContainerNotFound; import org.jclouds.mezeo.pcs2.xml.FileListToContainerMetadataListHandler; import org.jclouds.mezeo.pcs2.xml.FileListToFileMetadataListHandler; import org.jclouds.mezeo.pcs2.xml.FileMetadataHandler; @@ -99,11 +98,11 @@ public interface PCSBlobStore extends BlobStore createContainer(@EntityParam(CreateContainerBinder.class) String container); @DELETE - @ExceptionParser(ReturnTrueIfContainerNotFound.class) + @ExceptionParser(ReturnVoidOnNotFoundOr404.class) @Path("/containers/{containerResourceId}") @Endpoint(PCS.class) @ResponseParser(InvalidateContainerNameCacheAndReturnTrueIf2xx.class) - Future deleteContainer( + Future deleteContainer( @PathParam("containerResourceId") @ParamParser(ContainerNameToResourceId.class) String containerName); @GET @@ -123,13 +122,13 @@ public interface PCSBlobStore extends BlobStore removeBlob(String container, String key); + Future removeBlob(String container, String key); @GET @ExceptionParser(ThrowKeyNotFoundOn404.class) diff --git a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/PCSConnection.java b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/PCSConnection.java new file mode 100644 index 0000000000..75c290c9b0 --- /dev/null +++ b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/PCSConnection.java @@ -0,0 +1,104 @@ +/** + * + * Copyright (C) 2009 Global Cloud Specialists, Inc. + * + * ==================================================================== + * 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.mezeo.pcs2; + +import java.io.InputStream; +import java.net.URI; +import java.util.SortedSet; +import java.util.concurrent.Future; + +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; + +import org.jclouds.blobstore.functions.ReturnVoidOnNotFoundOr404; +import org.jclouds.blobstore.functions.ThrowKeyNotFoundOn404; +import org.jclouds.http.filters.BasicAuthentication; +import org.jclouds.mezeo.pcs2.binders.CreateContainerBinder; +import org.jclouds.mezeo.pcs2.binders.PCSFileAsMultipartFormBinder; +import org.jclouds.mezeo.pcs2.domain.ContainerMetadata; +import org.jclouds.mezeo.pcs2.domain.FileMetadata; +import org.jclouds.mezeo.pcs2.domain.PCSFile; +import org.jclouds.mezeo.pcs2.endpoints.RootContainer; +import org.jclouds.mezeo.pcs2.xml.FileListToContainerMetadataListHandler; +import org.jclouds.mezeo.pcs2.xml.FileListToFileMetadataListHandler; +import org.jclouds.rest.Endpoint; +import org.jclouds.rest.EntityParam; +import org.jclouds.rest.ExceptionParser; +import org.jclouds.rest.Headers; +import org.jclouds.rest.RequestFilters; +import org.jclouds.rest.SkipEncoding; +import org.jclouds.rest.XMLResponseParser; + +/** + * Provides access to Mezeo PCS v2 via their REST API. + *

+ * + * @see + * @author Adrian Cole + */ +@SkipEncoding('/') +@RequestFilters(BasicAuthentication.class) +public interface PCSConnection { + + @GET + @XMLResponseParser(FileListToContainerMetadataListHandler.class) + @Headers(keys = "X-Cloud-Depth", values = "2") + @Path("/contents") + @Endpoint(RootContainer.class) + SortedSet listContainers(); + + @POST + @Path("/contents") + @Endpoint(RootContainer.class) + Future createContainer(@EntityParam(CreateContainerBinder.class) String container); + + @DELETE + @ExceptionParser(ReturnVoidOnNotFoundOr404.class) + Future deleteContainer(@Endpoint URI container); + + @GET + @XMLResponseParser(FileListToFileMetadataListHandler.class) + @Headers(keys = "X-Cloud-Depth", values = "2") + @Path("/contents") + Future> listFiles(@Endpoint URI container); + + @POST + @Path("/contents") + Future uploadFile(@Endpoint URI container, + @EntityParam(PCSFileAsMultipartFormBinder.class) PCSFile object); + + @DELETE + @ExceptionParser(ReturnVoidOnNotFoundOr404.class) + Future deleteFile(@Endpoint URI file); + + @GET + @ExceptionParser(ThrowKeyNotFoundOn404.class) + @Path("/content") + Future downloadFile(@Endpoint URI file); + +} diff --git a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/PCSContext.java b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/PCSContext.java index 07b967a500..fcb58c55a6 100644 --- a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/PCSContext.java +++ b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/PCSContext.java @@ -38,6 +38,6 @@ import org.jclouds.mezeo.pcs2.domain.PCSFile; * */ public interface PCSContext extends - BlobStoreContext { + BlobStoreContext { } \ No newline at end of file diff --git a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/PCSContextBuilder.java b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/PCSContextBuilder.java index 7c2cf8d919..25eb473d1c 100755 --- a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/PCSContextBuilder.java +++ b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/PCSContextBuilder.java @@ -47,10 +47,10 @@ import com.google.inject.TypeLiteral; * @author Adrian Cole */ public class PCSContextBuilder extends - BlobStoreContextBuilder { + BlobStoreContextBuilder { public PCSContextBuilder(Properties props) { - super(new TypeLiteral() { + super(new TypeLiteral() { }, new TypeLiteral() { }, new TypeLiteral() { }, new TypeLiteral() { diff --git a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/config/PCSContextModule.java b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/config/PCSContextModule.java index 0fbc4a8d85..ea6f023b22 100644 --- a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/config/PCSContextModule.java +++ b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/config/PCSContextModule.java @@ -32,11 +32,12 @@ import javax.inject.Named; import javax.inject.Provider; import javax.inject.Singleton; +import org.jclouds.blobstore.BlobStore; import org.jclouds.blobstore.BlobStoreContextImpl; import org.jclouds.blobstore.BlobMap.Factory; import org.jclouds.lifecycle.Closer; import org.jclouds.mezeo.pcs2.PCS; -import org.jclouds.mezeo.pcs2.PCSBlobStore; +import org.jclouds.mezeo.pcs2.PCSConnection; import org.jclouds.mezeo.pcs2.PCSContext; import org.jclouds.mezeo.pcs2.domain.ContainerMetadata; import org.jclouds.mezeo.pcs2.domain.FileMetadata; @@ -64,15 +65,17 @@ public class PCSContextModule extends AbstractModule { } public static class PCSContextImpl extends - BlobStoreContextImpl implements + BlobStoreContextImpl implements PCSContext { @Inject PCSContextImpl(Factory blobMapFactory, org.jclouds.blobstore.InputStreamMap.Factory inputStreamMapFactory, - Closer closer, Provider blobProvider, PCSBlobStore defaultApi, - @PCS URI endPoint, @Named(PCSConstants.PROPERTY_PCS2_USER) String account) { - super(blobMapFactory, inputStreamMapFactory, closer, blobProvider, defaultApi, endPoint, - account); + Closer closer, Provider blobProvider, + BlobStore blobStore, + PCSConnection defaultApi, @PCS URI endPoint, + @Named(PCSConstants.PROPERTY_PCS2_USER) String account) { + super(blobMapFactory, inputStreamMapFactory, closer, blobProvider, blobStore, defaultApi, + endPoint, account); } } @@ -91,7 +94,8 @@ public class PCSContextModule extends AbstractModule { @Provides @Singleton - public ConcurrentMap provideConcurrentMap(final PCSBlobStore connection) { + public ConcurrentMap provideConcurrentMap( + final BlobStore connection) { return new MapMaker().expiration(30, TimeUnit.SECONDS).makeComputingMap( new Function() { diff --git a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/config/RestPCSBlobStoreModule.java b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/config/RestPCSBlobStoreModule.java index 418edeffd9..dc9c474743 100755 --- a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/config/RestPCSBlobStoreModule.java +++ b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/config/RestPCSBlobStoreModule.java @@ -29,6 +29,7 @@ import java.net.URI; import javax.inject.Named; import javax.inject.Singleton; +import org.jclouds.blobstore.BlobStore; import org.jclouds.cloud.ConfiguresCloudConnection; import org.jclouds.concurrent.SingleThreaded; import org.jclouds.http.HttpRetryHandler; @@ -38,8 +39,12 @@ import org.jclouds.http.filters.BasicAuthentication; import org.jclouds.mezeo.pcs2.PCS; import org.jclouds.mezeo.pcs2.PCSBlobStore; import org.jclouds.mezeo.pcs2.PCSCloud; +import org.jclouds.mezeo.pcs2.PCSConnection; import org.jclouds.mezeo.pcs2.PCSUtil; import org.jclouds.mezeo.pcs2.PCSCloud.Response; +import org.jclouds.mezeo.pcs2.domain.ContainerMetadata; +import org.jclouds.mezeo.pcs2.domain.FileMetadata; +import org.jclouds.mezeo.pcs2.domain.PCSFile; import org.jclouds.mezeo.pcs2.endpoints.Contacts; import org.jclouds.mezeo.pcs2.endpoints.Metacontainers; import org.jclouds.mezeo.pcs2.endpoints.Projects; @@ -88,7 +93,13 @@ public class RestPCSBlobStoreModule extends AbstractModule { @Provides @Singleton - protected PCSBlobStore provideConnection(RestClientFactory factory) { + protected PCSConnection provideConnection(RestClientFactory factory) { + return factory.create(PCSConnection.class); + } + + @Provides + @Singleton + protected BlobStore provideBlobStore(RestClientFactory factory) { return factory.create(PCSBlobStore.class); } diff --git a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/functions/FindIdInContainerList.java b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/functions/FindIdInContainerList.java index 887c123e4b..eadfdec5b1 100644 --- a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/functions/FindIdInContainerList.java +++ b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/functions/FindIdInContainerList.java @@ -1,3 +1,26 @@ +/** + * + * Copyright (C) 2009 Global Cloud Specialists, Inc. + * + * ==================================================================== + * 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.mezeo.pcs2.functions; import java.util.SortedSet; @@ -5,17 +28,17 @@ import java.util.SortedSet; import javax.inject.Inject; import org.jclouds.blobstore.ContainerNotFoundException; -import org.jclouds.mezeo.pcs2.PCSBlobStore; +import org.jclouds.mezeo.pcs2.PCSConnection; import org.jclouds.mezeo.pcs2.domain.ContainerMetadata; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; public class FindIdInContainerList implements Function { - private PCSBlobStore connection; + private PCSConnection connection; @Inject - public FindIdInContainerList(PCSBlobStore connection) { + public FindIdInContainerList(PCSConnection connection) { this.connection = connection; } @@ -25,7 +48,8 @@ public class FindIdInContainerList implements Function { } @VisibleForTesting - String idForNameInListOrException(String toFind, SortedSet containerMetadataList) { + String idForNameInListOrException(String toFind, + SortedSet containerMetadataList) { for (ContainerMetadata data : containerMetadataList) { if (toFind.equals(data.getName())) { String path = data.getUrl().getPath(); diff --git a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/functions/FindIdInFileList.java b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/functions/FindIdInFileList.java index a6c6b7e1bb..da579439fc 100644 --- a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/functions/FindIdInFileList.java +++ b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/functions/FindIdInFileList.java @@ -1,3 +1,26 @@ +/** + * + * Copyright (C) 2009 Global Cloud Specialists, Inc. + * + * ==================================================================== + * 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.mezeo.pcs2.functions; import java.util.Arrays; @@ -6,21 +29,23 @@ import java.util.concurrent.TimeUnit; import javax.inject.Inject; +import org.jclouds.blobstore.BlobStore; import org.jclouds.blobstore.ContainerNotFoundException; import org.jclouds.blobstore.KeyNotFoundException; import org.jclouds.http.HttpException; -import org.jclouds.mezeo.pcs2.PCSBlobStore; +import org.jclouds.mezeo.pcs2.domain.ContainerMetadata; import org.jclouds.mezeo.pcs2.domain.FileMetadata; +import org.jclouds.mezeo.pcs2.domain.PCSFile; import org.jclouds.util.Utils; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; public class FindIdInFileList implements Function { - private PCSBlobStore connection; + private BlobStore connection; @Inject - public FindIdInFileList(PCSBlobStore connection) { + public FindIdInFileList(BlobStore connection) { this.connection = connection; } @@ -36,7 +61,8 @@ public class FindIdInFileList implements Function { } @VisibleForTesting - String idForNameInListOrException(String container, String toFind, SortedSet response) { + String idForNameInListOrException(String container, String toFind, + SortedSet response) { for (FileMetadata data : response) { if (toFind.equals(data.getKey())) { String path = data.getUrl().getPath(); diff --git a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/functions/InvalidateContainerNameCacheAndReturnTrueIf2xx.java b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/functions/InvalidateContainerNameCacheAndReturnTrueIf2xx.java index 7060765fa0..c32c2210e1 100644 --- a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/functions/InvalidateContainerNameCacheAndReturnTrueIf2xx.java +++ b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/functions/InvalidateContainerNameCacheAndReturnTrueIf2xx.java @@ -1,3 +1,26 @@ +/** + * + * Copyright (C) 2009 Global Cloud Specialists, Inc. + * + * ==================================================================== + * 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.mezeo.pcs2.functions; import java.util.concurrent.ConcurrentMap; diff --git a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/functions/InvalidatePCSKeyCacheAndReturnTrueIf2xx.java b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/functions/InvalidatePCSKeyCacheAndReturnVoidIf2xx.java similarity index 52% rename from mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/functions/InvalidatePCSKeyCacheAndReturnTrueIf2xx.java rename to mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/functions/InvalidatePCSKeyCacheAndReturnVoidIf2xx.java index d9a5a5edb7..0793b4c250 100644 --- a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/functions/InvalidatePCSKeyCacheAndReturnTrueIf2xx.java +++ b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/functions/InvalidatePCSKeyCacheAndReturnVoidIf2xx.java @@ -1,3 +1,26 @@ +/** + * + * Copyright (C) 2009 Global Cloud Specialists, Inc. + * + * ==================================================================== + * 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.mezeo.pcs2.functions; import java.util.concurrent.ConcurrentMap; @@ -17,7 +40,7 @@ import com.google.common.base.Function; * * @author Adrian Cole */ -public class InvalidatePCSKeyCacheAndReturnTrueIf2xx implements Function, +public class InvalidatePCSKeyCacheAndReturnVoidIf2xx implements Function, RestContext { private final ConcurrentMap cache; private final ConcurrentMap mdCache; @@ -25,13 +48,13 @@ public class InvalidatePCSKeyCacheAndReturnTrueIf2xx implements Function cache, + public InvalidatePCSKeyCacheAndReturnVoidIf2xx(ConcurrentMap cache, ConcurrentMap mdCache) { this.cache = cache; this.mdCache = mdCache; } - public Boolean apply(HttpResponse from) { + public Void apply(HttpResponse from) { IOUtils.closeQuietly(from.getContent()); int code = from.getStatusCode(); if (code >= 300 || code < 200) { @@ -40,7 +63,7 @@ public class InvalidatePCSKeyCacheAndReturnTrueIf2xx implements Function + * + * ==================================================================== + * 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.mezeo.pcs2.functions; public class Key { diff --git a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/handlers/PCSClientErrorRetryHandler.java b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/handlers/PCSClientErrorRetryHandler.java index 66fe8b898a..aa65bea1c7 100644 --- a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/handlers/PCSClientErrorRetryHandler.java +++ b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/handlers/PCSClientErrorRetryHandler.java @@ -1,3 +1,26 @@ +/** + * + * Copyright (C) 2009 Global Cloud Specialists, Inc. + * + * ==================================================================== + * 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.mezeo.pcs2.handlers; import javax.annotation.Resource; diff --git a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/PCSBlobStoreLiveTest.java b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/PCSBlobStoreLiveTest.java deleted file mode 100644 index e2c4debf1d..0000000000 --- a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/PCSBlobStoreLiveTest.java +++ /dev/null @@ -1,203 +0,0 @@ -/** - * - * Copyright (C) 2009 Global Cloud Specialists, Inc. - * - * ==================================================================== - * 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.mezeo.pcs2; - -import static com.google.common.base.Preconditions.checkNotNull; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertTrue; - -import java.io.InputStream; -import java.net.URI; -import java.util.SortedSet; -import java.util.concurrent.TimeUnit; - -import org.apache.commons.io.IOUtils; -import org.jclouds.blobstore.KeyNotFoundException; -import org.jclouds.blobstore.integration.internal.BaseBlobStoreIntegrationTest; -import org.jclouds.http.HttpResponseException; -import org.jclouds.http.HttpUtils; -import org.jclouds.logging.log4j.config.Log4JLoggingModule; -import org.jclouds.mezeo.pcs2.domain.ContainerMetadata; -import org.jclouds.mezeo.pcs2.domain.FileMetadata; -import org.jclouds.mezeo.pcs2.domain.PCSFile; -import org.testng.annotations.BeforeGroups; -import org.testng.annotations.Test; - -import com.google.common.collect.Iterables; - -/** - * Tests behavior of {@code PCSDiscovery} - * - * @author Adrian Cole - */ -@Test(groups = "live", testName = "pcs2.PCSConnectionLiveTest") -public class PCSBlobStoreLiveTest { - - private PCSBlobStore connection; - - @BeforeGroups(groups = { "live" }) - public void setupConnection() { - String user = checkNotNull(System.getProperty("jclouds.test.user"), "jclouds.test.user"); - String password = checkNotNull(System.getProperty("jclouds.test.key"), "jclouds.test.key"); - URI endpoint = URI.create(checkNotNull(System.getProperty("jclouds.test.endpoint"), - "jclouds.test.endpoint")); - - connection = PCSContextFactory.createContext(endpoint, user, password, - new Log4JLoggingModule()).getApi(); - - } - - private String containerPrefix = BaseBlobStoreIntegrationTest.CONTAINER_PREFIX; - - @Test - public void testListContainers() throws Exception { - SortedSet response = connection.listContainers(); - assertNotNull(response); - long initialContainerCount = response.size(); - assertTrue(initialContainerCount >= 0); - - // Create test containers - String[] containerJsr330 = new String[] { containerPrefix + ".testListOwnedContainers1", - containerPrefix + ".testListOwnedContainers2" }; - assertTrue(connection.createContainer(containerJsr330[0]).get(10, TimeUnit.SECONDS)); - assertTrue(connection.createContainer(containerJsr330[1]).get(10, TimeUnit.SECONDS)); - assertTrue(connection.containerExists(containerJsr330[1])); - - // Test default listing - response = connection.listContainers(); - // Map nameToId = Maps.newHashMap(); - // - // for (ContainerMetadata data : response) { - // String path = data.getUrl().getPath(); - // int indexAfterContainersSlash = path.indexOf("containers/") + "containers/".length(); - // String id = path.substring(indexAfterContainersSlash); - // nameToId.put(data.getName(), id); - // } - // assert nameToId.size() >= 2 : nameToId; - // assertEquals(response.size(), initialContainerCount + 2);// if the containers already - // exist, this will fail - - // Cleanup and test containers have been removed - // assertTrue(connection.deleteContainer(nameToId.get(containerJsr330[0])).get(10, - // TimeUnit.SECONDS)); - // assertTrue(connection.deleteContainer(nameToId.get(containerJsr330[1])).get(10, - // TimeUnit.SECONDS)); - connection.listBlobs(containerJsr330[0]).get(10, TimeUnit.SECONDS); - connection.listBlobs(containerJsr330[1]).get(10, TimeUnit.SECONDS); - assertTrue(connection.deleteContainer(containerJsr330[0]).get(10, TimeUnit.SECONDS)); - assertTrue(connection.deleteContainer(containerJsr330[1]).get(10, TimeUnit.SECONDS)); - assertTrue(!connection.containerExists(containerJsr330[0])); - - response = connection.listContainers(); - // assertEquals(response.size(), initialContainerCount + 2);// if the containers already - // exist, this will fail - } - - @Test - public void testObjectOperations() throws Exception { - String containerName = containerPrefix + ".testObjectOperations"; - String data = "Here is my data"; - - assertTrue(connection.createContainer(containerName).get(10, TimeUnit.SECONDS)); - - // Test PUT with string data, ETag hash, and a piece of metadata - PCSFile object = new PCSFile("object"); - object.setData(data); - object.setContentLength(data.length()); - object.generateMD5(); - object.getMetadata().setContentType("text/plain"); - object.getMetadata().getUserMetadata().put("Metadata", "metadata-value"); - byte[] md5 = object.getMetadata().getContentMD5(); - // etag support by end of the year - assertNotNull(connection.putBlob(containerName, object).get(10, TimeUnit.SECONDS)); - assertEquals(HttpUtils.toHexString(md5), HttpUtils.toHexString(object.getMetadata() - .getContentMD5())); - - // Test HEAD of missing object - try { - connection.blobMetadata(containerName, "non-existent-object"); - assert false; - } catch (KeyNotFoundException e) { - } - - // Test HEAD of object - FileMetadata metadata = connection.blobMetadata(containerName, object.getKey()); - // TODO assertEquals(metadata.getKey(), object.getKey()); - assertEquals(metadata.getSize(), data.length()); - assertEquals(metadata.getContentType(), "text/plain"); - // assertEquals(HttpUtils.toHexString(md5), HttpUtils.toHexString(object.getMetadata() - // .getContentMD5())); - // assertEquals(metadata.getETag(), newEtag); - assertEquals(metadata.getUserMetadata().entries().size(), 1); - assertEquals(Iterables.getLast(metadata.getUserMetadata().get("metadata")), "metadata-value"); - - // // Test POST to update object's metadata - // Multimap userMetadata = HashMultimap.create(); - // userMetadata.put("New-Metadata-1", "value-1"); - // userMetadata.put("New-Metadata-2", "value-2"); - // assertTrue(connection.setObjectMetadata(containerName, object.getKey(), userMetadata)); - - // Test GET of missing object - try { - connection.getBlob(containerName, "non-existent-object").get(10, TimeUnit.SECONDS); - assert false; - } catch (KeyNotFoundException e) { - } - // Test GET of object (including updated metadata) - PCSFile getBlob = connection.getBlob(containerName, object.getKey()).get(120, - TimeUnit.SECONDS); - assertEquals(IOUtils.toString((InputStream) getBlob.getData()), data); - assertEquals(getBlob.getKey(), object.getKey()); - assertEquals(getBlob.getContentLength(), data.length()); - assertEquals(getBlob.getMetadata().getContentType(), "text/plain"); - // assertEquals(HttpUtils.toHexString(md5), HttpUtils.toHexString(getBlob.getMetadata() - // .getContentMD5())); - // assertEquals(newEtag, getBlob.getMetadata().getETag()); - // assertEquals(getBlob.getMetadata().getUserMetadata().entries().size(), 2); - // assertEquals( - // Iterables.getLast(getBlob.getMetadata().getUserMetadata().get("new-metadata-1")), - // "value-1"); - // assertEquals( - // Iterables.getLast(getBlob.getMetadata().getUserMetadata().get("new-metadata-2")), - // "value-2"); - - // Test PUT with invalid ETag (as if object's data was corrupted in transit) - // String correctEtag = HttpUtils.toHexString(newEtag); - // String incorrectEtag = "0" + correctEtag.substring(1); - // object.getMetadata().setETag(HttpUtils.fromHexString(incorrectEtag)); - try { - connection.putBlob(containerName, object).get(10, TimeUnit.SECONDS); - } catch (Throwable e) { - assertEquals(e.getCause().getClass(), HttpResponseException.class); - assertEquals(((HttpResponseException) e.getCause()).getResponse().getStatusCode(), 422); - } - - assertTrue(connection.removeBlob(containerName, "object").get(10, TimeUnit.SECONDS)); - assertTrue(connection.removeBlob(containerName, "chunked-object").get(10, TimeUnit.SECONDS)); - - assertTrue(connection.deleteContainer(containerName).get(10, TimeUnit.SECONDS)); - } - -} diff --git a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/PCSBlobStoreTest.java b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/PCSBlobStoreTest.java index 887503441c..2a8c7151a1 100644 --- a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/PCSBlobStoreTest.java +++ b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/PCSBlobStoreTest.java @@ -32,17 +32,17 @@ import java.lang.reflect.Method; import java.net.URI; import java.util.Collections; import java.util.SortedSet; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; -import javax.inject.Named; import javax.inject.Singleton; import javax.ws.rs.HttpMethod; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; -import org.jclouds.blobstore.BlobStoreMapsModule; -import org.jclouds.blobstore.functions.ReturnTrueOnNotFoundOr404; +import org.jclouds.blobstore.functions.ReturnVoidOnNotFoundOr404; import org.jclouds.blobstore.functions.ThrowKeyNotFoundOn404; import org.jclouds.blobstore.integration.internal.StubBlobStore; import org.jclouds.concurrent.WithinThreadExecutorService; @@ -56,7 +56,6 @@ import org.jclouds.http.functions.ReturnStringIf200; import org.jclouds.http.functions.ReturnTrueIf2xx; import org.jclouds.http.options.GetOptions; import org.jclouds.mezeo.pcs2.binders.PCSFileAsMultipartFormBinderTest; -import org.jclouds.mezeo.pcs2.config.PCSContextModule; import org.jclouds.mezeo.pcs2.domain.ContainerMetadata; import org.jclouds.mezeo.pcs2.domain.FileMetadata; import org.jclouds.mezeo.pcs2.domain.PCSFile; @@ -64,14 +63,11 @@ import org.jclouds.mezeo.pcs2.endpoints.RootContainer; import org.jclouds.mezeo.pcs2.functions.AddMetadataAndParseResourceIdIntoBytes; import org.jclouds.mezeo.pcs2.functions.AssembleBlobFromContentAndMetadataCache; import org.jclouds.mezeo.pcs2.functions.InvalidateContainerNameCacheAndReturnTrueIf2xx; -import org.jclouds.mezeo.pcs2.functions.InvalidatePCSKeyCacheAndReturnTrueIf2xx; +import org.jclouds.mezeo.pcs2.functions.InvalidatePCSKeyCacheAndReturnVoidIf2xx; import org.jclouds.mezeo.pcs2.functions.ReturnFalseIfContainerNotFound; -import org.jclouds.mezeo.pcs2.functions.ReturnTrueIfContainerNotFound; -import org.jclouds.mezeo.pcs2.reference.PCSConstants; import org.jclouds.rest.JaxrsAnnotationProcessor; import org.jclouds.rest.config.JaxrsModule; import org.jclouds.util.DateService; -import org.jclouds.util.Jsr330; import org.jclouds.util.Utils; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -91,17 +87,9 @@ import com.google.inject.TypeLiteral; */ @Test(groups = "unit", testName = "pcs2.PCSConnectionTest") public class PCSBlobStoreTest { - public static final class StubPCSConnection implements PCSBlobStore { + public static final class StubPCSConnection implements PCSConnection { DateService dateService = new DateService(); - public Future createContainer(String container) { - return null; - } - - public Future deleteContainer(String containerName) { - return null; - } - public Future> listBlobs(String containerName) { return new StubBlobStore.FutureBase>() { public SortedSet get() throws InterruptedException, ExecutionException { @@ -144,7 +132,7 @@ public class PCSBlobStoreTest { 1, 1024)); } - public Future putBlob(String containerName, PCSFile object) { + public Future uploadFile(String containerName, PCSFile object) { return null; } @@ -168,6 +156,30 @@ public class PCSBlobStoreTest { return false; } + public Future createContainer(String container) { + return null; + } + + public Future deleteContainer(URI container) { + return null; + } + + public Future deleteFile(URI file) { + return null; + } + + public Future downloadFile(URI file) { + return null; + } + + public Future> listFiles(URI container) { + return null; + } + + public Future uploadFile(URI container, PCSFile object) { + return null; + } + } public void testListContainers() throws SecurityException, NoSuchMethodException { @@ -218,7 +230,7 @@ public class PCSBlobStoreTest { assertEquals(processor.createResponseParser(method, httpMethod, null).getClass(), InvalidateContainerNameCacheAndReturnTrueIf2xx.class); assertEquals(processor.createExceptionParserOrNullIfNotFound(method).getClass(), - ReturnTrueIfContainerNotFound.class); + ReturnVoidOnNotFoundOr404.class); } public void testContainerExists() throws SecurityException, NoSuchMethodException { @@ -288,9 +300,9 @@ public class PCSBlobStoreTest { assertEquals(httpMethod.getMethod(), HttpMethod.DELETE); assertEquals(httpMethod.getHeaders().size(), 0); assertEquals(processor.createResponseParser(method, httpMethod, null).getClass(), - InvalidatePCSKeyCacheAndReturnTrueIf2xx.class); + InvalidatePCSKeyCacheAndReturnVoidIf2xx.class); assertEquals(processor.createExceptionParserOrNullIfNotFound(method).getClass(), - ReturnTrueOnNotFoundOr404.class); + ReturnVoidOnNotFoundOr404.class); } public void testGetBlob() throws SecurityException, NoSuchMethodException, IOException { @@ -385,57 +397,74 @@ public class PCSBlobStoreTest { @BeforeClass void setupFactory() { - Injector injector = Guice.createInjector(new AbstractModule() { - @Override - protected void configure() { - bind(URI.class).annotatedWith(PCS.class) - .toInstance(URI.create("http://localhost:8080")); - bind(URI.class).annotatedWith(RootContainer.class).toInstance( - URI.create("http://localhost:8080/root")); - bindConstant().annotatedWith(Jsr330.named(PCSConstants.PROPERTY_PCS2_USER)).to("user"); - bindConstant().annotatedWith(Jsr330.named(PCSConstants.PROPERTY_PCS2_PASSWORD)).to( - "password"); - } - @SuppressWarnings("unused") - @Provides - @Singleton - public PCSBlobStore getPCSConnection() { - return new StubPCSConnection(); - } + Injector injector = Guice.createInjector( + new AbstractModule() { + @Override + protected void configure() { + bind(URI.class).annotatedWith(PCS.class).toInstance( + URI.create("http://localhost:8080")); + bind(URI.class).annotatedWith(RootContainer.class).toInstance( + URI.create("http://localhost:8080/root")); + } - @SuppressWarnings("unused") - @Provides - @Singleton - public PCSUtil getPCSUtil() { - return new PCSUtil() { + @SuppressWarnings("unused") + @Provides + @Singleton + public PCSUtil getPCSUtil() { + return new PCSUtil() { - public String get(URI resource) { - return null; - } + public String get(URI resource) { + return null; + } - public boolean put(URI resource, String value) { - return true; - } + public boolean put(URI resource, String value) { + return true; + } - }; - } + }; + } - @SuppressWarnings("unused") - @Provides - @Singleton - public BasicAuthentication provideBasicAuthentication( - @Named(PCSConstants.PROPERTY_PCS2_USER) String user, - @Named(PCSConstants.PROPERTY_PCS2_PASSWORD) String password) - throws UnsupportedEncodingException { - return new BasicAuthentication(user, password); - } - }, new JaxrsModule(), BlobStoreMapsModule.Builder.newBuilder(new TypeLiteral() { - }, new TypeLiteral() { - }, new TypeLiteral() { - }, new TypeLiteral() { - }).build(), new PCSContextModule(), new ExecutorServiceModule( - new WithinThreadExecutorService()), new JavaUrlHttpCommandExecutorServiceModule()); + @SuppressWarnings("unused") + @Provides + @Singleton + ConcurrentMap giveMap() { + ConcurrentHashMap map = new ConcurrentHashMap(); + map.put( + new org.jclouds.mezeo.pcs2.functions.Key("mycontainer", + "testfile.txt"), "9E4C5AFA-A98B-11DE-8B4C-C3884B4A2DA3"); + return map; + } + + @SuppressWarnings("unused") + @Provides + @Singleton + ConcurrentMap giveMap2() { + ConcurrentHashMap map = new ConcurrentHashMap(); + map.put( + new org.jclouds.mezeo.pcs2.functions.Key("mycontainer", + "testfile.txt"), new FileMetadata("testfile.txt")); + return map; + } + + @SuppressWarnings("unused") + @Provides + @Singleton + ConcurrentMap giveMap3() { + ConcurrentHashMap map = new ConcurrentHashMap(); + map.put("mycontainer", "7F143552-AAF5-11DE-BBB0-0BC388ED913B"); + return map; + } + + @SuppressWarnings("unused") + @Provides + @Singleton + public BasicAuthentication provideBasicAuthentication() + throws UnsupportedEncodingException { + return new BasicAuthentication("foo", "bar"); + } + }, new JaxrsModule(), new ExecutorServiceModule(new WithinThreadExecutorService()), + new JavaUrlHttpCommandExecutorServiceModule()); processor = injector.getInstance(Key .get(new TypeLiteral>() { diff --git a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/PCSConnectionLiveTest.java b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/PCSConnectionLiveTest.java new file mode 100644 index 0000000000..9f8798cd6a --- /dev/null +++ b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/PCSConnectionLiveTest.java @@ -0,0 +1,135 @@ +/** + * + * Copyright (C) 2009 Global Cloud Specialists, Inc. + * + * ==================================================================== + * 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.mezeo.pcs2; + +import static com.google.common.base.Preconditions.checkNotNull; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; + +import java.io.InputStream; +import java.net.URI; +import java.util.SortedSet; +import java.util.concurrent.TimeUnit; + +import javax.ws.rs.core.UriBuilder; + +import org.apache.commons.io.IOUtils; +import org.jclouds.blobstore.KeyNotFoundException; +import org.jclouds.blobstore.integration.internal.BaseBlobStoreIntegrationTest; +import org.jclouds.http.HttpResponseException; +import org.jclouds.logging.log4j.config.Log4JLoggingModule; +import org.jclouds.mezeo.pcs2.domain.ContainerMetadata; +import org.jclouds.mezeo.pcs2.domain.PCSFile; +import org.testng.annotations.BeforeGroups; +import org.testng.annotations.Test; + +/** + * Tests behavior of {@code PCSDiscovery} + * + * @author Adrian Cole + */ +@Test(groups = "live", testName = "pcs2.PCSConnectionLiveTest") +public class PCSConnectionLiveTest { + + private PCSConnection connection; + + @BeforeGroups(groups = { "live" }) + public void setupConnection() { + String user = checkNotNull(System.getProperty("jclouds.test.user"), "jclouds.test.user"); + String password = checkNotNull(System.getProperty("jclouds.test.key"), "jclouds.test.key"); + URI endpoint = URI.create(checkNotNull(System.getProperty("jclouds.test.endpoint"), + "jclouds.test.endpoint")); + + connection = PCSContextFactory.createContext(endpoint, user, password, + new Log4JLoggingModule()).getApi(); + + } + + private String containerPrefix = BaseBlobStoreIntegrationTest.CONTAINER_PREFIX; + + URI container1; + URI container2; + + @Test + public void testListContainers() throws Exception { + SortedSet response = connection.listContainers(); + assertNotNull(response); + long initialContainerCount = response.size(); + assertTrue(initialContainerCount >= 0); + + // Create test containers + String[] containerJsr330 = new String[] { containerPrefix + ".testListOwnedContainers1", + containerPrefix + ".testListOwnedContainers2" }; + container1 = connection.createContainer(containerJsr330[0]).get(10, TimeUnit.SECONDS); + container2 = connection.createContainer(containerJsr330[1]).get(10, TimeUnit.SECONDS); + + // Test default listing + response = connection.listContainers(); + + connection.listFiles(container1).get(10, TimeUnit.SECONDS); + connection.listFiles(container2).get(10, TimeUnit.SECONDS); + connection.deleteContainer(container1).get(10, TimeUnit.SECONDS); + connection.deleteContainer(container2).get(10, TimeUnit.SECONDS); + + response = connection.listContainers(); + // assertEquals(response.size(), initialContainerCount + 2);// if the containers already + // exist, this will fail + } + + @Test + public void testObjectOperations() throws Exception { + String containerName = containerPrefix + ".testObjectOperations"; + String data = "Here is my data"; + + URI container = connection.createContainer(containerName).get(10, TimeUnit.SECONDS); + + // Test PUT with string data, ETag hash, and a piece of metadata + PCSFile object = new PCSFile("object"); + object.setData(data); + object.setContentLength(data.length()); + URI objectURI = connection.uploadFile(container, object).get(10, TimeUnit.SECONDS); + + try { + connection.downloadFile(UriBuilder.fromUri(objectURI).path("sad").build()).get(10, + TimeUnit.SECONDS); + assert false; + } catch (KeyNotFoundException e) { + } + // Test GET of object (including updated metadata) + InputStream file = connection.downloadFile(objectURI).get(120, TimeUnit.SECONDS); + assertEquals(IOUtils.toString(file), data); + + try { + connection.uploadFile(container, object).get(10, TimeUnit.SECONDS); + } catch (Throwable e) { + assertEquals(e.getCause().getClass(), HttpResponseException.class); + assertEquals(((HttpResponseException) e.getCause()).getResponse().getStatusCode(), 422); + } + + connection.deleteFile(objectURI).get(10, TimeUnit.SECONDS); + connection.deleteContainer(container).get(10, TimeUnit.SECONDS); + } + +} diff --git a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/PCSConnectionTest.java b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/PCSConnectionTest.java new file mode 100644 index 0000000000..c369989187 --- /dev/null +++ b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/PCSConnectionTest.java @@ -0,0 +1,273 @@ +/** + * + * Copyright (C) 2009 Global Cloud Specialists, Inc. + * + * ==================================================================== + * 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.mezeo.pcs2; + +import static org.testng.Assert.assertEquals; + +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Method; +import java.net.URI; +import java.util.Collections; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import javax.inject.Singleton; +import javax.ws.rs.HttpMethod; +import javax.ws.rs.core.HttpHeaders; + +import org.jclouds.blobstore.functions.ReturnVoidOnNotFoundOr404; +import org.jclouds.concurrent.WithinThreadExecutorService; +import org.jclouds.concurrent.config.ExecutorServiceModule; +import org.jclouds.http.HttpRequest; +import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule; +import org.jclouds.http.filters.BasicAuthentication; +import org.jclouds.http.functions.ParseSax; +import org.jclouds.http.functions.ParseURIList; +import org.jclouds.http.functions.ReturnInputStream; +import org.jclouds.http.functions.ReturnVoidIf2xx; +import org.jclouds.mezeo.pcs2.binders.PCSFileAsMultipartFormBinderTest; +import org.jclouds.mezeo.pcs2.domain.FileMetadata; +import org.jclouds.mezeo.pcs2.domain.PCSFile; +import org.jclouds.mezeo.pcs2.endpoints.RootContainer; +import org.jclouds.rest.JaxrsAnnotationProcessor; +import org.jclouds.rest.config.JaxrsModule; +import org.jclouds.util.Utils; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import com.google.inject.AbstractModule; +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.google.inject.Key; +import com.google.inject.Provides; +import com.google.inject.TypeLiteral; + +/** + * Tests behavior of {@code PCSConnection} + * + * @author Adrian Cole + */ +@Test(groups = "unit", testName = "pcs2.PCSConnectionTest") +public class PCSConnectionTest { + + public void testListContainers() throws SecurityException, NoSuchMethodException { + Method method = PCSConnection.class.getMethod("listContainers"); + + HttpRequest httpMethod = processor.createRequest(method, new Object[] {}); + assertEquals(httpMethod.getEndpoint().getHost(), "localhost"); + assertEquals(httpMethod.getEndpoint().getPath(), "/root/contents"); + assertEquals(httpMethod.getEndpoint().getQuery(), null); + assertEquals(httpMethod.getMethod(), HttpMethod.GET); + assertEquals(httpMethod.getHeaders().size(), 1); + assertEquals(httpMethod.getHeaders().get("X-Cloud-Depth"), Collections.singletonList("2")); + assertEquals(processor.createResponseParser(method, httpMethod, null).getClass(), + ParseSax.class); + // TODO check generic type of response parser + assertEquals(processor.createExceptionParserOrNullIfNotFound(method), null); + } + + public void testCreateContainer() throws SecurityException, NoSuchMethodException, IOException { + Method method = PCSConnection.class.getMethod("createContainer", String.class); + + HttpRequest httpMethod = processor.createRequest(method, new Object[] { "container" }); + assertEquals(httpMethod.getEndpoint().getHost(), "localhost"); + assertEquals(httpMethod.getEndpoint().getPath(), "/root/contents"); + assertEquals(httpMethod.getEndpoint().getQuery(), null); + assertEquals(httpMethod.getMethod(), HttpMethod.POST); + assertEquals(httpMethod.getHeaders().size(), 2); + assertEquals(httpMethod.getHeaders().get(HttpHeaders.CONTENT_LENGTH), Collections + .singletonList("45")); + assertEquals(httpMethod.getHeaders().get(HttpHeaders.CONTENT_TYPE), Collections + .singletonList("application/vnd.csp.container-info+xml")); + assertEquals(httpMethod.getEntity(), "container"); + assertEquals(processor.createResponseParser(method, httpMethod, null).getClass(), + ParseURIList.class); + // TODO check generic type of response parser + } + + public void testDeleteContainer() throws SecurityException, NoSuchMethodException { + Method method = PCSConnection.class.getMethod("deleteContainer", URI.class); + + HttpRequest httpMethod = processor.createRequest(method, new Object[] { URI + .create("http://localhost/container/1234") }); + assertEquals(httpMethod.getEndpoint().getHost(), "localhost"); + assertEquals(httpMethod.getEndpoint().getPath(), "/container/1234"); + assertEquals(httpMethod.getEndpoint().getQuery(), null); + assertEquals(httpMethod.getMethod(), HttpMethod.DELETE); + assertEquals(httpMethod.getHeaders().size(), 0); + assertEquals(processor.createResponseParser(method, httpMethod, null).getClass(), + ReturnVoidIf2xx.class); + assertEquals(processor.createExceptionParserOrNullIfNotFound(method).getClass(), + ReturnVoidOnNotFoundOr404.class); + } + + public void testListFiles() throws SecurityException, NoSuchMethodException { + Method method = PCSConnection.class.getMethod("listFiles", URI.class); + + HttpRequest httpMethod = processor.createRequest(method, new Object[] { URI + .create("http://localhost/mycontainer") }); + assertEquals(httpMethod.getEndpoint().getHost(), "localhost"); + assertEquals(httpMethod.getEndpoint().getPath(), "/mycontainer/contents"); + assertEquals(httpMethod.getEndpoint().getQuery(), null); + assertEquals(httpMethod.getMethod(), HttpMethod.GET); + assertEquals(httpMethod.getHeaders().size(), 1); + assertEquals(httpMethod.getHeaders().get("X-Cloud-Depth"), Collections.singletonList("2")); + assertEquals(processor.createResponseParser(method, httpMethod, null).getClass(), + ParseSax.class); + // TODO check generic type of response parser + assertEquals(processor.createExceptionParserOrNullIfNotFound(method), null); + } + + public void testUploadFile() throws SecurityException, NoSuchMethodException, IOException { + Method method = PCSConnection.class.getMethod("uploadFile", URI.class, PCSFile.class); + + HttpRequest httpMethod = processor.createRequest(method, new Object[] { + URI.create("http://localhost/mycontainer"), + PCSFileAsMultipartFormBinderTest.TEST_BLOB }); + assertEquals(httpMethod.getEndpoint().getHost(), "localhost"); + assertEquals(httpMethod.getEndpoint().getPath(), "/mycontainer/contents"); + assertEquals(httpMethod.getEndpoint().getQuery(), null); + assertEquals(httpMethod.getMethod(), HttpMethod.POST); + assertEquals(httpMethod.getHeaders().size(), 2); + assertEquals(httpMethod.getHeaders().get(HttpHeaders.CONTENT_LENGTH), Collections + .singletonList(PCSFileAsMultipartFormBinderTest.EXPECTS.length() + "")); + assertEquals(httpMethod.getHeaders().get(HttpHeaders.CONTENT_TYPE), Collections + .singletonList("multipart/form-data; boundary=" + + PCSFileAsMultipartFormBinderTest.BOUNDRY)); + assertEquals(Utils.toStringAndClose((InputStream) httpMethod.getEntity()), + PCSFileAsMultipartFormBinderTest.EXPECTS); + assertEquals(processor.createResponseParser(method, httpMethod, null).getClass(), + ParseURIList.class); + } + + public void testDownloadFile() throws SecurityException, NoSuchMethodException, IOException { + Method method = PCSConnection.class.getMethod("downloadFile", URI.class); + + HttpRequest httpMethod = processor.createRequest(method, new Object[] { URI + .create("http://localhost/container") }); + assertEquals(httpMethod.getEndpoint().getHost(), "localhost"); + assertEquals(httpMethod.getEndpoint().getPath(), "/container/content"); + assertEquals(httpMethod.getEndpoint().getQuery(), null); + assertEquals(httpMethod.getMethod(), HttpMethod.GET); + assertEquals(httpMethod.getHeaders().size(), 0); + assertEquals(processor.createResponseParser(method, httpMethod, null).getClass(), + ReturnInputStream.class); + } + + public void testDeleteFile() throws SecurityException, NoSuchMethodException, IOException { + Method method = PCSConnection.class.getMethod("deleteFile", URI.class); + + HttpRequest httpMethod = processor.createRequest(method, new Object[] { URI + .create("http://localhost/contents/file") }); + assertEquals(httpMethod.getEndpoint().getHost(), "localhost"); + assertEquals(httpMethod.getEndpoint().getPath(), "/contents/file"); + assertEquals(httpMethod.getEndpoint().getQuery(), null); + assertEquals(httpMethod.getMethod(), HttpMethod.DELETE); + assertEquals(httpMethod.getHeaders().size(), 0); + assertEquals(processor.createResponseParser(method, httpMethod, null).getClass(), + ReturnVoidIf2xx.class); + assertEquals(processor.createExceptionParserOrNullIfNotFound(method).getClass(), + ReturnVoidOnNotFoundOr404.class); + } + + JaxrsAnnotationProcessor processor; + + @BeforeClass + void setupFactory() { + + Injector injector = Guice.createInjector( + new AbstractModule() { + @Override + protected void configure() { + bind(URI.class).annotatedWith(PCS.class).toInstance( + URI.create("http://localhost:8080")); + bind(URI.class).annotatedWith(RootContainer.class).toInstance( + URI.create("http://localhost:8080/root")); + } + + @SuppressWarnings("unused") + @Provides + @Singleton + public PCSUtil getPCSUtil() { + return new PCSUtil() { + + public String get(URI resource) { + return null; + } + + public boolean put(URI resource, String value) { + return true; + } + + }; + } + + @SuppressWarnings("unused") + @Provides + @Singleton + ConcurrentMap giveMap() { + ConcurrentHashMap map = new ConcurrentHashMap(); + map.put( + new org.jclouds.mezeo.pcs2.functions.Key("mycontainer", + "testfile.txt"), "9E4C5AFA-A98B-11DE-8B4C-C3884B4A2DA3"); + return map; + } + + @SuppressWarnings("unused") + @Provides + @Singleton + ConcurrentMap giveMap2() { + ConcurrentHashMap map = new ConcurrentHashMap(); + map.put( + new org.jclouds.mezeo.pcs2.functions.Key("mycontainer", + "testfile.txt"), new FileMetadata("testfile.txt")); + return map; + } + + @SuppressWarnings("unused") + @Provides + @Singleton + ConcurrentMap giveMap3() { + ConcurrentHashMap map = new ConcurrentHashMap(); + map.put("mycontainer", "7F143552-AAF5-11DE-BBB0-0BC388ED913B"); + return map; + } + + @SuppressWarnings("unused") + @Provides + @Singleton + public BasicAuthentication provideBasicAuthentication() + throws UnsupportedEncodingException { + return new BasicAuthentication("foo", "bar"); + } + }, new JaxrsModule(), new ExecutorServiceModule(new WithinThreadExecutorService()), + new JavaUrlHttpCommandExecutorServiceModule()); + + processor = injector.getInstance(Key + .get(new TypeLiteral>() { + })); + } +} diff --git a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/PCSContextBuilderTest.java b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/PCSContextBuilderTest.java index 8a76d9c076..08d763fbc8 100644 --- a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/PCSContextBuilderTest.java +++ b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/PCSContextBuilderTest.java @@ -29,10 +29,12 @@ import java.net.URI; import java.util.ArrayList; import java.util.List; -import org.jclouds.cloud.CloudContext; +import org.jclouds.blobstore.integration.internal.StubBlobStore; import org.jclouds.http.filters.BasicAuthentication; import org.jclouds.mezeo.pcs2.config.RestPCSBlobStoreModule; +import org.jclouds.mezeo.pcs2.config.StubPCSBlobStoreModule; import org.jclouds.mezeo.pcs2.config.PCSContextModule.PCSContextImpl; +import org.jclouds.mezeo.pcs2.internal.StubPCSConnection; import org.jclouds.mezeo.pcs2.reference.PCSConstants; import org.testng.annotations.Test; @@ -48,34 +50,37 @@ import com.google.inject.Module; public class PCSContextBuilderTest { public void testNewBuilder() { - PCSContextBuilder builder = new PCSContextBuilder(URI.create("https://localhost:8080"), "id", - "secret"); + PCSContextBuilder builder = new PCSContextBuilder(URI.create("https://localhost/pcsblob"), + "id", "secret"); assertEquals(builder.getProperties().getProperty(PCSConstants.PROPERTY_PCS2_ENDPOINT), - "https://localhost:8080"); + "https://localhost/pcsblob"); assertEquals(builder.getProperties().getProperty(PCSConstants.PROPERTY_PCS2_USER), "id"); assertEquals(builder.getProperties().getProperty(PCSConstants.PROPERTY_PCS2_PASSWORD), "secret"); } public void testBuildContext() { - CloudContext context = new PCSContextBuilder(URI - .create("https://localhost:8080"), "id", "secret").buildContext(); + PCSContext context = new PCSContextBuilder(URI.create("https://localhost/pcsblob"), "id", + "secret").withModule(new StubPCSBlobStoreModule()).buildContext(); assertEquals(context.getClass(), PCSContextImpl.class); + assertEquals(context.getApi().getClass(), StubPCSConnection.class); + assertEquals(context.getBlobStore().getClass(), StubBlobStore.class); assertEquals(context.getAccount(), "id"); - assertEquals(context.getEndPoint(), URI.create("https://localhost:8080")); + assertEquals(context.getEndPoint(), URI.create("https://localhost/pcsblob")); } public void testBuildInjector() { - Injector i = new PCSContextBuilder(URI.create("https://localhost:8080"), "id", "secret") + Injector i = new PCSContextBuilder(URI.create("https://localhost/pcsblob"), "id", "secret") .buildInjector(); assert i.getInstance(PCSContext.class) != null; + // TODO: test all things taken from context assert i.getInstance(BasicAuthentication.class) != null; } protected void testAddContextModule() { List modules = new ArrayList(); - PCSContextBuilder builder = new PCSContextBuilder(URI.create("https://localhost:8080"), "id", - "secret"); + PCSContextBuilder builder = new PCSContextBuilder(URI.create("https://localhost/pcsblob"), + "id", "secret"); builder.addContextModule(modules); assertEquals(modules.size(), 1); assertEquals(modules.get(0).getClass(), RestPCSBlobStoreModule.class); @@ -83,8 +88,8 @@ public class PCSContextBuilderTest { protected void addConnectionModule() { List modules = new ArrayList(); - PCSContextBuilder builder = new PCSContextBuilder(URI.create("https://localhost:8080"), "id", - "secret"); + PCSContextBuilder builder = new PCSContextBuilder(URI.create("https://localhost/pcsblob"), + "id", "secret"); builder.addConnectionModule(modules); assertEquals(modules.size(), 1); assertEquals(modules.get(0).getClass(), RestPCSBlobStoreModule.class); diff --git a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/PCSContextModuleTest.java b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/PCSContextModuleTest.java new file mode 100644 index 0000000000..fc7988f0cb --- /dev/null +++ b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/PCSContextModuleTest.java @@ -0,0 +1,72 @@ +/** + * + * Copyright (C) 2009 Global Cloud Specialists, Inc. + * + * ==================================================================== + * 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.mezeo.pcs2; + +import static org.testng.Assert.assertEquals; + +import org.jclouds.blobstore.BlobStoreMapsModule; +import org.jclouds.mezeo.pcs2.config.PCSContextModule; +import org.jclouds.mezeo.pcs2.config.StubPCSBlobStoreModule; +import org.jclouds.mezeo.pcs2.config.PCSContextModule.PCSContextImpl; +import org.jclouds.mezeo.pcs2.domain.ContainerMetadata; +import org.jclouds.mezeo.pcs2.domain.FileMetadata; +import org.jclouds.mezeo.pcs2.domain.PCSFile; +import org.jclouds.mezeo.pcs2.reference.PCSConstants; +import org.jclouds.util.Jsr330; +import org.testng.annotations.Test; + +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.google.inject.TypeLiteral; + +/** + * @author Adrian Cole + */ +@Test(groups = "unit", testName = "pcs2.PCSContextModuleTest") +public class PCSContextModuleTest { + Injector createInjector() { + return Guice.createInjector(new StubPCSBlobStoreModule(), BlobStoreMapsModule.Builder + .newBuilder(new TypeLiteral() { + }, new TypeLiteral() { + }, new TypeLiteral() { + }).build(), new PCSContextModule() { + @Override + protected void configure() { + bindConstant().annotatedWith(Jsr330.named(PCSConstants.PROPERTY_PCS2_USER)).to("user"); + bindConstant().annotatedWith(Jsr330.named(PCSConstants.PROPERTY_PCS2_PASSWORD)).to( + "key"); + bindConstant().annotatedWith(Jsr330.named(PCSConstants.PROPERTY_PCS2_ENDPOINT)).to( + "http://localhost"); + super.configure(); + } + }); + } + + @Test + void testContextImpl() { + PCSContext handler = createInjector().getInstance(PCSContext.class); + assertEquals(handler.getClass(), PCSContextImpl.class); + } + +} \ No newline at end of file diff --git a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/config/PCSContextModuleTest.java b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/config/PCSContextModuleTest.java index ba017dd541..86d51d7021 100644 --- a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/config/PCSContextModuleTest.java +++ b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/config/PCSContextModuleTest.java @@ -1,3 +1,26 @@ +/** + * + * Copyright (C) 2009 Global Cloud Specialists, Inc. + * + * ==================================================================== + * 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.mezeo.pcs2.config; import static org.testng.Assert.assertEquals; @@ -11,7 +34,6 @@ import org.jclouds.http.handlers.CloseContentAndSetExceptionErrorHandler; import org.jclouds.http.handlers.DelegatingErrorHandler; import org.jclouds.http.handlers.DelegatingRetryHandler; import org.jclouds.http.handlers.RedirectionRetryHandler; -import org.jclouds.mezeo.pcs2.PCSBlobStore; import org.jclouds.mezeo.pcs2.domain.ContainerMetadata; import org.jclouds.mezeo.pcs2.domain.FileMetadata; import org.jclouds.mezeo.pcs2.domain.PCSFile; @@ -32,8 +54,7 @@ public class PCSContextModuleTest { Injector createInjector() { return Guice.createInjector(new RestPCSBlobStoreModule(), BlobStoreMapsModule.Builder - .newBuilder(new TypeLiteral() { - }, new TypeLiteral() { + .newBuilder(new TypeLiteral() { }, new TypeLiteral() { }, new TypeLiteral() { }).build(), new PCSContextModule() { diff --git a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/config/StubPCSBlobStoreModule.java b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/config/StubPCSBlobStoreModule.java index 340064d2dc..9c64f3f844 100644 --- a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/config/StubPCSBlobStoreModule.java +++ b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/config/StubPCSBlobStoreModule.java @@ -27,12 +27,15 @@ import java.net.URI; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import org.jclouds.blobstore.BlobStore; +import org.jclouds.blobstore.integration.internal.StubBlobStore; import org.jclouds.cloud.ConfiguresCloudConnection; -import org.jclouds.http.functions.config.ParserModule; import org.jclouds.mezeo.pcs2.PCS; -import org.jclouds.mezeo.pcs2.PCSBlobStore; +import org.jclouds.mezeo.pcs2.PCSConnection; +import org.jclouds.mezeo.pcs2.domain.ContainerMetadata; +import org.jclouds.mezeo.pcs2.domain.FileMetadata; import org.jclouds.mezeo.pcs2.domain.PCSFile; -import org.jclouds.mezeo.pcs2.internal.StubPCSBlobStore; +import org.jclouds.mezeo.pcs2.internal.StubPCSConnection; import com.google.inject.AbstractModule; import com.google.inject.TypeLiteral; @@ -48,10 +51,12 @@ public class StubPCSBlobStoreModule extends AbstractModule { static final ConcurrentHashMap> map = new ConcurrentHashMap>(); protected void configure() { - install(new ParserModule()); bind(new TypeLiteral>>() { }).toInstance(map); - bind(PCSBlobStore.class).to(StubPCSBlobStore.class).asEagerSingleton(); + bind(new TypeLiteral>() { + }).to(new TypeLiteral>() { + }).asEagerSingleton(); + bind(PCSConnection.class).to(StubPCSConnection.class).asEagerSingleton(); bind(URI.class).annotatedWith(PCS.class).toInstance(URI.create("https://localhost/pcsblob")); } diff --git a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/functions/FindIdInContainerListTest.java b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/functions/FindIdInContainerListTest.java index 8c295d03c1..c22d0a2fb3 100644 --- a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/functions/FindIdInContainerListTest.java +++ b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/functions/FindIdInContainerListTest.java @@ -29,7 +29,7 @@ import static org.testng.Assert.assertEquals; import java.net.URI; import org.jclouds.blobstore.ContainerNotFoundException; -import org.jclouds.mezeo.pcs2.PCSBlobStore; +import org.jclouds.mezeo.pcs2.PCSConnection; import org.jclouds.mezeo.pcs2.domain.ContainerMetadata; import org.jclouds.util.DateService; import org.testng.annotations.Test; @@ -56,12 +56,12 @@ public class FindIdInContainerListTest { @Test(expectedExceptions = ContainerNotFoundException.class) public void testBad() { - FindIdInContainerList binder = new FindIdInContainerList(createNiceMock(PCSBlobStore.class)); + FindIdInContainerList binder = new FindIdInContainerList(createNiceMock(PCSConnection.class)); binder.idForNameInListOrException("hello", OF); } public void testGood() { - FindIdInContainerList binder = new FindIdInContainerList(createNiceMock(PCSBlobStore.class)); + FindIdInContainerList binder = new FindIdInContainerList(createNiceMock(PCSConnection.class)); assertEquals(binder.idForNameInListOrException("test1", OF), "7F143552-AAF5-11DE-BBB0-0BC388ED913B"); } diff --git a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/functions/FindIdInFileListIdTest.java b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/functions/FindIdInFileListIdTest.java index c6fd01edbd..cbf0c21ace 100644 --- a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/functions/FindIdInFileListIdTest.java +++ b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/functions/FindIdInFileListIdTest.java @@ -30,8 +30,8 @@ import java.net.URI; import javax.ws.rs.core.MediaType; +import org.jclouds.blobstore.BlobStore; import org.jclouds.blobstore.KeyNotFoundException; -import org.jclouds.mezeo.pcs2.PCSBlobStore; import org.jclouds.mezeo.pcs2.domain.FileMetadata; import org.jclouds.util.DateService; import org.testng.annotations.Test; @@ -47,8 +47,9 @@ import com.google.common.collect.ImmutableSortedSet; public class FindIdInFileListIdTest { private DateService dateService = new DateService(); - private final ImmutableSortedSet OF = ImmutableSortedSet.of(new FileMetadata("more", URI - .create("https://pcsbeta.mezeo.net/v2/files/5C81DADC-AAEE-11DE-9D55-B39340AEFF3A"), + private final ImmutableSortedSet OF = ImmutableSortedSet.of(new FileMetadata( + "more", + URI.create("https://pcsbeta.mezeo.net/v2/files/5C81DADC-AAEE-11DE-9D55-B39340AEFF3A"), dateService.fromSeconds(1254005157), dateService.fromSeconds(1254005158), dateService .fromSeconds(1254005159), "adrian@jclouds.org", false, false, 1, 254288, MediaType.APPLICATION_OCTET_STREAM, true), @@ -59,14 +60,16 @@ public class FindIdInFileListIdTest { .fromSeconds(1254000182), "adrian@jclouds.org", false, true, 3, 5, MediaType.TEXT_PLAIN, false)); + @SuppressWarnings("unchecked") @Test(expectedExceptions = KeyNotFoundException.class) public void testBad() { - FindIdInFileList binder = new FindIdInFileList(createNiceMock(PCSBlobStore.class)); + FindIdInFileList binder = new FindIdInFileList(createNiceMock(BlobStore.class)); binder.idForNameInListOrException("bob", "hello", OF); } + @SuppressWarnings("unchecked") public void testGood() { - FindIdInFileList binder = new FindIdInFileList(createNiceMock(PCSBlobStore.class)); + FindIdInFileList binder = new FindIdInFileList(createNiceMock(BlobStore.class)); assertEquals(binder.idForNameInListOrException("bob", "testfile.txt", OF), "9E4C5AFA-A98B-11DE-8B4C-C3884B4A2DA3"); } diff --git a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/integration/PCSBlobMapIntegrationTest.java b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/integration/PCSBlobMapIntegrationTest.java index 9a83e54c79..97c280831b 100644 --- a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/integration/PCSBlobMapIntegrationTest.java +++ b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/integration/PCSBlobMapIntegrationTest.java @@ -27,7 +27,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; import org.jclouds.blobstore.integration.internal.BaseBlobMapIntegrationTest; -import org.jclouds.mezeo.pcs2.PCSBlobStore; +import org.jclouds.mezeo.pcs2.PCSConnection; import org.jclouds.mezeo.pcs2.domain.ContainerMetadata; import org.jclouds.mezeo.pcs2.domain.FileMetadata; import org.jclouds.mezeo.pcs2.domain.PCSFile; @@ -38,7 +38,7 @@ import org.testng.annotations.Test; */ @Test(groups = { "integration", "live" }, testName = "cloudfiles.PCSBlobMapIntegrationTest") public class PCSBlobMapIntegrationTest extends - BaseBlobMapIntegrationTest { + BaseBlobMapIntegrationTest { @Override public void testContains() throws InterruptedException, ExecutionException, TimeoutException { diff --git a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/integration/PCSBlobStoreContainerIntegrationTest.java b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/integration/PCSBlobStoreContainerIntegrationTest.java index 01602baac6..ec7c50e58f 100755 --- a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/integration/PCSBlobStoreContainerIntegrationTest.java +++ b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/integration/PCSBlobStoreContainerIntegrationTest.java @@ -23,10 +23,8 @@ */ package org.jclouds.mezeo.pcs2.integration; -import java.util.concurrent.TimeUnit; - import org.jclouds.blobstore.integration.internal.BaseContainerIntegrationTest; -import org.jclouds.mezeo.pcs2.PCSBlobStore; +import org.jclouds.mezeo.pcs2.PCSConnection; import org.jclouds.mezeo.pcs2.domain.ContainerMetadata; import org.jclouds.mezeo.pcs2.domain.FileMetadata; import org.jclouds.mezeo.pcs2.domain.PCSFile; @@ -37,19 +35,6 @@ import org.testng.annotations.Test; */ @Test(groups = { "integration", "live" }, testName = "cloudfiles.PCSBlobContainerIntegrationTest") public class PCSBlobStoreContainerIntegrationTest extends - BaseContainerIntegrationTest { + BaseContainerIntegrationTest { - @Override - @Test(groups = { "integration", "live" }) - public void deleteContainerIfEmptyButHasContents() throws Exception { - String containerName = getContainerName(); - try { - addBlobToContainer(containerName, "test"); - // true is returned, since we can delete containers with contents - assert context.getApi().deleteContainer(containerName).get(10, TimeUnit.SECONDS); - } finally { - returnContainer(containerName); - } - } - } \ No newline at end of file diff --git a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/integration/PCSBlobStoreContainerLiveTest.java b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/integration/PCSBlobStoreContainerLiveTest.java index 6cd601967d..4b0ef3d6a7 100644 --- a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/integration/PCSBlobStoreContainerLiveTest.java +++ b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/integration/PCSBlobStoreContainerLiveTest.java @@ -24,7 +24,7 @@ package org.jclouds.mezeo.pcs2.integration; import org.jclouds.blobstore.integration.internal.BaseContainerLiveTest; -import org.jclouds.mezeo.pcs2.PCSBlobStore; +import org.jclouds.mezeo.pcs2.PCSConnection; import org.jclouds.mezeo.pcs2.domain.ContainerMetadata; import org.jclouds.mezeo.pcs2.domain.FileMetadata; import org.jclouds.mezeo.pcs2.domain.PCSFile; @@ -35,6 +35,6 @@ import org.testng.annotations.Test; */ @Test(groups = { "live" }, testName = "cloudfiles.PCSBlobContainerLiveTest") public class PCSBlobStoreContainerLiveTest extends - BaseContainerLiveTest { + BaseContainerLiveTest { } \ No newline at end of file diff --git a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/integration/PCSBlobStoreIntegrationTest.java b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/integration/PCSBlobStoreIntegrationTest.java index ad39388493..0b710dd8cb 100755 --- a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/integration/PCSBlobStoreIntegrationTest.java +++ b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/integration/PCSBlobStoreIntegrationTest.java @@ -31,7 +31,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; import org.jclouds.blobstore.integration.internal.BaseBlobIntegrationTest; -import org.jclouds.mezeo.pcs2.PCSBlobStore; +import org.jclouds.mezeo.pcs2.PCSConnection; import org.jclouds.mezeo.pcs2.domain.ContainerMetadata; import org.jclouds.mezeo.pcs2.domain.FileMetadata; import org.jclouds.mezeo.pcs2.domain.PCSFile; @@ -43,7 +43,7 @@ import org.testng.annotations.Test; */ @Test(groups = { "integration", "live" }, testName = "cloudfiles.PCSBlobIntegrationTest") public class PCSBlobStoreIntegrationTest extends - BaseBlobIntegrationTest { + BaseBlobIntegrationTest { @Override protected void validateMetadata(FileMetadata metadata) { assertEquals(metadata.getContentType(), "text/plain"); diff --git a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/integration/PCSBlobStoreLiveTest.java b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/integration/PCSBlobStoreLiveTest.java index 67d6929e1f..0df1199f6a 100644 --- a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/integration/PCSBlobStoreLiveTest.java +++ b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/integration/PCSBlobStoreLiveTest.java @@ -24,7 +24,7 @@ package org.jclouds.mezeo.pcs2.integration; import org.jclouds.blobstore.integration.internal.BaseBlobLiveTest; -import org.jclouds.mezeo.pcs2.PCSBlobStore; +import org.jclouds.mezeo.pcs2.PCSConnection; import org.jclouds.mezeo.pcs2.domain.ContainerMetadata; import org.jclouds.mezeo.pcs2.domain.FileMetadata; import org.jclouds.mezeo.pcs2.domain.PCSFile; @@ -35,7 +35,7 @@ import org.testng.annotations.Test; */ @Test(groups = { "live" }, testName = "cloudfiles.PCSBlobLiveTest") public class PCSBlobStoreLiveTest extends - BaseBlobLiveTest { + BaseBlobLiveTest { @Override @Test(enabled = false) diff --git a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/integration/PCSBlobStoreServiceIntegrationTest.java b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/integration/PCSBlobStoreServiceIntegrationTest.java index 877c8ae218..a66662ed3d 100644 --- a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/integration/PCSBlobStoreServiceIntegrationTest.java +++ b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/integration/PCSBlobStoreServiceIntegrationTest.java @@ -24,7 +24,7 @@ package org.jclouds.mezeo.pcs2.integration; import org.jclouds.blobstore.integration.internal.BaseServiceIntegrationTest; -import org.jclouds.mezeo.pcs2.PCSBlobStore; +import org.jclouds.mezeo.pcs2.PCSConnection; import org.jclouds.mezeo.pcs2.domain.ContainerMetadata; import org.jclouds.mezeo.pcs2.domain.FileMetadata; import org.jclouds.mezeo.pcs2.domain.PCSFile; @@ -35,6 +35,6 @@ import org.testng.annotations.Test; */ @Test(groups = { "integration", "live" }, testName = "cloudfiles.PCSBlobServiceIntegrationTest") public class PCSBlobStoreServiceIntegrationTest extends - BaseServiceIntegrationTest { + BaseServiceIntegrationTest { } \ No newline at end of file diff --git a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/integration/PCSInputStreamMapIntegrationTest.java b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/integration/PCSInputStreamMapIntegrationTest.java index 6e966cbfb8..70427ca50b 100644 --- a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/integration/PCSInputStreamMapIntegrationTest.java +++ b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/integration/PCSInputStreamMapIntegrationTest.java @@ -27,7 +27,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; import org.jclouds.blobstore.integration.internal.BaseInputStreamMapIntegrationTest; -import org.jclouds.mezeo.pcs2.PCSBlobStore; +import org.jclouds.mezeo.pcs2.PCSConnection; import org.jclouds.mezeo.pcs2.domain.ContainerMetadata; import org.jclouds.mezeo.pcs2.domain.FileMetadata; import org.jclouds.mezeo.pcs2.domain.PCSFile; @@ -38,7 +38,7 @@ import org.testng.annotations.Test; */ @Test(groups = { "integration", "live" }, testName = "cloudfiles.PCSBlobInputStreamMapIntegrationTest") public class PCSInputStreamMapIntegrationTest extends - BaseInputStreamMapIntegrationTest { + BaseInputStreamMapIntegrationTest { @Override public void testContainsBytesValue() throws InterruptedException, ExecutionException, diff --git a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/integration/PCSTestInitializer.java b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/integration/PCSTestInitializer.java index 6852d37ede..b90c6cef63 100644 --- a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/integration/PCSTestInitializer.java +++ b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/integration/PCSTestInitializer.java @@ -27,7 +27,7 @@ import java.net.URI; import org.jclouds.blobstore.integration.internal.BaseTestInitializer; import org.jclouds.logging.log4j.config.Log4JLoggingModule; -import org.jclouds.mezeo.pcs2.PCSBlobStore; +import org.jclouds.mezeo.pcs2.PCSConnection; import org.jclouds.mezeo.pcs2.PCSContext; import org.jclouds.mezeo.pcs2.PCSContextBuilder; import org.jclouds.mezeo.pcs2.PCSContextFactory; @@ -43,7 +43,7 @@ import com.google.inject.Module; * @author Adrian Cole */ public class PCSTestInitializer extends - BaseTestInitializer { + BaseTestInitializer { @Override protected PCSContext createLiveContext(Module configurationModule, String url, String app, diff --git a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/internal/StubPCSBlobStore.java b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/internal/StubPCSConnection.java similarity index 60% rename from mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/internal/StubPCSBlobStore.java rename to mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/internal/StubPCSConnection.java index 539c64218e..387312b3af 100644 --- a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/internal/StubPCSBlobStore.java +++ b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/internal/StubPCSConnection.java @@ -23,42 +23,49 @@ */ package org.jclouds.mezeo.pcs2.internal; -import java.util.Map; -import java.util.concurrent.ExecutionException; +import java.io.InputStream; +import java.net.URI; +import java.util.SortedSet; import java.util.concurrent.Future; -import javax.inject.Inject; -import javax.inject.Provider; - -import org.jclouds.blobstore.integration.internal.StubBlobStore; import org.jclouds.mezeo.pcs2.PCSBlobStore; +import org.jclouds.mezeo.pcs2.PCSConnection; import org.jclouds.mezeo.pcs2.domain.ContainerMetadata; import org.jclouds.mezeo.pcs2.domain.FileMetadata; import org.jclouds.mezeo.pcs2.domain.PCSFile; -import org.jclouds.util.DateService; /** * Implementation of {@link PCSBlobStore} which keeps all data in a local Map object. * * @author Adrian Cole */ -public class StubPCSBlobStore extends StubBlobStore - implements PCSBlobStore { +public class StubPCSConnection implements PCSConnection { - @Override - public Future deleteContainer(final String container) { - return new FutureBase() { - public Boolean get() throws InterruptedException, ExecutionException { - getContainerToBlobs().remove(container); - return true; - } - }; + public Future deleteContainer(URI container) { + throw new UnsupportedOperationException(); } - @Inject - protected StubPCSBlobStore(Map> containerToBlobs, - DateService dateService, Provider containerMetaProvider, - Provider blobProvider) { - super(containerToBlobs, dateService, containerMetaProvider, blobProvider); + public Future deleteFile(URI file) { + throw new UnsupportedOperationException(); + } + + public Future downloadFile(URI file) { + throw new UnsupportedOperationException(); + } + + public Future> listFiles(URI container) { + throw new UnsupportedOperationException(); + } + + public Future uploadFile(URI container, PCSFile object) { + throw new UnsupportedOperationException(); + } + + public Future createContainer(String container) { + throw new UnsupportedOperationException(); + } + + public SortedSet listContainers() { + throw new UnsupportedOperationException(); } } diff --git a/rackspace/cloudfiles/core/src/main/java/org/jclouds/rackspace/cloudfiles/CloudFilesBlobStore.java b/rackspace/cloudfiles/core/src/main/java/org/jclouds/rackspace/cloudfiles/CloudFilesBlobStore.java index 3f3db3760c..35380e2890 100644 --- a/rackspace/cloudfiles/core/src/main/java/org/jclouds/rackspace/cloudfiles/CloudFilesBlobStore.java +++ b/rackspace/cloudfiles/core/src/main/java/org/jclouds/rackspace/cloudfiles/CloudFilesBlobStore.java @@ -30,54 +30,38 @@ import java.util.concurrent.Future; import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.HEAD; -import javax.ws.rs.HeaderParam; -import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import org.jclouds.blobstore.BlobStore; import org.jclouds.blobstore.binders.BlobBinder; -import org.jclouds.blobstore.binders.UserMetadataBinder; import org.jclouds.blobstore.domain.Blob; import org.jclouds.blobstore.domain.BlobMetadata; import org.jclouds.blobstore.functions.BlobKey; -import org.jclouds.blobstore.functions.ThrowContainerNotFoundOn404; +import org.jclouds.blobstore.functions.ReturnVoidOnNotFoundOr404; import org.jclouds.blobstore.functions.ThrowKeyNotFoundOn404; import org.jclouds.http.functions.ParseETagHeader; import org.jclouds.http.functions.ReturnFalseOn404; -import org.jclouds.http.functions.ReturnTrueOn404; import org.jclouds.http.options.GetOptions; import org.jclouds.rackspace.CloudFiles; -import org.jclouds.rackspace.CloudFilesCDN; -import org.jclouds.rackspace.cloudfiles.domain.AccountMetadata; -import org.jclouds.rackspace.cloudfiles.domain.ContainerCDNMetadata; import org.jclouds.rackspace.cloudfiles.domain.ContainerMetadata; -import org.jclouds.rackspace.cloudfiles.functions.ParseAccountMetadataResponseFromHeaders; +import org.jclouds.rackspace.cloudfiles.functions.ClearAndDeleteContainerIfNotEmpty; import org.jclouds.rackspace.cloudfiles.functions.ParseBlobMetadataListFromJsonResponse; -import org.jclouds.rackspace.cloudfiles.functions.ParseCdnUriFromHeaders; -import org.jclouds.rackspace.cloudfiles.functions.ParseContainerCDNMetadataFromHeaders; -import org.jclouds.rackspace.cloudfiles.functions.ParseContainerCDNMetadataListFromGsonResponse; import org.jclouds.rackspace.cloudfiles.functions.ParseContainerListFromJsonResponse; import org.jclouds.rackspace.cloudfiles.functions.ParseObjectFromHeadersAndHttpContent; import org.jclouds.rackspace.cloudfiles.functions.ParseObjectMetadataFromHeaders; -import org.jclouds.rackspace.cloudfiles.functions.ReturnTrueOn404FalseOn409; -import org.jclouds.rackspace.cloudfiles.options.ListCdnContainerOptions; import org.jclouds.rackspace.cloudfiles.options.ListContainerOptions; -import org.jclouds.rackspace.cloudfiles.reference.CloudFilesHeaders; import org.jclouds.rackspace.filters.AuthenticateRequest; import org.jclouds.rest.Endpoint; import org.jclouds.rest.EntityParam; import org.jclouds.rest.ExceptionParser; -import org.jclouds.rest.Headers; import org.jclouds.rest.ParamParser; import org.jclouds.rest.QueryParams; import org.jclouds.rest.RequestFilters; import org.jclouds.rest.ResponseParser; import org.jclouds.rest.SkipEncoding; -import com.google.common.collect.Multimap; - /** * Provides access to Cloud Files via their REST API. *

@@ -114,9 +98,9 @@ public interface CloudFilesBlobStore extends Future createContainer(@PathParam("container") String container); @DELETE - @ExceptionParser(ReturnTrueOn404FalseOn409.class) + @ExceptionParser(ClearAndDeleteContainerIfNotEmpty.class) @Path("{container}") - Future deleteContainer(@PathParam("container") String container); + Future deleteContainer(@PathParam("container") String container); @GET @QueryParams(keys = "format", values = "json") @@ -152,64 +136,8 @@ public interface CloudFilesBlobStore extends BlobMetadata blobMetadata(@PathParam("container") String container, @PathParam("key") String key); @DELETE - @ExceptionParser(ReturnTrueOn404.class) + @ExceptionParser(ReturnVoidOnNotFoundOr404.class) @Path("{container}/{key}") - Future removeBlob(@PathParam("container") String container, @PathParam("key") String key); - - // Custom rackspace stuff - @HEAD - @ResponseParser(ParseAccountMetadataResponseFromHeaders.class) - @Path("/") - AccountMetadata getAccountMetadata(); - - @POST - @Path("{container}/{key}") - boolean setObjectMetadata(@PathParam("container") String container, - @PathParam("key") String key, - @EntityParam(UserMetadataBinder.class) Multimap userMetadata); - - // / cdn functionality below - - @GET - @ResponseParser(ParseContainerCDNMetadataListFromGsonResponse.class) - @QueryParams(keys = "format", values = "json") - @Path("/") - @Endpoint(CloudFilesCDN.class) - SortedSet listCDNContainers(ListCdnContainerOptions... options); - - // TODO: Container name is not included in CDN HEAD response headers, so we cannot populate it - // here. - @HEAD - @ResponseParser(ParseContainerCDNMetadataFromHeaders.class) - @ExceptionParser(ThrowContainerNotFoundOn404.class) - @Path("{container}") - @Endpoint(CloudFilesCDN.class) - ContainerCDNMetadata getCDNMetadata(@PathParam("container") String container); - - @PUT - @Path("{container}") - @ResponseParser(ParseCdnUriFromHeaders.class) - @Endpoint(CloudFilesCDN.class) - String enableCDN(@PathParam("container") String container, - @HeaderParam(CloudFilesHeaders.CDN_TTL) Long ttl); - - @PUT - @Path("{container}") - @ResponseParser(ParseCdnUriFromHeaders.class) - @Endpoint(CloudFilesCDN.class) - String enableCDN(@PathParam("container") String container); - - @POST - @Path("{container}") - @ResponseParser(ParseCdnUriFromHeaders.class) - @Endpoint(CloudFilesCDN.class) - String updateCDN(@PathParam("container") String container, - @HeaderParam(CloudFilesHeaders.CDN_TTL) Long ttl); - - @POST - @Path("{container}") - @Headers(keys = CloudFilesHeaders.CDN_ENABLED, values = "False") - @Endpoint(CloudFilesCDN.class) - boolean disableCDN(@PathParam("container") String container); + Future removeBlob(@PathParam("container") String container, @PathParam("key") String key); } diff --git a/rackspace/cloudfiles/core/src/main/java/org/jclouds/rackspace/cloudfiles/CloudFilesConnection.java b/rackspace/cloudfiles/core/src/main/java/org/jclouds/rackspace/cloudfiles/CloudFilesConnection.java new file mode 100644 index 0000000000..8e3cf09ad2 --- /dev/null +++ b/rackspace/cloudfiles/core/src/main/java/org/jclouds/rackspace/cloudfiles/CloudFilesConnection.java @@ -0,0 +1,228 @@ +/** + * + * Copyright (C) 2009 Global Cloud Specialists, Inc. + * + * ==================================================================== + * 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.rackspace.cloudfiles; + +import java.util.SortedSet; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; + +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.HEAD; +import javax.ws.rs.HeaderParam; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; + +import org.jclouds.blobstore.binders.BlobBinder; +import org.jclouds.blobstore.binders.UserMetadataBinder; +import org.jclouds.blobstore.domain.Blob; +import org.jclouds.blobstore.domain.BlobMetadata; +import org.jclouds.blobstore.functions.BlobKey; +import org.jclouds.blobstore.functions.ThrowContainerNotFoundOn404; +import org.jclouds.blobstore.functions.ThrowKeyNotFoundOn404; +import org.jclouds.http.functions.ParseETagHeader; +import org.jclouds.http.functions.ReturnTrueOn404; +import org.jclouds.http.options.GetOptions; +import org.jclouds.rackspace.CloudFiles; +import org.jclouds.rackspace.CloudFilesCDN; +import org.jclouds.rackspace.cloudfiles.domain.AccountMetadata; +import org.jclouds.rackspace.cloudfiles.domain.ContainerCDNMetadata; +import org.jclouds.rackspace.cloudfiles.domain.ContainerMetadata; +import org.jclouds.rackspace.cloudfiles.functions.ParseAccountMetadataResponseFromHeaders; +import org.jclouds.rackspace.cloudfiles.functions.ParseBlobMetadataListFromJsonResponse; +import org.jclouds.rackspace.cloudfiles.functions.ParseCdnUriFromHeaders; +import org.jclouds.rackspace.cloudfiles.functions.ParseContainerCDNMetadataFromHeaders; +import org.jclouds.rackspace.cloudfiles.functions.ParseContainerCDNMetadataListFromGsonResponse; +import org.jclouds.rackspace.cloudfiles.functions.ParseContainerListFromJsonResponse; +import org.jclouds.rackspace.cloudfiles.functions.ParseObjectFromHeadersAndHttpContent; +import org.jclouds.rackspace.cloudfiles.functions.ParseObjectMetadataFromHeaders; +import org.jclouds.rackspace.cloudfiles.functions.ReturnTrueOn404FalseOn409; +import org.jclouds.rackspace.cloudfiles.options.ListCdnContainerOptions; +import org.jclouds.rackspace.cloudfiles.options.ListContainerOptions; +import org.jclouds.rackspace.cloudfiles.reference.CloudFilesHeaders; +import org.jclouds.rackspace.filters.AuthenticateRequest; +import org.jclouds.rest.Endpoint; +import org.jclouds.rest.EntityParam; +import org.jclouds.rest.ExceptionParser; +import org.jclouds.rest.Headers; +import org.jclouds.rest.ParamParser; +import org.jclouds.rest.QueryParams; +import org.jclouds.rest.RequestFilters; +import org.jclouds.rest.ResponseParser; +import org.jclouds.rest.SkipEncoding; + +import com.google.common.collect.Multimap; + +/** + * Provides access to Cloud Files via their REST API. + *

+ * All commands return a Future of the result from Cloud Files. Any exceptions incurred during + * processing will be wrapped in an {@link ExecutionException} as documented in {@link Future#get()}. + * + * @see + * @author Adrian Cole + */ +@SkipEncoding('/') +@RequestFilters(AuthenticateRequest.class) +@Endpoint(CloudFiles.class) +public interface CloudFilesConnection { + /** + * HEAD operations against an account are performed to retrieve the number of Containers and the + * total bytes stored in Cloud Files for the account. + *

+ * Determine the number of Containers within the account and the total bytes stored. Since the + * storage system is designed to store large amounts of data, care should be taken when + * representing the total bytes response as an integer; when possible, convert it to a 64-bit + * unsigned integer if your platform supports that primitive type. + */ + @HEAD + @ResponseParser(ParseAccountMetadataResponseFromHeaders.class) + @Path("/") + AccountMetadata getAccountStatistics(); + + /** + * GET operations against the X-Storage-Url for an account are performed to retrieve a list of + * existing storage + *

+ * Containers ordered by name. The following list describes the optional query parameters that + * are supported with this request. + *

+ *

+ * At this time, a prex query parameter is not supported at the Account level. + * + *

Large Container Lists

+ * The system will return a maximum of 10,000 Container names per request. To retrieve subsequent + * container names, another request must be made with a marker parameter. The marker indicates + * where the last list left off and the system will return container names greater than this + * marker, up to 10,000 again. Note that the marker value should be URL encoded prior to + * sending the HTTP request. + *

+ * If 10,000 is larger than desired, a limit parameter may be given. + *

+ * If the number of container names returned equals the limit given (or 10,000 if no limit is + * given), it can be assumed there are more container names to be listed. If the container name + * list is exactly divisible by the limit, the last request will simply have no content. + */ + @GET + @ResponseParser(ParseContainerListFromJsonResponse.class) + @QueryParams(keys = "format", values = "json") + @Path("/") + SortedSet listContainers(ListContainerOptions... options); + + @POST + @Path("{container}/{key}") + boolean setObjectMetadata(@PathParam("container") String container, + @PathParam("key") String key, + @EntityParam(UserMetadataBinder.class) Multimap userMetadata); + + @GET + @ResponseParser(ParseContainerCDNMetadataListFromGsonResponse.class) + @QueryParams(keys = "format", values = "json") + @Path("/") + @Endpoint(CloudFilesCDN.class) + SortedSet listCDNContainers(ListCdnContainerOptions... options); + + // TODO: Container name is not included in CDN HEAD response headers, so we cannot populate it + // here. + @HEAD + @ResponseParser(ParseContainerCDNMetadataFromHeaders.class) + @ExceptionParser(ThrowContainerNotFoundOn404.class) + @Path("{container}") + @Endpoint(CloudFilesCDN.class) + ContainerCDNMetadata getCDNMetadata(@PathParam("container") String container); + + @PUT + @Path("{container}") + @ResponseParser(ParseCdnUriFromHeaders.class) + @Endpoint(CloudFilesCDN.class) + String enableCDN(@PathParam("container") String container, + @HeaderParam(CloudFilesHeaders.CDN_TTL) Long ttl); + + @PUT + @Path("{container}") + @ResponseParser(ParseCdnUriFromHeaders.class) + @Endpoint(CloudFilesCDN.class) + String enableCDN(@PathParam("container") String container); + + @POST + @Path("{container}") + @ResponseParser(ParseCdnUriFromHeaders.class) + @Endpoint(CloudFilesCDN.class) + String updateCDN(@PathParam("container") String container, + @HeaderParam(CloudFilesHeaders.CDN_TTL) Long ttl); + + @POST + @Path("{container}") + @Headers(keys = CloudFilesHeaders.CDN_ENABLED, values = "False") + @Endpoint(CloudFilesCDN.class) + boolean disableCDN(@PathParam("container") String container); + + @PUT + @Path("{container}") + Future createContainer(@PathParam("container") String container); + + @DELETE + @ExceptionParser(ReturnTrueOn404FalseOn409.class) + @Path("{container}") + Future deleteContainerIfEmpty(@PathParam("container") String container); + + @GET + @QueryParams(keys = "format", values = "json") + @ResponseParser(ParseBlobMetadataListFromJsonResponse.class) + @Path("{container}") + Future> listObjects(@PathParam("container") String container); + + @PUT + @Path("{container}/{key}") + @ResponseParser(ParseETagHeader.class) + Future putObject( + @PathParam("container") String container, + @PathParam("key") @ParamParser(BlobKey.class) @EntityParam(BlobBinder.class) Blob object); + + @GET + @ResponseParser(ParseObjectFromHeadersAndHttpContent.class) + @ExceptionParser(ThrowKeyNotFoundOn404.class) + @Path("{container}/{key}") + Future> getObject(@PathParam("container") String container, + @PathParam("key") String key, GetOptions... options); + + @HEAD + @ResponseParser(ParseObjectMetadataFromHeaders.class) + @ExceptionParser(ThrowKeyNotFoundOn404.class) + @Path("{container}/{key}") + BlobMetadata getObjectMetadata(@PathParam("container") String container, @PathParam("key") String key); + + @DELETE + @ExceptionParser(ReturnTrueOn404.class) + @Path("{container}/{key}") + Future removeObject(@PathParam("container") String container, @PathParam("key") String key); + +} diff --git a/rackspace/cloudfiles/core/src/main/java/org/jclouds/rackspace/cloudfiles/CloudFilesContext.java b/rackspace/cloudfiles/core/src/main/java/org/jclouds/rackspace/cloudfiles/CloudFilesContext.java index 12eb7ec76e..afe534a92c 100644 --- a/rackspace/cloudfiles/core/src/main/java/org/jclouds/rackspace/cloudfiles/CloudFilesContext.java +++ b/rackspace/cloudfiles/core/src/main/java/org/jclouds/rackspace/cloudfiles/CloudFilesContext.java @@ -38,7 +38,8 @@ import org.jclouds.rackspace.cloudfiles.domain.ContainerMetadata; * @author Adrian Cole * */ -public interface CloudFilesContext extends - BlobStoreContext> { +public interface CloudFilesContext + extends + BlobStoreContext> { } \ No newline at end of file diff --git a/rackspace/cloudfiles/core/src/main/java/org/jclouds/rackspace/cloudfiles/CloudFilesContextBuilder.java b/rackspace/cloudfiles/core/src/main/java/org/jclouds/rackspace/cloudfiles/CloudFilesContextBuilder.java index f58c5bb0b0..367c1497dd 100644 --- a/rackspace/cloudfiles/core/src/main/java/org/jclouds/rackspace/cloudfiles/CloudFilesContextBuilder.java +++ b/rackspace/cloudfiles/core/src/main/java/org/jclouds/rackspace/cloudfiles/CloudFilesContextBuilder.java @@ -59,7 +59,7 @@ import com.google.inject.TypeLiteral; */ public class CloudFilesContextBuilder extends - BlobStoreContextBuilder> { + BlobStoreContextBuilder> { @Override public CloudFilesContext buildContext() { @@ -133,7 +133,7 @@ public class CloudFilesContextBuilder } public CloudFilesContextBuilder(Properties props) { - super(new TypeLiteral() { + super(new TypeLiteral() { }, new TypeLiteral() { }, new TypeLiteral() { }, new TypeLiteral>() { diff --git a/rackspace/cloudfiles/core/src/main/java/org/jclouds/rackspace/cloudfiles/binders/CFObjectBinder.java b/rackspace/cloudfiles/core/src/main/java/org/jclouds/rackspace/cloudfiles/binders/CFObjectBinder.java index e72bf5913d..f39cb5d355 100644 --- a/rackspace/cloudfiles/core/src/main/java/org/jclouds/rackspace/cloudfiles/binders/CFObjectBinder.java +++ b/rackspace/cloudfiles/core/src/main/java/org/jclouds/rackspace/cloudfiles/binders/CFObjectBinder.java @@ -23,6 +23,7 @@ */ package org.jclouds.rackspace.cloudfiles.binders; +import static com.google.common.base.Preconditions.checkArgument; import static org.jclouds.blobstore.reference.BlobStoreConstants.PROPERTY_USER_METADATA_PREFIX; import javax.ws.rs.core.HttpHeaders; @@ -44,6 +45,8 @@ public class CFObjectBinder extends BlobBinder { public void addEntityToRequest(Object entity, HttpRequest request) { Blob object = (Blob) entity; if (object.getMetadata().getSize() >= 0) { + checkArgument(object.getContentLength() <= 5 * 1024 * 1024 * 1024, + "maximum size for put object is 5GB"); request.getHeaders().put(HttpHeaders.CONTENT_LENGTH, object.getMetadata().getSize() + ""); } else { // Enable "chunked"/"streamed" data, where the size needn't be known in advance. diff --git a/rackspace/cloudfiles/core/src/main/java/org/jclouds/rackspace/cloudfiles/config/CloudFilesContextModule.java b/rackspace/cloudfiles/core/src/main/java/org/jclouds/rackspace/cloudfiles/config/CloudFilesContextModule.java index 5ee053a33d..3cd0c841f9 100644 --- a/rackspace/cloudfiles/core/src/main/java/org/jclouds/rackspace/cloudfiles/config/CloudFilesContextModule.java +++ b/rackspace/cloudfiles/core/src/main/java/org/jclouds/rackspace/cloudfiles/config/CloudFilesContextModule.java @@ -29,13 +29,14 @@ import javax.inject.Inject; import javax.inject.Named; import javax.inject.Provider; +import org.jclouds.blobstore.BlobStore; import org.jclouds.blobstore.BlobStoreContextImpl; import org.jclouds.blobstore.BlobMap.Factory; import org.jclouds.blobstore.domain.Blob; import org.jclouds.blobstore.domain.BlobMetadata; import org.jclouds.lifecycle.Closer; import org.jclouds.rackspace.CloudFiles; -import org.jclouds.rackspace.cloudfiles.CloudFilesBlobStore; +import org.jclouds.rackspace.cloudfiles.CloudFilesConnection; import org.jclouds.rackspace.cloudfiles.CloudFilesContext; import org.jclouds.rackspace.cloudfiles.domain.ContainerMetadata; import org.jclouds.rackspace.reference.RackspaceConstants; @@ -52,16 +53,17 @@ public class CloudFilesContextModule extends AbstractModule { public static class CloudFilesContextImpl extends - BlobStoreContextImpl> + BlobStoreContextImpl> implements CloudFilesContext { @Inject CloudFilesContextImpl(Factory> blobMapFactory, org.jclouds.blobstore.InputStreamMap.Factory inputStreamMapFactory, Closer closer, Provider> blobProvider, - CloudFilesBlobStore defaultApi, @CloudFiles URI endPoint, + BlobStore> blobStore, + CloudFilesConnection defaultApi, @CloudFiles URI endPoint, @Named(RackspaceConstants.PROPERTY_RACKSPACE_USER) String account) { - super(blobMapFactory, inputStreamMapFactory, closer, blobProvider, defaultApi, endPoint, - account); + super(blobMapFactory, inputStreamMapFactory, closer, blobProvider, blobStore, defaultApi, + endPoint, account); } } diff --git a/rackspace/cloudfiles/core/src/main/java/org/jclouds/rackspace/cloudfiles/config/RestCloudFilesBlobStoreModule.java b/rackspace/cloudfiles/core/src/main/java/org/jclouds/rackspace/cloudfiles/config/RestCloudFilesBlobStoreModule.java index 48d19f777e..ac48561773 100755 --- a/rackspace/cloudfiles/core/src/main/java/org/jclouds/rackspace/cloudfiles/config/RestCloudFilesBlobStoreModule.java +++ b/rackspace/cloudfiles/core/src/main/java/org/jclouds/rackspace/cloudfiles/config/RestCloudFilesBlobStoreModule.java @@ -25,9 +25,14 @@ package org.jclouds.rackspace.cloudfiles.config; import javax.inject.Singleton; +import org.jclouds.blobstore.BlobStore; +import org.jclouds.blobstore.domain.Blob; +import org.jclouds.blobstore.domain.BlobMetadata; import org.jclouds.cloud.ConfiguresCloudConnection; import org.jclouds.http.RequiresHttp; import org.jclouds.rackspace.cloudfiles.CloudFilesBlobStore; +import org.jclouds.rackspace.cloudfiles.CloudFilesConnection; +import org.jclouds.rackspace.cloudfiles.domain.ContainerMetadata; import org.jclouds.rest.RestClientFactory; import com.google.inject.AbstractModule; @@ -47,7 +52,14 @@ public class RestCloudFilesBlobStoreModule extends AbstractModule { @Provides @Singleton - public CloudFilesBlobStore provideConnection(RestClientFactory factory) { + public CloudFilesConnection provideConnection(RestClientFactory factory) { + return factory.create(CloudFilesConnection.class); + } + + @Provides + @Singleton + public BlobStore> provideBlobStore( + RestClientFactory factory) { return factory.create(CloudFilesBlobStore.class); } } \ No newline at end of file diff --git a/rackspace/cloudfiles/core/src/main/java/org/jclouds/rackspace/cloudfiles/functions/ClearAndDeleteContainerIfNotEmpty.java b/rackspace/cloudfiles/core/src/main/java/org/jclouds/rackspace/cloudfiles/functions/ClearAndDeleteContainerIfNotEmpty.java new file mode 100644 index 0000000000..7b640e8e88 --- /dev/null +++ b/rackspace/cloudfiles/core/src/main/java/org/jclouds/rackspace/cloudfiles/functions/ClearAndDeleteContainerIfNotEmpty.java @@ -0,0 +1,45 @@ +/** + * + * Copyright (C) 2009 Global Cloud Specialists, Inc. + * + * ==================================================================== + * 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.rackspace.cloudfiles.functions; + +import javax.inject.Inject; + +import org.jclouds.blobstore.BlobStore; +import org.jclouds.blobstore.domain.Blob; +import org.jclouds.blobstore.domain.BlobMetadata; +import org.jclouds.blobstore.functions.ClearAndDeleteIfNotEmpty; +import org.jclouds.blobstore.strategy.ClearContainerStrategy; +import org.jclouds.rackspace.cloudfiles.domain.ContainerMetadata; + +public class ClearAndDeleteContainerIfNotEmpty extends + ClearAndDeleteIfNotEmpty> { + + @Inject + ClearAndDeleteContainerIfNotEmpty( + ClearContainerStrategy> clear, + BlobStore> connection) { + super(clear, connection); + } + +} diff --git a/rackspace/cloudfiles/core/src/main/java/org/jclouds/rackspace/cloudfiles/functions/ParseBlobMetadataListFromJsonResponse.java b/rackspace/cloudfiles/core/src/main/java/org/jclouds/rackspace/cloudfiles/functions/ParseBlobMetadataListFromJsonResponse.java index 95240e25d6..f741c5d3b7 100644 --- a/rackspace/cloudfiles/core/src/main/java/org/jclouds/rackspace/cloudfiles/functions/ParseBlobMetadataListFromJsonResponse.java +++ b/rackspace/cloudfiles/core/src/main/java/org/jclouds/rackspace/cloudfiles/functions/ParseBlobMetadataListFromJsonResponse.java @@ -84,6 +84,7 @@ public class ParseBlobMetadataListFromJsonResponse extends ParseJson= initialContainerCount); - assertTrue(connection.deleteContainer(containerName).get(10, TimeUnit.SECONDS)); + assertTrue(connection.deleteContainerIfEmpty(containerName).get(10, TimeUnit.SECONDS)); } @Test public void testDeleteContainer() throws Exception { - assertTrue(connection.deleteContainer("does-not-exist").get(10, TimeUnit.SECONDS)); + assertTrue(connection.deleteContainerIfEmpty("does-not-exist").get(10, TimeUnit.SECONDS)); String containerName = bucketPrefix + ".testDeleteContainer"; assertTrue(connection.createContainer(containerName).get(10, TimeUnit.SECONDS)); - assertTrue(connection.deleteContainer(containerName).get(10, TimeUnit.SECONDS)); + assertTrue(connection.deleteContainerIfEmpty(containerName).get(10, TimeUnit.SECONDS)); } @Test @@ -255,8 +265,8 @@ public class CloudFilesBlobStoreLiveTest { } catch (Exception e) { } - assertTrue(connection.deleteContainer(containerName1).get(10, TimeUnit.SECONDS)); - assertTrue(connection.deleteContainer(containerName2).get(10, TimeUnit.SECONDS)); + assertTrue(connection.deleteContainerIfEmpty(containerName1).get(10, TimeUnit.SECONDS)); + assertTrue(connection.deleteContainerIfEmpty(containerName2).get(10, TimeUnit.SECONDS)); } @Test @@ -274,20 +284,19 @@ public class CloudFilesBlobStoreLiveTest { object.getMetadata().setContentType("text/plain"); object.getMetadata().getUserMetadata().put("Metadata", "metadata-value"); byte[] md5 = object.getMetadata().getContentMD5(); - byte[] newEtag = connection.putBlob(containerName, object).get(10, TimeUnit.SECONDS); + byte[] newEtag = connection.putObject(containerName, object).get(10, TimeUnit.SECONDS); assertEquals(HttpUtils.toHexString(md5), HttpUtils.toHexString(object.getMetadata() .getContentMD5())); // Test HEAD of missing object try { - connection.blobMetadata(containerName, "non-existent-object"); + connection.getObjectMetadata(containerName, "non-existent-object"); assert false; - } catch (Exception e) { - e.printStackTrace(); + } catch (KeyNotFoundException e) { } // Test HEAD of object - BlobMetadata metadata = connection.blobMetadata(containerName, object.getKey()); + BlobMetadata metadata = connection.getObjectMetadata(containerName, object.getKey()); // TODO assertEquals(metadata.getKey(), object.getKey()); assertEquals(metadata.getSize(), data.length()); assertEquals(metadata.getContentType(), "text/plain"); @@ -305,13 +314,12 @@ public class CloudFilesBlobStoreLiveTest { // Test GET of missing object try { - connection.getBlob(containerName, "non-existent-object").get(10, TimeUnit.SECONDS); + connection.getObject(containerName, "non-existent-object").get(10, TimeUnit.SECONDS); assert false; - } catch (Exception e) { - e.printStackTrace(); + } catch (KeyNotFoundException e) { } // Test GET of object (including updated metadata) - Blob getBlob = connection.getBlob(containerName, object.getKey()).get(120, + Blob getBlob = connection.getObject(containerName, object.getKey()).get(120, TimeUnit.SECONDS); assertEquals(IOUtils.toString((InputStream) getBlob.getData()), data); // TODO assertEquals(getBlob.getKey(), object.getKey()); @@ -333,7 +341,7 @@ public class CloudFilesBlobStoreLiveTest { String incorrectEtag = "0" + correctEtag.substring(1); object.getMetadata().setETag(HttpUtils.fromHexString(incorrectEtag)); try { - connection.putBlob(containerName, object).get(10, TimeUnit.SECONDS); + connection.putObject(containerName, object).get(10, TimeUnit.SECONDS); } catch (Throwable e) { assertEquals(e.getCause().getClass(), HttpResponseException.class); assertEquals(((HttpResponseException) e.getCause()).getResponse().getStatusCode(), 422); @@ -343,14 +351,14 @@ public class CloudFilesBlobStoreLiveTest { ByteArrayInputStream bais = new ByteArrayInputStream(data.getBytes("UTF-8")); object = new Blob("chunked-object"); object.setData(bais); - newEtag = connection.putBlob(containerName, object).get(10, TimeUnit.SECONDS); + newEtag = connection.putObject(containerName, object).get(10, TimeUnit.SECONDS); assertEquals(HttpUtils.toHexString(md5), HttpUtils.toHexString(getBlob.getMetadata() .getContentMD5())); // Test GET with options // Non-matching ETag try { - connection.getBlob(containerName, object.getKey(), + connection.getObject(containerName, object.getKey(), GetOptions.Builder.ifETagDoesntMatch(newEtag)).get(120, TimeUnit.SECONDS); } catch (Exception e) { assertEquals(e.getCause().getClass(), HttpResponseException.class); @@ -358,17 +366,17 @@ public class CloudFilesBlobStoreLiveTest { } // Matching ETag - getBlob = connection.getBlob(containerName, object.getKey(), + getBlob = connection.getObject(containerName, object.getKey(), GetOptions.Builder.ifETagMatches(newEtag)).get(120, TimeUnit.SECONDS); assertEquals(getBlob.getMetadata().getETag(), newEtag); - getBlob = connection.getBlob(containerName, object.getKey(), GetOptions.Builder.startAt(8)) + getBlob = connection.getObject(containerName, object.getKey(), GetOptions.Builder.startAt(8)) .get(120, TimeUnit.SECONDS); assertEquals(IOUtils.toString((InputStream) getBlob.getData()), data.substring(8)); - assertTrue(connection.removeBlob(containerName, "object").get(10, TimeUnit.SECONDS)); - assertTrue(connection.removeBlob(containerName, "chunked-object").get(10, TimeUnit.SECONDS)); + assertTrue(connection.removeObject(containerName, "object").get(10, TimeUnit.SECONDS)); + assertTrue(connection.removeObject(containerName, "chunked-object").get(10, TimeUnit.SECONDS)); - assertTrue(connection.deleteContainer(containerName).get(10, TimeUnit.SECONDS)); + assertTrue(connection.deleteContainerIfEmpty(containerName).get(10, TimeUnit.SECONDS)); } } diff --git a/rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/CloudFilesContextBuilderTest.java b/rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/CloudFilesContextBuilderTest.java index 46b4f23130..9ea0a94dd2 100755 --- a/rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/CloudFilesContextBuilderTest.java +++ b/rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/CloudFilesContextBuilderTest.java @@ -30,10 +30,11 @@ import java.net.URI; import java.util.ArrayList; import java.util.List; -import org.jclouds.cloud.CloudContext; +import org.jclouds.blobstore.integration.internal.StubBlobStore; import org.jclouds.rackspace.StubRackspaceAuthenticationModule; import org.jclouds.rackspace.cloudfiles.config.CloudFilesContextModule.CloudFilesContextImpl; -import org.jclouds.rackspace.cloudfiles.integration.StubCloudFilesConnectionModule; +import org.jclouds.rackspace.cloudfiles.integration.StubCloudFilesBlobStoreModule; +import org.jclouds.rackspace.cloudfiles.internal.StubCloudFilesConnection; import org.jclouds.rackspace.config.RestRackspaceAuthenticationModule; import org.jclouds.rackspace.reference.RackspaceConstants; import org.testng.annotations.Test; @@ -62,17 +63,19 @@ public class CloudFilesContextBuilderTest { } public void testBuildContext() { - CloudContext context = new CloudFilesContextBuilder("id", "secret") - .withModules(new StubCloudFilesConnectionModule(), - new StubRackspaceAuthenticationModule()).buildContext(); + CloudFilesContext context = new CloudFilesContextBuilder("id", "secret").withModules( + new StubCloudFilesBlobStoreModule(), new StubRackspaceAuthenticationModule()) + .buildContext(); assertEquals(context.getClass(), CloudFilesContextImpl.class); + assertEquals(context.getApi().getClass(), StubCloudFilesConnection.class); + assertEquals(context.getBlobStore().getClass(), StubBlobStore.class); assertEquals(context.getAccount(), "id"); assertEquals(context.getEndPoint(), URI.create("http://localhost/rackspacestub/cloudfiles")); } public void testBuildInjector() { Injector i = new CloudFilesContextBuilder("id", "secret").withModules( - new StubCloudFilesConnectionModule(), new StubRackspaceAuthenticationModule()) + new StubCloudFilesBlobStoreModule(), new StubRackspaceAuthenticationModule()) .buildInjector(); assert i.getInstance(CloudFilesContextImpl.class) != null; } diff --git a/rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/CloudFilesContextModuleTest.java b/rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/CloudFilesContextModuleTest.java new file mode 100644 index 0000000000..3cf5200ae2 --- /dev/null +++ b/rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/CloudFilesContextModuleTest.java @@ -0,0 +1,74 @@ +/** + * + * Copyright (C) 2009 Global Cloud Specialists, Inc. + * + * ==================================================================== + * 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.rackspace.cloudfiles; + +import static org.testng.Assert.assertEquals; + +import org.jclouds.blobstore.BlobStoreMapsModule; +import org.jclouds.blobstore.domain.Blob; +import org.jclouds.blobstore.domain.BlobMetadata; +import org.jclouds.rackspace.StubRackspaceAuthenticationModule; +import org.jclouds.rackspace.cloudfiles.config.CloudFilesContextModule; +import org.jclouds.rackspace.cloudfiles.config.CloudFilesContextModule.CloudFilesContextImpl; +import org.jclouds.rackspace.cloudfiles.domain.ContainerMetadata; +import org.jclouds.rackspace.cloudfiles.integration.StubCloudFilesBlobStoreModule; +import org.jclouds.rackspace.cloudfiles.reference.CloudFilesConstants; +import org.jclouds.util.Jsr330; +import org.testng.annotations.Test; + +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.google.inject.TypeLiteral; + +/** + * @author Adrian Cole + */ +@Test(groups = "unit", testName = "azureblob.CloudFilesContextModuleTest") +public class CloudFilesContextModuleTest { + + Injector createInjector() { + return Guice.createInjector(new StubCloudFilesBlobStoreModule(), + new StubRackspaceAuthenticationModule(), BlobStoreMapsModule.Builder.newBuilder( + new TypeLiteral() { + }, new TypeLiteral() { + }, new TypeLiteral>() { + }).build(), new CloudFilesContextModule() { + @Override + protected void configure() { + bindConstant().annotatedWith( + Jsr330.named(CloudFilesConstants.PROPERTY_RACKSPACE_USER)).to("user"); + bindConstant().annotatedWith( + Jsr330.named(CloudFilesConstants.PROPERTY_RACKSPACE_KEY)).to("key"); + super.configure(); + } + }); + } + + @Test + void testContextImpl() { + CloudFilesContext context = createInjector().getInstance(CloudFilesContext.class); + assertEquals(context.getClass(), CloudFilesContextImpl.class); + } + +} \ No newline at end of file diff --git a/rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/integration/CloudFilesBlobIntegrationTest.java b/rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/integration/CloudFilesBlobIntegrationTest.java index f61aa1b0e2..5fbf97d4b3 100755 --- a/rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/integration/CloudFilesBlobIntegrationTest.java +++ b/rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/integration/CloudFilesBlobIntegrationTest.java @@ -30,7 +30,7 @@ import java.util.concurrent.TimeoutException; import org.jclouds.blobstore.domain.Blob; import org.jclouds.blobstore.domain.BlobMetadata; import org.jclouds.blobstore.integration.internal.BaseBlobIntegrationTest; -import org.jclouds.rackspace.cloudfiles.CloudFilesBlobStore; +import org.jclouds.rackspace.cloudfiles.CloudFilesConnection; import org.jclouds.rackspace.cloudfiles.domain.ContainerMetadata; import org.testng.annotations.Test; @@ -42,7 +42,7 @@ import org.testng.annotations.Test; @Test(groups = { "integration", "live" }, testName = "cloudfiles.CloudFilesBlobIntegrationTest") public class CloudFilesBlobIntegrationTest extends - BaseBlobIntegrationTest> { + BaseBlobIntegrationTest> { @Test(enabled = false) @Override diff --git a/rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/integration/CloudFilesBlobLiveTest.java b/rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/integration/CloudFilesBlobLiveTest.java index 6b58530b7c..edda27c8a1 100644 --- a/rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/integration/CloudFilesBlobLiveTest.java +++ b/rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/integration/CloudFilesBlobLiveTest.java @@ -26,7 +26,7 @@ package org.jclouds.rackspace.cloudfiles.integration; import org.jclouds.blobstore.domain.Blob; import org.jclouds.blobstore.domain.BlobMetadata; import org.jclouds.blobstore.integration.internal.BaseBlobLiveTest; -import org.jclouds.rackspace.cloudfiles.CloudFilesBlobStore; +import org.jclouds.rackspace.cloudfiles.CloudFilesConnection; import org.jclouds.rackspace.cloudfiles.domain.ContainerMetadata; import org.testng.annotations.Test; @@ -36,7 +36,8 @@ import org.testng.annotations.Test; * @author Adrian Cole */ @Test(groups = { "live" }, testName = "cloudfiles.CloudFilesBlobLiveTest") -public class CloudFilesBlobLiveTest extends - BaseBlobLiveTest> { +public class CloudFilesBlobLiveTest + extends + BaseBlobLiveTest> { } \ No newline at end of file diff --git a/rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/integration/CloudFilesBlobMapIntegrationTest.java b/rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/integration/CloudFilesBlobMapIntegrationTest.java index 0f20b81356..af83b87241 100644 --- a/rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/integration/CloudFilesBlobMapIntegrationTest.java +++ b/rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/integration/CloudFilesBlobMapIntegrationTest.java @@ -26,7 +26,7 @@ package org.jclouds.rackspace.cloudfiles.integration; import org.jclouds.blobstore.domain.Blob; import org.jclouds.blobstore.domain.BlobMetadata; import org.jclouds.blobstore.integration.internal.BaseBlobMapIntegrationTest; -import org.jclouds.rackspace.cloudfiles.CloudFilesBlobStore; +import org.jclouds.rackspace.cloudfiles.CloudFilesConnection; import org.jclouds.rackspace.cloudfiles.domain.ContainerMetadata; import org.testng.annotations.Test; @@ -36,6 +36,6 @@ import org.testng.annotations.Test; @Test(groups = { "integration", "live" }, testName = "cloudfiles.CloudFilesBlobMapIntegrationTest") public class CloudFilesBlobMapIntegrationTest extends - BaseBlobMapIntegrationTest> { + BaseBlobMapIntegrationTest> { } \ No newline at end of file diff --git a/rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/integration/CloudFilesContainerIntegrationTest.java b/rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/integration/CloudFilesContainerIntegrationTest.java index 885c1390da..9b3ebfeb08 100755 --- a/rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/integration/CloudFilesContainerIntegrationTest.java +++ b/rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/integration/CloudFilesContainerIntegrationTest.java @@ -26,7 +26,7 @@ package org.jclouds.rackspace.cloudfiles.integration; import org.jclouds.blobstore.domain.Blob; import org.jclouds.blobstore.domain.BlobMetadata; import org.jclouds.blobstore.integration.internal.BaseContainerIntegrationTest; -import org.jclouds.rackspace.cloudfiles.CloudFilesBlobStore; +import org.jclouds.rackspace.cloudfiles.CloudFilesConnection; import org.jclouds.rackspace.cloudfiles.domain.ContainerMetadata; import org.testng.annotations.Test; @@ -37,6 +37,6 @@ import org.testng.annotations.Test; @Test(groups = { "integration", "live" }, testName = "cloudfiles.CloudFilesContainerIntegrationTest") public class CloudFilesContainerIntegrationTest extends - BaseContainerIntegrationTest> { + BaseContainerIntegrationTest> { } \ No newline at end of file diff --git a/rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/integration/CloudFilesContainerLiveTest.java b/rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/integration/CloudFilesContainerLiveTest.java index 4a7a761944..4b2f704a0e 100644 --- a/rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/integration/CloudFilesContainerLiveTest.java +++ b/rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/integration/CloudFilesContainerLiveTest.java @@ -26,7 +26,7 @@ package org.jclouds.rackspace.cloudfiles.integration; import org.jclouds.blobstore.domain.Blob; import org.jclouds.blobstore.domain.BlobMetadata; import org.jclouds.blobstore.integration.internal.BaseContainerLiveTest; -import org.jclouds.rackspace.cloudfiles.CloudFilesBlobStore; +import org.jclouds.rackspace.cloudfiles.CloudFilesConnection; import org.jclouds.rackspace.cloudfiles.domain.ContainerMetadata; import org.testng.annotations.Test; @@ -37,6 +37,6 @@ import org.testng.annotations.Test; @Test(groups = { "live" }, testName = "cloudfiles.CloudFilesContainerLiveTest") public class CloudFilesContainerLiveTest extends - BaseContainerLiveTest> { + BaseContainerLiveTest> { } \ No newline at end of file diff --git a/rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/integration/CloudFilesInputStreamMapIntegrationTest.java b/rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/integration/CloudFilesInputStreamMapIntegrationTest.java index dd3a911530..e8a1e2a39b 100644 --- a/rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/integration/CloudFilesInputStreamMapIntegrationTest.java +++ b/rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/integration/CloudFilesInputStreamMapIntegrationTest.java @@ -26,7 +26,7 @@ package org.jclouds.rackspace.cloudfiles.integration; import org.jclouds.blobstore.domain.Blob; import org.jclouds.blobstore.domain.BlobMetadata; import org.jclouds.blobstore.integration.internal.BaseInputStreamMapIntegrationTest; -import org.jclouds.rackspace.cloudfiles.CloudFilesBlobStore; +import org.jclouds.rackspace.cloudfiles.CloudFilesConnection; import org.jclouds.rackspace.cloudfiles.domain.ContainerMetadata; import org.testng.annotations.Test; @@ -36,6 +36,6 @@ import org.testng.annotations.Test; @Test(groups = { "integration", "live" }, testName = "cloudfiles.CloudFilesInputStreamMapIntegrationTest") public class CloudFilesInputStreamMapIntegrationTest extends - BaseInputStreamMapIntegrationTest> { + BaseInputStreamMapIntegrationTest> { } \ No newline at end of file diff --git a/rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/integration/CloudFilesServiceIntegrationTest.java b/rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/integration/CloudFilesServiceIntegrationTest.java index 78d3b40a75..a93ba5ee4e 100644 --- a/rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/integration/CloudFilesServiceIntegrationTest.java +++ b/rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/integration/CloudFilesServiceIntegrationTest.java @@ -26,16 +26,16 @@ package org.jclouds.rackspace.cloudfiles.integration; import org.jclouds.blobstore.domain.Blob; import org.jclouds.blobstore.domain.BlobMetadata; import org.jclouds.blobstore.integration.internal.BaseServiceIntegrationTest; -import org.jclouds.rackspace.cloudfiles.CloudFilesBlobStore; +import org.jclouds.rackspace.cloudfiles.CloudFilesConnection; import org.jclouds.rackspace.cloudfiles.domain.ContainerMetadata; import org.testng.annotations.Test; /** * @author Adrian Cole */ -@Test(groups = { "integration", "live" }, testName = "cloudfiles.CloudFilesServiceIntegrationTest") -public class CloudFilesServiceIntegrationTest extends - BaseServiceIntegrationTest> { - +@Test(groups = { "integration", "live" }, testName = "cloudfiles.CloudFilesServiceIntegrationTest") +public class CloudFilesServiceIntegrationTest + extends + BaseServiceIntegrationTest> { } \ No newline at end of file diff --git a/rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/integration/CloudFilesTestInitializer.java b/rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/integration/CloudFilesTestInitializer.java index 6887076c0b..196085c951 100644 --- a/rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/integration/CloudFilesTestInitializer.java +++ b/rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/integration/CloudFilesTestInitializer.java @@ -29,7 +29,7 @@ import org.jclouds.blobstore.domain.BlobMetadata; import org.jclouds.blobstore.integration.internal.BaseTestInitializer; import org.jclouds.logging.log4j.config.Log4JLoggingModule; import org.jclouds.rackspace.StubRackspaceAuthenticationModule; -import org.jclouds.rackspace.cloudfiles.CloudFilesBlobStore; +import org.jclouds.rackspace.cloudfiles.CloudFilesConnection; import org.jclouds.rackspace.cloudfiles.CloudFilesContextBuilder; import org.jclouds.rackspace.cloudfiles.domain.ContainerMetadata; @@ -41,15 +41,15 @@ import com.google.inject.Module; */ public class CloudFilesTestInitializer extends - BaseTestInitializer> { + BaseTestInitializer> { - protected BlobStoreContext> createStubContext() { + protected BlobStoreContext> createStubContext() { return new CloudFilesContextBuilder("foo", "bar").withModules( - new StubCloudFilesConnectionModule(), new StubRackspaceAuthenticationModule()) + new StubCloudFilesBlobStoreModule(), new StubRackspaceAuthenticationModule()) .buildContext(); } - protected BlobStoreContext> createLiveContext( + protected BlobStoreContext> createLiveContext( Module configurationModule, String url, String app, String account, String key) { return new CloudFilesContextBuilder(account, key).relaxSSLHostname().withModules( configurationModule, new Log4JLoggingModule()).buildContext(); diff --git a/rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/integration/StubCloudFilesConnectionModule.java b/rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/integration/StubCloudFilesBlobStoreModule.java similarity index 72% rename from rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/integration/StubCloudFilesConnectionModule.java rename to rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/integration/StubCloudFilesBlobStoreModule.java index f992ca0777..d079c577cd 100644 --- a/rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/integration/StubCloudFilesConnectionModule.java +++ b/rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/integration/StubCloudFilesBlobStoreModule.java @@ -26,17 +26,20 @@ package org.jclouds.rackspace.cloudfiles.integration; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import org.jclouds.blobstore.BlobStore; import org.jclouds.blobstore.domain.Blob; import org.jclouds.blobstore.domain.BlobMetadata; +import org.jclouds.blobstore.integration.internal.StubBlobStore; import org.jclouds.cloud.ConfiguresCloudConnection; -import org.jclouds.rackspace.cloudfiles.CloudFilesBlobStore; -import org.jclouds.rackspace.cloudfiles.internal.StubCloudFilesBlobStore; +import org.jclouds.rackspace.cloudfiles.CloudFilesConnection; +import org.jclouds.rackspace.cloudfiles.domain.ContainerMetadata; +import org.jclouds.rackspace.cloudfiles.internal.StubCloudFilesConnection; import com.google.inject.AbstractModule; import com.google.inject.TypeLiteral; @ConfiguresCloudConnection -public class StubCloudFilesConnectionModule extends AbstractModule { +public class StubCloudFilesBlobStoreModule extends AbstractModule { // must be singleton for all threads and all objects or tests may fail; static final ConcurrentHashMap>> map = new ConcurrentHashMap>>(); @@ -44,8 +47,11 @@ public class StubCloudFilesConnectionModule extends AbstractModule { protected void configure() { bind(new TypeLiteral>>>() { }).toInstance(map); - bind(new TypeLiteral() { - }).to(new TypeLiteral() { + bind(new TypeLiteral>>() { + }).to(new TypeLiteral>>() { + }).asEagerSingleton(); + bind(new TypeLiteral() { + }).to(new TypeLiteral() { }).asEagerSingleton(); } diff --git a/rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/internal/StubCloudFilesBlobStore.java b/rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/internal/StubCloudFilesConnection.java similarity index 60% rename from rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/internal/StubCloudFilesBlobStore.java rename to rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/internal/StubCloudFilesConnection.java index b72a725a5c..fc198d5b9d 100644 --- a/rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/internal/StubCloudFilesBlobStore.java +++ b/rackspace/cloudfiles/core/src/test/java/org/jclouds/rackspace/cloudfiles/internal/StubCloudFilesConnection.java @@ -25,6 +25,7 @@ package org.jclouds.rackspace.cloudfiles.internal; import java.util.Map; import java.util.SortedSet; +import java.util.concurrent.Future; import javax.inject.Inject; import javax.inject.Provider; @@ -32,7 +33,9 @@ import javax.inject.Provider; import org.jclouds.blobstore.domain.Blob; import org.jclouds.blobstore.domain.BlobMetadata; import org.jclouds.blobstore.integration.internal.StubBlobStore; +import org.jclouds.http.options.GetOptions; import org.jclouds.rackspace.cloudfiles.CloudFilesBlobStore; +import org.jclouds.rackspace.cloudfiles.CloudFilesConnection; import org.jclouds.rackspace.cloudfiles.domain.AccountMetadata; import org.jclouds.rackspace.cloudfiles.domain.ContainerCDNMetadata; import org.jclouds.rackspace.cloudfiles.domain.ContainerMetadata; @@ -48,52 +51,83 @@ import com.google.common.collect.Multimap; * @author Adrian Cole * @author James Murty */ -public class StubCloudFilesBlobStore extends +public class StubCloudFilesConnection extends StubBlobStore> implements - CloudFilesBlobStore { + CloudFilesConnection { @Inject - protected StubCloudFilesBlobStore(Map>> containerToBlobs, - DateService dateService, Provider containerMetaProvider, + protected StubCloudFilesConnection( + Map>> containerToBlobs, DateService dateService, + Provider containerMetaProvider, Provider> blobProvider) { super(containerToBlobs, dateService, containerMetaProvider, blobProvider); } - public AccountMetadata getAccountMetadata() { - return null; + public AccountMetadata getAccountStatistics() { + throw new UnsupportedOperationException(); } public SortedSet listContainers(ListContainerOptions options) { - return null; + throw new UnsupportedOperationException(); } public boolean setObjectMetadata(String container, String key, Multimap userMetadata) { - return false; + throw new UnsupportedOperationException(); } public boolean disableCDN(String container) { - return false; + throw new UnsupportedOperationException(); } public String enableCDN(String container, Long ttl) { - return null; + throw new UnsupportedOperationException(); } public String enableCDN(String container) { - return null; + throw new UnsupportedOperationException(); } public ContainerCDNMetadata getCDNMetadata(String container) { - return null; + throw new UnsupportedOperationException(); } public SortedSet listCDNContainers(ListCdnContainerOptions... options) { - return null; + throw new UnsupportedOperationException(); } public String updateCDN(String container, Long ttl) { - return null; + throw new UnsupportedOperationException(); + } + + public Future deleteContainerIfEmpty(String container) { + return super.deleteContainerImpl(container); + } + + public Future> getObject(String container, String key, GetOptions... options) { + throw new UnsupportedOperationException(); + + } + + public SortedSet listContainers(ListContainerOptions... options) { + throw new UnsupportedOperationException(); + + } + + public BlobMetadata getObjectMetadata(String container, String key) { + throw new UnsupportedOperationException(); + } + + public Future> listObjects(String container) { + throw new UnsupportedOperationException(); + } + + public Future putObject(String container, Blob object) { + throw new UnsupportedOperationException(); + } + + public Future removeObject(String container, String key) { + throw new UnsupportedOperationException(); } }