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
This commit is contained in:
adrian.f.cole 2009-10-05 16:27:48 +00:00
parent ce6930226e
commit 557d28783f
156 changed files with 5698 additions and 2254 deletions

View File

@ -34,30 +34,23 @@ import javax.ws.rs.PUT;
import javax.ws.rs.Path; import javax.ws.rs.Path;
import javax.ws.rs.PathParam; import javax.ws.rs.PathParam;
import org.jclouds.aws.s3.binders.AccessControlListBinder;
import org.jclouds.aws.s3.binders.S3ObjectBinder; 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.BucketMetadata;
import org.jclouds.aws.s3.domain.ListBucketResponse; import org.jclouds.aws.s3.domain.ListBucketResponse;
import org.jclouds.aws.s3.domain.ObjectMetadata; import org.jclouds.aws.s3.domain.ObjectMetadata;
import org.jclouds.aws.s3.domain.S3Object; import org.jclouds.aws.s3.domain.S3Object;
import org.jclouds.aws.s3.filters.RequestAuthorizeSignature; 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.ParseObjectFromHeadersAndHttpContent;
import org.jclouds.aws.s3.functions.ParseObjectMetadataFromHeaders; import org.jclouds.aws.s3.functions.ParseObjectMetadataFromHeaders;
import org.jclouds.aws.s3.functions.ReturnTrueIfBucketAlreadyOwnedByYou; 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.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.ListAllMyBucketsHandler;
import org.jclouds.aws.s3.xml.ListBucketHandler; import org.jclouds.aws.s3.xml.ListBucketHandler;
import org.jclouds.blobstore.BlobStore; import org.jclouds.blobstore.BlobStore;
import org.jclouds.blobstore.domain.Blob; import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.functions.BlobKey; 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.blobstore.functions.ThrowKeyNotFoundOn404;
import org.jclouds.http.functions.ParseETagHeader; import org.jclouds.http.functions.ParseETagHeader;
import org.jclouds.http.functions.ReturnFalseOn404; 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.Endpoint;
import org.jclouds.rest.EntityParam; import org.jclouds.rest.EntityParam;
import org.jclouds.rest.ExceptionParser; import org.jclouds.rest.ExceptionParser;
import org.jclouds.rest.Headers;
import org.jclouds.rest.HostPrefixParam; import org.jclouds.rest.HostPrefixParam;
import org.jclouds.rest.ParamParser; import org.jclouds.rest.ParamParser;
import org.jclouds.rest.QueryParams; import org.jclouds.rest.QueryParams;
@ -74,9 +66,6 @@ import org.jclouds.rest.ResponseParser;
import org.jclouds.rest.SkipEncoding; import org.jclouds.rest.SkipEncoding;
import org.jclouds.rest.VirtualHost; import org.jclouds.rest.VirtualHost;
import org.jclouds.rest.XMLResponseParser; 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. * Provides access to S3 via their REST API.
@ -184,38 +173,8 @@ public interface S3BlobStore extends BlobStore<BucketMetadata, ObjectMetadata, S
*/ */
@DELETE @DELETE
@Path("{key}") @Path("{key}")
Future<Boolean> removeBlob(@HostPrefixParam String bucketName, @PathParam("key") String key); @ExceptionParser(ReturnVoidOnNotFoundOr404.class)
Future<Void> removeBlob(@HostPrefixParam String bucketName, @PathParam("key") String key);
/**
* Store data by creating or overwriting an object.
* <p/>
* This method will store the object with the default <code>private</code> acl.
*
* <p/>
* This returns a byte[] of the eTag hash of what Amazon S3 received
* <p />
*
* @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 <a
* href="http://docs.amazonwebservices.com/AmazonS3/2006-03-01/index.html?RESTObjectPUT.html"
* />
*/
@PUT
@Path("{key}")
@ResponseParser(ParseETagHeader.class)
Future<byte[]> putBlob(
@HostPrefixParam String bucketName,
@PathParam("key") @ParamParser(BlobKey.class) @EntityParam(S3ObjectBinder.class) S3Object object,
PutObjectOptions options);
@PUT @PUT
@Path("{key}") @Path("{key}")
@ -224,33 +183,6 @@ public interface S3BlobStore extends BlobStore<BucketMetadata, ObjectMetadata, S
@HostPrefixParam String bucketName, @HostPrefixParam String bucketName,
@PathParam("key") @ParamParser(BlobKey.class) @EntityParam(S3ObjectBinder.class) S3Object object); @PathParam("key") @ParamParser(BlobKey.class) @EntityParam(S3ObjectBinder.class) S3Object object);
/**
* Create and name your own bucket in which to store your objects.
*
* <p/>
* you can use {@link PutBucketOptions} to create the bucket in EU.
* <p/>
* 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 <a
* href="http://docs.amazonwebservices.com/AmazonS3/2006-03-01/index.html?RESTBucketPUT.html"
* />
*
*/
@PUT
@Path("/")
@ExceptionParser(ReturnTrueIfBucketAlreadyOwnedByYou.class)
Future<Boolean> createContainer(@HostPrefixParam String bucketName,
@Nullable @EntityParam(HttpRequestOptionsBinder.class) PutBucketOptions options);
@PUT @PUT
@Path("/") @Path("/")
@ExceptionParser(ReturnTrueIfBucketAlreadyOwnedByYou.class) @ExceptionParser(ReturnTrueIfBucketAlreadyOwnedByYou.class)
@ -267,7 +199,6 @@ public interface S3BlobStore extends BlobStore<BucketMetadata, ObjectMetadata, S
* *
* @param bucketName * @param bucketName
* what to delete * what to delete
* @return false, if the bucket was not empty and therefore not deleted
* @see org.jclouds.aws.s3.commands.DeleteBucket * @see org.jclouds.aws.s3.commands.DeleteBucket
* @see <a href= * @see <a href=
* "http://docs.amazonwebservices.com/AmazonS3/2006-03-01/index.html?RESTBucketDELETE.html" * "http://docs.amazonwebservices.com/AmazonS3/2006-03-01/index.html?RESTBucketDELETE.html"
@ -275,8 +206,8 @@ public interface S3BlobStore extends BlobStore<BucketMetadata, ObjectMetadata, S
*/ */
@DELETE @DELETE
@Path("/") @Path("/")
@ExceptionParser(ReturnTrueOn404FalseIfNotEmpty.class) @ExceptionParser(ClearAndDeleteBucketIfNotEmpty.class)
Future<Boolean> deleteContainer(@HostPrefixParam String bucketName); Future<Void> deleteContainer(@HostPrefixParam String bucketName);
/** /**
* Issues a HEAD command to determine if the bucket exists or not. * Issues a HEAD command to determine if the bucket exists or not.
@ -308,12 +239,6 @@ public interface S3BlobStore extends BlobStore<BucketMetadata, ObjectMetadata, S
@GET @GET
@Path("/") @Path("/")
@XMLResponseParser(ListBucketHandler.class) @XMLResponseParser(ListBucketHandler.class)
Future<ListBucketResponse> listBlobs(@HostPrefixParam String bucketName,
@Nullable ListBucketOptions options);
@GET
@Path("/")
@XMLResponseParser(ListBucketHandler.class)
Future<ListBucketResponse> listBlobs(@HostPrefixParam String bucketName); Future<ListBucketResponse> listBlobs(@HostPrefixParam String bucketName);
/** /**
@ -330,129 +255,4 @@ public interface S3BlobStore extends BlobStore<BucketMetadata, ObjectMetadata, S
@Path("/") @Path("/")
SortedSet<BucketMetadata> listContainers(); SortedSet<BucketMetadata> 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.
* <p/>
* 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 <a
* href="http://docs.amazonwebservices.com/AmazonS3/2006-03-01/index.html?RESTObjectCOPY.html"
* />
* @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<ObjectMetadata> 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<ObjectMetadata> 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.
* <p />
* To list a bucket's ACL, you must have READ_ACP access to the item.
*
* @return access permissions of the bucket
*
* @see <a href="http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAccessPolicy.html"/>
*/
@GET
@QueryParams(keys = "acl")
@XMLResponseParser(AccessControlListHandler.class)
@ExceptionParser(ThrowContainerNotFoundOn404.class)
@Path("/")
Future<AccessControlList> getContainerACL(@HostPrefixParam String bucketName);
/**
* Update a bucket's Access Control List settings.
* <p/>
* A PUT request operation directed at a bucket URI with the "acl" parameter sets the Access
* Control List (ACL) settings for that S3 item.
* <p />
* 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 <strong>must</strong> include a
* valid owner identifier string in {@link AccessControlList#getOwner()}.
* @return true if the bucket's Access Control List was updated successfully.
*
* @see <a href="http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAccessPolicy.html"/>
*/
@PUT
@Path("/")
@QueryParams(keys = "acl")
Future<Boolean> 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.
* <p />
* To list a object's ACL, you must have READ_ACP access to the item.
*
* @return access permissions of the object
*
* @see <a href="http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAccessPolicy.html"/>
*/
@GET
@QueryParams(keys = "acl")
@Path("{key}")
@XMLResponseParser(AccessControlListHandler.class)
@ExceptionParser(ThrowKeyNotFoundOn404.class)
Future<AccessControlList> getBlobACL(@HostPrefixParam String bucketName,
@PathParam("key") String key);
/**
* Update an object's Access Control List settings.
* <p/>
* A PUT request operation directed at an object URI with the "acl" parameter sets the Access
* Control List (ACL) settings for that S3 item.
* <p />
* 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 <strong>must</strong> include a
* valid owner identifier string in {@link AccessControlList#getOwner()}.
* @return true if the object's Access Control List was updated successfully.
*
* @see <a href="http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAccessPolicy.html"/>
*/
@PUT
@QueryParams(keys = "acl")
@Path("{key}")
Future<Boolean> putBlobACL(@HostPrefixParam String bucketName, @PathParam("key") String key,
@EntityParam(AccessControlListBinder.class) AccessControlList acl);
} }

View File

@ -0,0 +1,426 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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.
* <p/>
* 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 <a href="http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAPI.html" />
*/
@VirtualHost
@SkipEncoding('/')
@RequestFilters(RequestAuthorizeSignature.class)
@Endpoint(S3.class)
public interface S3Connection {
/**
* Retrieves the S3Object associated with the Key or {@link Blob <ObjectMetadata>#NOT_FOUND} if
* not available;
*
* <p/>
* 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.
*
* <p />
* This command allows you to specify {@link GetObjectOptions} to control delivery of content.
*
* <h2>Note</h2> If you specify any of the below options, you will receive partial content:
* <ul>
* <li>{@link GetObjectOptions#range}</li>
* <li>{@link GetObjectOptions#startAt}</li>
* <li>{@link GetObjectOptions#tail}</li>
* </ul>
*
* @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<S3Object> 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.
*
* <p/>
* 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 <a
* href="http://docs.amazonwebservices.com/AmazonS3/2006-03-01/index.html?RESTObjectHEAD.html"
* />
*/
@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.
* <p/>
* 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 <a href="http://docs.amazonwebservices.com/AmazonS3/2006-03-01/index.html?
* RESTObjectDELETE.html" />
*/
@DELETE
@Path("{key}")
@ExceptionParser(ReturnVoidOnNotFoundOr404.class)
Future<Void> deleteObject(@HostPrefixParam String bucketName, @PathParam("key") String key);
/**
* Store data by creating or overwriting an object.
* <p/>
* This method will store the object with the default <code>private</code> acl.
*
* <p/>
* This returns a byte[] of the eTag hash of what Amazon S3 received
* <p />
*
* @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 <a
* href="http://docs.amazonwebservices.com/AmazonS3/2006-03-01/index.html?RESTObjectPUT.html"
* />
*/
@PUT
@Path("{key}")
@ResponseParser(ParseETagHeader.class)
Future<byte[]> 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.
*
* <p/>
* you can use {@link PutBucketOptions} to create the bucket in EU.
* <p/>
* 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 <a
* href="http://docs.amazonwebservices.com/AmazonS3/2006-03-01/index.html?RESTBucketPUT.html"
* />
*
*/
@PUT
@Path("/")
@ExceptionParser(ReturnTrueIfBucketAlreadyOwnedByYou.class)
Future<Boolean> putBucketIfNotExists(@HostPrefixParam String bucketName,
@Nullable @EntityParam(HttpRequestOptionsBinder.class) PutBucketOptions... options);
/**
* Deletes the bucket, if it is empty.
* <p/>
* 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.
* <p />
* 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 <a href=
* "http://docs.amazonwebservices.com/AmazonS3/2006-03-01/index.html?RESTBucketDELETE.html"
* />
*/
@DELETE
@Path("/")
@ExceptionParser(ReturnTrueOn404FalseIfNotEmpty.class)
Future<Boolean> 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 <code>S3Bucket</code> 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.
* <p />
* To list the keys of a bucket, you must have READ access to the bucket.
* <p/>
*
* @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<ObjectMetadata>#NOT_FOUND} if not present.
* @see ListBucketOptions
*
* @see <a
* href="http://docs.amazonwebservices.com/AmazonS3/2006-03-01/index.html?RESTBucketGET.html"
* />
*/
@GET
@Path("/")
@XMLResponseParser(ListBucketHandler.class)
Future<ListBucketResponse> 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 <a
* href="http://docs.amazonwebservices.com/AmazonS3/2006-03-01/index.html?RESTServiceGET.html"
* />
*
*/
@GET
@XMLResponseParser(ListAllMyBucketsHandler.class)
@Path("/")
SortedSet<BucketMetadata> 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.
* <p/>
* 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 <a
* href="http://docs.amazonwebservices.com/AmazonS3/2006-03-01/index.html?RESTObjectCOPY.html"
* />
* @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<ObjectMetadata> 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.
* <p />
* To list a bucket's ACL, you must have READ_ACP access to the item.
*
* @return access permissions of the bucket
*
* @see <a href="http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAccessPolicy.html"/>
*/
@GET
@QueryParams(keys = "acl")
@XMLResponseParser(AccessControlListHandler.class)
@ExceptionParser(ThrowContainerNotFoundOn404.class)
@Path("/")
Future<AccessControlList> getBucketACL(@HostPrefixParam String bucketName);
/**
* Update a bucket's Access Control List settings.
* <p/>
* A PUT request operation directed at a bucket URI with the "acl" parameter sets the Access
* Control List (ACL) settings for that S3 item.
* <p />
* 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 <strong>must</strong> include a
* valid owner identifier string in {@link AccessControlList#getOwner()}.
* @return true if the bucket's Access Control List was updated successfully.
*
* @see <a href="http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAccessPolicy.html"/>
*/
@PUT
@Path("/")
@QueryParams(keys = "acl")
Future<Boolean> 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.
* <p />
* To list a object's ACL, you must have READ_ACP access to the item.
*
* @return access permissions of the object
*
* @see <a href="http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAccessPolicy.html"/>
*/
@GET
@QueryParams(keys = "acl")
@Path("{key}")
@XMLResponseParser(AccessControlListHandler.class)
@ExceptionParser(ThrowKeyNotFoundOn404.class)
Future<AccessControlList> getObjectACL(@HostPrefixParam String bucketName,
@PathParam("key") String key);
/**
* Update an object's Access Control List settings.
* <p/>
* A PUT request operation directed at an object URI with the "acl" parameter sets the Access
* Control List (ACL) settings for that S3 item.
* <p />
* 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 <strong>must</strong> include a
* valid owner identifier string in {@link AccessControlList#getOwner()}.
* @return true if the object's Access Control List was updated successfully.
*
* @see <a href="http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAccessPolicy.html"/>
*/
@PUT
@QueryParams(keys = "acl")
@Path("{key}")
Future<Boolean> putObjectACL(@HostPrefixParam String bucketName, @PathParam("key") String key,
@EntityParam(AccessControlListBinder.class) AccessControlList acl);
}

View File

@ -41,6 +41,6 @@ import org.jclouds.blobstore.BlobStoreContext;
* *
*/ */
public interface S3Context extends public interface S3Context extends
BlobStoreContext<S3BlobStore, BucketMetadata, ObjectMetadata, S3Object> { BlobStoreContext<S3Connection, BucketMetadata, ObjectMetadata, S3Object> {
} }

View File

@ -61,7 +61,7 @@ import com.google.inject.TypeLiteral;
* @see S3Context * @see S3Context
*/ */
public class S3ContextBuilder extends public class S3ContextBuilder extends
BlobStoreContextBuilder<S3BlobStore, BucketMetadata, ObjectMetadata, S3Object> { BlobStoreContextBuilder<S3Connection, BucketMetadata, ObjectMetadata, S3Object> {
@Override @Override
public S3Context buildContext() { public S3Context buildContext() {
@ -69,7 +69,7 @@ public class S3ContextBuilder extends
} }
public S3ContextBuilder(Properties props) { public S3ContextBuilder(Properties props) {
super(new TypeLiteral<S3BlobStore>() { super(new TypeLiteral<S3Connection>() {
}, new TypeLiteral<BucketMetadata>() { }, new TypeLiteral<BucketMetadata>() {
}, new TypeLiteral<ObjectMetadata>() { }, new TypeLiteral<ObjectMetadata>() {
}, new TypeLiteral<S3Object>() { }, new TypeLiteral<S3Object>() {

View File

@ -45,7 +45,8 @@ public class S3ObjectBinder extends BlobBinder {
public void addEntityToRequest(Object entity, HttpRequest request) { public void addEntityToRequest(Object entity, HttpRequest request) {
Blob<?> object = (Blob<?>) entity; Blob<?> object = (Blob<?>) entity;
checkArgument(object.getMetadata().getSize() >= 0, "size must be set"); 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) { if (object instanceof S3Object) {
S3Object s3Object = (S3Object) object; S3Object s3Object = (S3Object) object;
if (s3Object.getMetadata().getCacheControl() != null) { if (s3Object.getMetadata().getCacheControl() != null) {

View File

@ -30,11 +30,16 @@ import javax.inject.Singleton;
import org.jclouds.aws.s3.S3; import org.jclouds.aws.s3.S3;
import org.jclouds.aws.s3.S3BlobStore; 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.filters.RequestAuthorizeSignature;
import org.jclouds.aws.s3.handlers.AWSClientErrorRetryHandler; import org.jclouds.aws.s3.handlers.AWSClientErrorRetryHandler;
import org.jclouds.aws.s3.handlers.AWSRedirectionRetryHandler; import org.jclouds.aws.s3.handlers.AWSRedirectionRetryHandler;
import org.jclouds.aws.s3.handlers.ParseAWSErrorFromXmlContent; import org.jclouds.aws.s3.handlers.ParseAWSErrorFromXmlContent;
import org.jclouds.aws.s3.reference.S3Constants; import org.jclouds.aws.s3.reference.S3Constants;
import org.jclouds.blobstore.BlobStore;
import org.jclouds.cloud.ConfiguresCloudConnection; import org.jclouds.cloud.ConfiguresCloudConnection;
import org.jclouds.http.HttpErrorHandler; import org.jclouds.http.HttpErrorHandler;
import org.jclouds.http.HttpRetryHandler; import org.jclouds.http.HttpRetryHandler;
@ -73,10 +78,16 @@ public class RestS3ConnectionModule extends AbstractModule {
@Provides @Provides
@Singleton @Singleton
protected S3BlobStore provideS3Connection(RestClientFactory factory) { protected BlobStore<BucketMetadata, ObjectMetadata, S3Object> provideS3BlobStore(RestClientFactory factory) {
return factory.create(S3BlobStore.class); return factory.create(S3BlobStore.class);
} }
@Provides
@Singleton
protected S3Connection provideS3Connection(RestClientFactory factory) {
return factory.create(S3Connection.class);
}
protected void bindErrorHandlers() { protected void bindErrorHandlers() {
bind(HttpErrorHandler.class).annotatedWith(Redirection.class).to( bind(HttpErrorHandler.class).annotatedWith(Redirection.class).to(
ParseAWSErrorFromXmlContent.class); ParseAWSErrorFromXmlContent.class);

View File

@ -32,10 +32,12 @@ import javax.inject.Provider;
import org.jclouds.aws.reference.AWSConstants; import org.jclouds.aws.reference.AWSConstants;
import org.jclouds.aws.s3.S3; import org.jclouds.aws.s3.S3;
import org.jclouds.aws.s3.S3BlobStore; import org.jclouds.aws.s3.S3BlobStore;
import org.jclouds.aws.s3.S3Connection;
import org.jclouds.aws.s3.S3Context; import org.jclouds.aws.s3.S3Context;
import org.jclouds.aws.s3.domain.BucketMetadata; import org.jclouds.aws.s3.domain.BucketMetadata;
import org.jclouds.aws.s3.domain.ObjectMetadata; import org.jclouds.aws.s3.domain.ObjectMetadata;
import org.jclouds.aws.s3.domain.S3Object; import org.jclouds.aws.s3.domain.S3Object;
import org.jclouds.blobstore.BlobStore;
import org.jclouds.blobstore.BlobStoreContextImpl; import org.jclouds.blobstore.BlobStoreContextImpl;
import org.jclouds.blobstore.BlobMap.Factory; import org.jclouds.blobstore.BlobMap.Factory;
import org.jclouds.lifecycle.Closer; import org.jclouds.lifecycle.Closer;
@ -56,15 +58,17 @@ public class S3ContextModule extends AbstractModule {
} }
public static class S3ContextImpl extends public static class S3ContextImpl extends
BlobStoreContextImpl<S3BlobStore, BucketMetadata, ObjectMetadata, S3Object> implements BlobStoreContextImpl<S3Connection, BucketMetadata, ObjectMetadata, S3Object> implements
S3Context { S3Context {
@Inject @Inject
S3ContextImpl(Factory<ObjectMetadata, S3Object> blobMapFactory, S3ContextImpl(Factory<ObjectMetadata, S3Object> blobMapFactory,
org.jclouds.blobstore.InputStreamMap.Factory<ObjectMetadata> inputStreamMapFactory, org.jclouds.blobstore.InputStreamMap.Factory<ObjectMetadata> inputStreamMapFactory,
Closer closer, Provider<S3Object> blobProvider, S3BlobStore defaultApi, Closer closer, Provider<S3Object> blobProvider,
@S3 URI endPoint, @Named(AWSConstants.PROPERTY_AWS_ACCESSKEYID) String account) { BlobStore<BucketMetadata, ObjectMetadata, S3Object> blobStore,
super(blobMapFactory, inputStreamMapFactory, closer, blobProvider, defaultApi, endPoint, S3Connection defaultApi, @S3 URI endPoint,
account); @Named(AWSConstants.PROPERTY_AWS_ACCESSKEYID) String account) {
super(blobMapFactory, inputStreamMapFactory, closer, blobProvider, blobStore, defaultApi,
endPoint, account);
} }
} }

View File

@ -0,0 +1,45 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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<BucketMetadata, ObjectMetadata, S3Object> {
@Inject
ClearAndDeleteBucketIfNotEmpty(
ClearContainerStrategy<BucketMetadata, ObjectMetadata, S3Object> clear,
BlobStore<BucketMetadata, ObjectMetadata, S3Object> connection) {
super(clear, connection);
}
}

View File

@ -95,6 +95,7 @@ public class ListBucketHandler extends ParseSax.HandlerWithResult<ListBucketResp
} else if (qName.equals("ETag")) { } else if (qName.equals("ETag")) {
currentObjectMetadata.setETag(HttpUtils.fromHexString(currentText.toString().replaceAll( currentObjectMetadata.setETag(HttpUtils.fromHexString(currentText.toString().replaceAll(
"\"", ""))); "\"", "")));
currentObjectMetadata.setContentMD5(currentObjectMetadata.getETag());
} else if (qName.equals("Size")) { } else if (qName.equals("Size")) {
currentObjectMetadata.setSize(Long.parseLong(currentText.toString())); currentObjectMetadata.setSize(Long.parseLong(currentText.toString()));
} else if (qName.equals("Owner")) { } else if (qName.equals("Owner")) {

View File

@ -0,0 +1,705 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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<S3Connection, BucketMetadata, ObjectMetadata, S3Object> {
/**
* 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.<RuntimeException> 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.<RuntimeException> 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<String, String> 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.<RuntimeException> 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<BucketMetadata> 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);
}
}
}

View File

@ -36,6 +36,8 @@ import org.jclouds.aws.s3.config.RestS3ConnectionModule;
import org.jclouds.aws.s3.config.S3ContextModule; import org.jclouds.aws.s3.config.S3ContextModule;
import org.jclouds.aws.s3.config.StubS3BlobStoreModule; import org.jclouds.aws.s3.config.StubS3BlobStoreModule;
import org.jclouds.aws.s3.config.S3ContextModule.S3ContextImpl; 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 org.testng.annotations.Test;
import com.google.inject.Injector; import com.google.inject.Injector;
@ -61,6 +63,8 @@ public class S3ContextBuilderTest {
S3Context context = new S3ContextBuilder("id", "secret").withModules( S3Context context = new S3ContextBuilder("id", "secret").withModules(
new StubS3BlobStoreModule()).buildContext(); new StubS3BlobStoreModule()).buildContext();
assertEquals(context.getClass(), S3ContextImpl.class); assertEquals(context.getClass(), S3ContextImpl.class);
assertEquals(context.getApi().getClass(), StubS3Connection.class);
assertEquals(context.getBlobStore().getClass(), StubBlobStore.class);
assertEquals(context.getAccount(), "id"); assertEquals(context.getAccount(), "id");
assertEquals(context.getEndPoint(), URI.create("https://localhost/s3stub")); assertEquals(context.getEndPoint(), URI.create("https://localhost/s3stub"));
} }

View File

@ -25,7 +25,6 @@ package org.jclouds.aws.s3.config;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
import org.jclouds.aws.s3.S3BlobStore;
import org.jclouds.aws.s3.S3Context; import org.jclouds.aws.s3.S3Context;
import org.jclouds.aws.s3.config.S3ContextModule.S3ContextImpl; import org.jclouds.aws.s3.config.S3ContextModule.S3ContextImpl;
import org.jclouds.aws.s3.domain.BucketMetadata; import org.jclouds.aws.s3.domain.BucketMetadata;
@ -48,8 +47,7 @@ public class S3ContextModuleTest {
Injector createInjector() { Injector createInjector() {
return Guice.createInjector(new StubS3BlobStoreModule(), BlobStoreMapsModule.Builder return Guice.createInjector(new StubS3BlobStoreModule(), BlobStoreMapsModule.Builder
.newBuilder(new TypeLiteral<S3BlobStore>() { .newBuilder(new TypeLiteral<BucketMetadata>() {
}, new TypeLiteral<BucketMetadata>() {
}, new TypeLiteral<ObjectMetadata>() { }, new TypeLiteral<ObjectMetadata>() {
}, new TypeLiteral<S3Object>() { }, new TypeLiteral<S3Object>() {
}).build(), new S3ContextModule() { }).build(), new S3ContextModule() {

View File

@ -28,9 +28,13 @@ import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import org.jclouds.aws.s3.S3; 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.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.cloud.ConfiguresCloudConnection;
import org.jclouds.http.functions.config.ParserModule; import org.jclouds.http.functions.config.ParserModule;
@ -50,7 +54,10 @@ public class StubS3BlobStoreModule extends AbstractModule {
install(new ParserModule()); install(new ParserModule());
bind(new TypeLiteral<Map<String, Map<String, S3Object>>>() { bind(new TypeLiteral<Map<String, Map<String, S3Object>>>() {
}).toInstance(map); }).toInstance(map);
bind(S3BlobStore.class).to(StubS3BlobStore.class).asEagerSingleton(); bind(new TypeLiteral<BlobStore<BucketMetadata, ObjectMetadata, S3Object>>() {
}).to(new TypeLiteral<StubBlobStore<BucketMetadata, ObjectMetadata, S3Object>>() {
}).asEagerSingleton();
bind(S3Connection.class).to(StubS3Connection.class).asEagerSingleton();
bind(URI.class).annotatedWith(S3.class).toInstance(URI.create("https://localhost/s3stub")); bind(URI.class).annotatedWith(S3.class).toInstance(URI.create("https://localhost/s3stub"));
} }
} }

View File

@ -23,42 +23,13 @@
*/ */
package org.jclouds.aws.s3.integration; package org.jclouds.aws.s3.integration;
import static org.jclouds.aws.s3.internal.StubS3BlobStore.TEST_ACL_EMAIL; import org.jclouds.aws.s3.S3Connection;
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.domain.BucketMetadata; 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.ObjectMetadata;
import org.jclouds.aws.s3.domain.S3Object; 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.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 org.testng.annotations.Test;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
/** /**
* *
* @author James Murty * @author James Murty
@ -66,361 +37,6 @@ import com.google.common.collect.Multimap;
*/ */
@Test(groups = { "integration", "live" }, testName = "s3.S3BlobIntegrationTest") @Test(groups = { "integration", "live" }, testName = "s3.S3BlobIntegrationTest")
public class S3BlobIntegrationTest extends public class S3BlobIntegrationTest extends
BaseBlobIntegrationTest<S3BlobStore, BucketMetadata, ObjectMetadata, S3Object> { BaseBlobIntegrationTest<S3Connection, BucketMetadata, ObjectMetadata, S3Object> {
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.<RuntimeException> 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.<RuntimeException> 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<String, String> 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);
}
}
} }

View File

@ -23,19 +23,11 @@
*/ */
package org.jclouds.aws.s3.integration; package org.jclouds.aws.s3.integration;
import static org.jclouds.aws.s3.options.CopyObjectOptions.Builder.overrideAcl; import org.jclouds.aws.s3.S3Connection;
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.domain.BucketMetadata; 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.ObjectMetadata;
import org.jclouds.aws.s3.domain.S3Object; import org.jclouds.aws.s3.domain.S3Object;
import org.jclouds.blobstore.integration.internal.BaseBlobLiveTest; import org.jclouds.blobstore.integration.internal.BaseBlobLiveTest;
import org.jclouds.util.Utils;
import org.testng.annotations.Test; import org.testng.annotations.Test;
/** /**
@ -45,48 +37,6 @@ import org.testng.annotations.Test;
*/ */
@Test(groups = { "live" }, testName = "s3.S3BlobLiveTest") @Test(groups = { "live" }, testName = "s3.S3BlobLiveTest")
public class S3BlobLiveTest extends public class S3BlobLiveTest extends
BaseBlobLiveTest<S3BlobStore, BucketMetadata, ObjectMetadata, S3Object> { BaseBlobLiveTest<S3Connection, BucketMetadata, ObjectMetadata, S3Object> {
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);
}
}
} }

View File

@ -23,7 +23,7 @@
*/ */
package org.jclouds.aws.s3.integration; 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.BucketMetadata;
import org.jclouds.aws.s3.domain.ObjectMetadata; import org.jclouds.aws.s3.domain.ObjectMetadata;
import org.jclouds.aws.s3.domain.S3Object; import org.jclouds.aws.s3.domain.S3Object;
@ -35,6 +35,6 @@ import org.testng.annotations.Test;
*/ */
@Test(groups = { "integration", "live" }, testName = "s3.S3BlobMapIntegrationTest") @Test(groups = { "integration", "live" }, testName = "s3.S3BlobMapIntegrationTest")
public class S3BlobMapIntegrationTest extends public class S3BlobMapIntegrationTest extends
BaseBlobMapIntegrationTest<S3BlobStore, BucketMetadata, ObjectMetadata, S3Object> { BaseBlobMapIntegrationTest<S3Connection, BucketMetadata, ObjectMetadata, S3Object> {
} }

View File

@ -23,31 +23,10 @@
*/ */
package org.jclouds.aws.s3.integration; package org.jclouds.aws.s3.integration;
import static org.jclouds.aws.s3.internal.StubS3BlobStore.TEST_ACL_EMAIL; import org.jclouds.aws.s3.S3Connection;
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.domain.BucketMetadata; 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.ObjectMetadata;
import org.jclouds.aws.s3.domain.S3Object; 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.jclouds.blobstore.integration.internal.BaseContainerIntegrationTest;
import org.testng.annotations.Test; import org.testng.annotations.Test;
@ -57,133 +36,6 @@ import org.testng.annotations.Test;
*/ */
@Test(groups = { "integration", "live" }, testName = "s3.S3ContainerIntegrationTest") @Test(groups = { "integration", "live" }, testName = "s3.S3ContainerIntegrationTest")
public class S3ContainerIntegrationTest extends public class S3ContainerIntegrationTest extends
BaseContainerIntegrationTest<S3BlobStore, BucketMetadata, ObjectMetadata, S3Object> { BaseContainerIntegrationTest<S3Connection, BucketMetadata, ObjectMetadata, S3Object> {
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);
}
}
} }

View File

@ -23,25 +23,11 @@
*/ */
package org.jclouds.aws.s3.integration; package org.jclouds.aws.s3.integration;
import static org.jclouds.aws.s3.options.PutBucketOptions.Builder.createIn; import org.jclouds.aws.s3.S3Connection;
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.domain.BucketMetadata; 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.ObjectMetadata;
import org.jclouds.aws.s3.domain.S3Object; 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.blobstore.integration.internal.BaseContainerLiveTest;
import org.jclouds.util.Utils;
import org.testng.annotations.Test; import org.testng.annotations.Test;
/** /**
@ -50,63 +36,6 @@ import org.testng.annotations.Test;
*/ */
@Test(groups = { "live" }, testName = "s3.S3ContainerLiveTest") @Test(groups = { "live" }, testName = "s3.S3ContainerLiveTest")
public class S3ContainerLiveTest extends public class S3ContainerLiveTest extends
BaseContainerLiveTest<S3BlobStore, BucketMetadata, ObjectMetadata, S3Object> { BaseContainerLiveTest<S3Connection, BucketMetadata, ObjectMetadata, S3Object> {
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.<RuntimeException> 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");
}
}
} }

View File

@ -23,7 +23,7 @@
*/ */
package org.jclouds.aws.s3.integration; 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.BucketMetadata;
import org.jclouds.aws.s3.domain.ObjectMetadata; import org.jclouds.aws.s3.domain.ObjectMetadata;
import org.jclouds.aws.s3.domain.S3Object; import org.jclouds.aws.s3.domain.S3Object;
@ -35,6 +35,6 @@ import org.testng.annotations.Test;
*/ */
@Test(groups = { "integration", "live" }, testName = "s3.S3InputStreamMapIntegrationTest") @Test(groups = { "integration", "live" }, testName = "s3.S3InputStreamMapIntegrationTest")
public class S3InputStreamMapIntegrationTest extends public class S3InputStreamMapIntegrationTest extends
BaseInputStreamMapIntegrationTest<S3BlobStore, BucketMetadata, ObjectMetadata, S3Object> { BaseInputStreamMapIntegrationTest<S3Connection, BucketMetadata, ObjectMetadata, S3Object> {
} }

View File

@ -23,9 +23,7 @@
*/ */
package org.jclouds.aws.s3.integration; package org.jclouds.aws.s3.integration;
import java.util.SortedSet; import org.jclouds.aws.s3.S3Connection;
import org.jclouds.aws.s3.S3BlobStore;
import org.jclouds.aws.s3.domain.BucketMetadata; import org.jclouds.aws.s3.domain.BucketMetadata;
import org.jclouds.aws.s3.domain.ObjectMetadata; import org.jclouds.aws.s3.domain.ObjectMetadata;
import org.jclouds.aws.s3.domain.S3Object; import org.jclouds.aws.s3.domain.S3Object;
@ -37,18 +35,6 @@ import org.testng.annotations.Test;
*/ */
@Test(groups = { "integration", "live" }, testName = "s3.S3ServiceIntegrationTest") @Test(groups = { "integration", "live" }, testName = "s3.S3ServiceIntegrationTest")
public class S3ServiceIntegrationTest extends public class S3ServiceIntegrationTest extends
BaseServiceIntegrationTest<S3BlobStore, BucketMetadata, ObjectMetadata, S3Object> { BaseServiceIntegrationTest<S3Connection, BucketMetadata, ObjectMetadata, S3Object> {
void containerExists() throws Exception {
String containerName = getContainerName();
try {
SortedSet<BucketMetadata> list = context.getApi().listContainers();
BucketMetadata firstContainer = list.first();
BucketMetadata toMatch = new BucketMetadata(containerName);
toMatch.setOwner(firstContainer.getOwner());
assert list.contains(toMatch);
} finally {
returnContainer(containerName);
}
}
} }

View File

@ -23,7 +23,7 @@
*/ */
package org.jclouds.aws.s3.integration; 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.S3ContextBuilder;
import org.jclouds.aws.s3.S3ContextFactory; import org.jclouds.aws.s3.S3ContextFactory;
import org.jclouds.aws.s3.config.StubS3BlobStoreModule; import org.jclouds.aws.s3.config.StubS3BlobStoreModule;
@ -42,10 +42,10 @@ import com.google.inject.Module;
* @author Adrian Cole * @author Adrian Cole
*/ */
public class S3TestInitializer extends public class S3TestInitializer extends
BaseTestInitializer<S3BlobStore, BucketMetadata, ObjectMetadata, S3Object> { BaseTestInitializer<S3Connection, BucketMetadata, ObjectMetadata, S3Object> {
@Override @Override
protected BlobStoreContext<S3BlobStore, BucketMetadata, ObjectMetadata, S3Object> createLiveContext( protected BlobStoreContext<S3Connection, BucketMetadata, ObjectMetadata, S3Object> createLiveContext(
Module configurationModule, String url, String app, String account, String key) { Module configurationModule, String url, String app, String account, String key) {
BaseBlobStoreIntegrationTest.SANITY_CHECK_RETURNED_BUCKET_NAME = true; BaseBlobStoreIntegrationTest.SANITY_CHECK_RETURNED_BUCKET_NAME = true;
return new S3ContextBuilder(account, key).withSaxDebug().relaxSSLHostname().withModules( return new S3ContextBuilder(account, key).withSaxDebug().relaxSSLHostname().withModules(
@ -53,7 +53,7 @@ public class S3TestInitializer extends
} }
@Override @Override
protected BlobStoreContext<S3BlobStore, BucketMetadata, ObjectMetadata, S3Object> createStubContext() { protected BlobStoreContext<S3Connection, BucketMetadata, ObjectMetadata, S3Object> createStubContext() {
return S3ContextFactory.createContext("user", "pass", new StubS3BlobStoreModule()); return S3ContextFactory.createContext("user", "pass", new StubS3BlobStoreModule());
} }

View File

@ -35,6 +35,7 @@ import javax.inject.Inject;
import javax.inject.Provider; import javax.inject.Provider;
import org.jclouds.aws.s3.S3BlobStore; 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.AccessControlList;
import org.jclouds.aws.s3.domain.BucketMetadata; import org.jclouds.aws.s3.domain.BucketMetadata;
import org.jclouds.aws.s3.domain.CannedAccessPolicy; import org.jclouds.aws.s3.domain.CannedAccessPolicy;
@ -70,11 +71,11 @@ import com.google.inject.internal.Nullable;
* @author Adrian Cole * @author Adrian Cole
* @author James Murty * @author James Murty
*/ */
public class StubS3BlobStore extends StubBlobStore<BucketMetadata, ObjectMetadata, S3Object> public class StubS3Connection extends StubBlobStore<BucketMetadata, ObjectMetadata, S3Object>
implements S3BlobStore { implements S3Connection {
@Inject @Inject
protected StubS3BlobStore(Map<String, Map<String, S3Object>> containerToBlobs, protected StubS3Connection(Map<String, Map<String, S3Object>> containerToBlobs,
DateService dateService, Provider<BucketMetadata> containerMetaProvider, DateService dateService, Provider<BucketMetadata> containerMetaProvider,
Provider<S3Object> blobProvider) { Provider<S3Object> blobProvider) {
super(containerToBlobs, dateService, containerMetaProvider, blobProvider); super(containerToBlobs, dateService, containerMetaProvider, blobProvider);
@ -91,16 +92,18 @@ public class StubS3BlobStore extends StubBlobStore<BucketMetadata, ObjectMetadat
public static final String DEFAULT_OWNER_ID = "abc123"; public static final String DEFAULT_OWNER_ID = "abc123";
public Future<Boolean> createContainer(String name, @Nullable PutBucketOptions nullableOptions) { public Future<Boolean> putBucketIfNotExists(String name, PutBucketOptions... optionsList) {
final PutBucketOptions options = (nullableOptions == null) ? new PutBucketOptions() final PutBucketOptions options = (optionsList.length == 0) ? new PutBucketOptions()
: nullableOptions; : optionsList[0];
if (options.getLocationConstraint() != null) if (options.getLocationConstraint() != null)
bucketToLocation.put(name, options.getLocationConstraint()); bucketToLocation.put(name, options.getLocationConstraint());
keyToAcl.put(name, options.getAcl()); keyToAcl.put(name, options.getAcl());
return super.createContainer(name); return super.createContainer(name);
} }
public Future<ListBucketResponse> listBlobs(final String name, final ListBucketOptions options) { public Future<ListBucketResponse> listBucket(final String name, ListBucketOptions... optionsList) {
final ListBucketOptions options = (optionsList.length == 0) ? new ListBucketOptions()
: optionsList[0];
return new FutureBase<ListBucketResponse>() { return new FutureBase<ListBucketResponse>() {
public ListBucketResponse get() throws InterruptedException, ExecutionException { public ListBucketResponse get() throws InterruptedException, ExecutionException {
final Map<String, S3Object> realContents = getContainerToBlobs().get(name); final Map<String, S3Object> realContents = getContainerToBlobs().get(name);
@ -172,11 +175,11 @@ public class StubS3BlobStore extends StubBlobStore<BucketMetadata, ObjectMetadat
}; };
} }
public Future<ObjectMetadata> copyBlob(final String sourceBucket, final String sourceObject, public Future<ObjectMetadata> copyObject(final String sourceBucket, final String sourceObject,
final String destinationBucket, final String destinationObject, final String destinationBucket, final String destinationObject,
@Nullable CopyObjectOptions nullableOptions) { CopyObjectOptions... nullableOptions) {
final CopyObjectOptions options = (nullableOptions == null) ? new CopyObjectOptions() final CopyObjectOptions options = (nullableOptions.length == 0) ? new CopyObjectOptions()
: nullableOptions; : nullableOptions[0];
return new FutureBase<ObjectMetadata>() { return new FutureBase<ObjectMetadata>() {
public ObjectMetadata get() throws InterruptedException, ExecutionException { public ObjectMetadata get() throws InterruptedException, ExecutionException {
Map<String, S3Object> source = getContainerToBlobs().get(sourceBucket); Map<String, S3Object> source = getContainerToBlobs().get(sourceBucket);
@ -223,7 +226,7 @@ public class StubS3BlobStore extends StubBlobStore<BucketMetadata, ObjectMetadat
}; };
} }
public Future<byte[]> putBlob(final String bucketName, final S3Object object, public Future<byte[]> putObject(final String bucketName, final S3Object object,
@Nullable PutObjectOptions nullableOptions) { @Nullable PutObjectOptions nullableOptions) {
final PutObjectOptions options = (nullableOptions == null) ? new PutObjectOptions() final PutObjectOptions options = (nullableOptions == null) ? new PutObjectOptions()
: nullableOptions; : nullableOptions;
@ -248,7 +251,7 @@ public class StubS3BlobStore extends StubBlobStore<BucketMetadata, ObjectMetadat
return acl; return acl;
} }
public Future<AccessControlList> getContainerACL(final String bucket) { public Future<AccessControlList> getBucketACL(final String bucket) {
return new FutureBase<AccessControlList>() { return new FutureBase<AccessControlList>() {
public AccessControlList get() throws InterruptedException, ExecutionException { public AccessControlList get() throws InterruptedException, ExecutionException {
return getACLforS3Item(bucket); return getACLforS3Item(bucket);
@ -256,7 +259,7 @@ public class StubS3BlobStore extends StubBlobStore<BucketMetadata, ObjectMetadat
}; };
} }
public Future<AccessControlList> getBlobACL(final String bucket, final String objectKey) { public Future<AccessControlList> getObjectACL(final String bucket, final String objectKey) {
return new FutureBase<AccessControlList>() { return new FutureBase<AccessControlList>() {
public AccessControlList get() throws InterruptedException, ExecutionException { public AccessControlList get() throws InterruptedException, ExecutionException {
return getACLforS3Item(bucket + "/" + objectKey); return getACLforS3Item(bucket + "/" + objectKey);
@ -287,7 +290,7 @@ public class StubS3BlobStore extends StubBlobStore<BucketMetadata, ObjectMetadat
return acl; return acl;
} }
public Future<Boolean> putContainerACL(final String bucket, final AccessControlList acl) { public Future<Boolean> putBucketACL(final String bucket, final AccessControlList acl) {
return new FutureBase<Boolean>() { return new FutureBase<Boolean>() {
public Boolean get() throws InterruptedException, ExecutionException { public Boolean get() throws InterruptedException, ExecutionException {
keyToAcl.put(bucket, sanitizeUploadedACL(acl)); keyToAcl.put(bucket, sanitizeUploadedACL(acl));
@ -296,7 +299,7 @@ public class StubS3BlobStore extends StubBlobStore<BucketMetadata, ObjectMetadat
}; };
} }
public Future<Boolean> putBlobACL(final String bucket, final String objectKey, public Future<Boolean> putObjectACL(final String bucket, final String objectKey,
final AccessControlList acl) { final AccessControlList acl) {
return new FutureBase<Boolean>() { return new FutureBase<Boolean>() {
public Boolean get() throws InterruptedException, ExecutionException { public Boolean get() throws InterruptedException, ExecutionException {
@ -306,27 +309,33 @@ public class StubS3BlobStore extends StubBlobStore<BucketMetadata, ObjectMetadat
}; };
} }
public Future<ObjectMetadata> copyBlob(String sourceBucket, String sourceObject, public Future<S3Object> getObject(String bucketName, String key, GetOptions... optionsList) {
String destinationBucket, String destinationObject) { return super.getBlob(bucketName, key, optionsList.length != 0 ? optionsList[0] : null);
return copyBlob(sourceBucket, sourceObject, destinationBucket, destinationObject,
CopyObjectOptions.NONE);
} }
public Future<Boolean> createContainer(String bucketName) { public Future<byte[]> putObject(String bucketName, S3Object object,
return createContainer(bucketName, PutBucketOptions.NONE); PutObjectOptions... optionsList) {
return putObject(bucketName, object, optionsList.length != 0 ? optionsList[0] : null);
} }
@Override public boolean bucketExists(String bucketName) {
public Future<S3Object> getBlob(String bucketName, String key) { return super.containerExists(bucketName);
return getBlob(bucketName, key, GetOptions.NONE);
} }
public Future<ListBucketResponse> listBlobs(String bucketName) { public Future<Boolean> deleteBucketIfEmpty(String bucketName) {
return listBlobs(bucketName, ListBucketOptions.NONE); return super.deleteContainerImpl(bucketName);
} }
public Future<byte[]> putBlob(String bucketName, S3Object object) { public Future<Void> deleteObject(String bucketName, String key) {
return putBlob(bucketName, object, PutObjectOptions.NONE); return super.removeBlob(bucketName, key);
}
public ObjectMetadata headObject(String bucketName, String key) {
return super.blobMetadata(bucketName, key);
}
public SortedSet<BucketMetadata> listOwnedBuckets() {
return super.listContainers();
} }
} }

View File

@ -33,6 +33,7 @@ import java.util.SortedSet;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.jclouds.aws.s3.S3BlobStore; import org.jclouds.aws.s3.S3BlobStore;
import org.jclouds.aws.s3.S3Connection;
import org.jclouds.aws.s3.S3ContextFactory; import org.jclouds.aws.s3.S3ContextFactory;
import org.jclouds.aws.s3.domain.BucketMetadata; import org.jclouds.aws.s3.domain.BucketMetadata;
import org.jclouds.aws.s3.domain.ListBucketResponse; import org.jclouds.aws.s3.domain.ListBucketResponse;
@ -65,8 +66,8 @@ public class JCloudsS3Service extends S3Service {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private final BlobStoreContext<S3BlobStore, BucketMetadata, ObjectMetadata, org.jclouds.aws.s3.domain.S3Object> context; private final BlobStoreContext<S3Connection, BucketMetadata, ObjectMetadata, org.jclouds.aws.s3.domain.S3Object> context;
private final S3BlobStore connection; private final S3Connection connection;
private final long requestTimeoutMilliseconds = 10000; private final long requestTimeoutMilliseconds = 10000;
@ -106,7 +107,7 @@ public class JCloudsS3Service extends S3Service {
try { try {
CopyObjectOptions options = Util.convertCopyObjectOptions(acl, destinationMetadata, CopyObjectOptions options = Util.convertCopyObjectOptions(acl, destinationMetadata,
ifModifiedSince, ifUnmodifiedSince, ifMatchTags, ifNoneMatchTags); 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, sourceBucketName, sourceObjectKey, destinationBucketName, destinationObjectKey,
options).get(requestTimeoutMilliseconds, TimeUnit.MILLISECONDS); options).get(requestTimeoutMilliseconds, TimeUnit.MILLISECONDS);
@ -130,7 +131,7 @@ public class JCloudsS3Service extends S3Service {
throw new UnsupportedOperationException("Bucket ACL is not yet supported"); throw new UnsupportedOperationException("Bucket ACL is not yet supported");
try { try {
if (connection.createContainer(bucketName).get(requestTimeoutMilliseconds, if (connection.putBucketIfNotExists(bucketName).get(requestTimeoutMilliseconds,
TimeUnit.MILLISECONDS)) { TimeUnit.MILLISECONDS)) {
// Bucket created. // Bucket created.
} }
@ -149,7 +150,7 @@ public class JCloudsS3Service extends S3Service {
@Override @Override
protected void deleteBucketImpl(String bucketName) throws S3ServiceException { protected void deleteBucketImpl(String bucketName) throws S3ServiceException {
try { try {
connection.deleteContainer(bucketName).get(requestTimeoutMilliseconds, connection.deleteBucketIfEmpty(bucketName).get(requestTimeoutMilliseconds,
TimeUnit.MILLISECONDS); TimeUnit.MILLISECONDS);
} catch (Exception e) { } catch (Exception e) {
Utils.<S3ServiceException> rethrowIfRuntimeOrSameType(e); Utils.<S3ServiceException> rethrowIfRuntimeOrSameType(e);
@ -165,7 +166,7 @@ public class JCloudsS3Service extends S3Service {
@Override @Override
protected void deleteObjectImpl(String bucketName, String objectKey) throws S3ServiceException { protected void deleteObjectImpl(String bucketName, String objectKey) throws S3ServiceException {
try { try {
connection.removeBlob(bucketName, objectKey).get(requestTimeoutMilliseconds, connection.deleteObject(bucketName, objectKey).get(requestTimeoutMilliseconds,
TimeUnit.MILLISECONDS); TimeUnit.MILLISECONDS);
} catch (Exception e) { } catch (Exception e) {
Utils.<S3ServiceException> rethrowIfRuntimeOrSameType(e); Utils.<S3ServiceException> rethrowIfRuntimeOrSameType(e);
@ -177,7 +178,7 @@ public class JCloudsS3Service extends S3Service {
@Override @Override
protected AccessControlList getBucketAclImpl(String bucketName) throws S3ServiceException { protected AccessControlList getBucketAclImpl(String bucketName) throws S3ServiceException {
try { 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); .get(requestTimeoutMilliseconds, TimeUnit.MILLISECONDS);
return Util.convertAccessControlList(jcACL); return Util.convertAccessControlList(jcACL);
} catch (Exception e) { } catch (Exception e) {
@ -203,7 +204,7 @@ public class JCloudsS3Service extends S3Service {
protected AccessControlList getObjectAclImpl(String bucketName, String objectKey) protected AccessControlList getObjectAclImpl(String bucketName, String objectKey)
throws S3ServiceException { throws S3ServiceException {
try { 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); objectKey).get(requestTimeoutMilliseconds, TimeUnit.MILLISECONDS);
return Util.convertAccessControlList(jcACL); return Util.convertAccessControlList(jcACL);
} catch (Exception e) { } catch (Exception e) {
@ -226,7 +227,7 @@ public class JCloudsS3Service extends S3Service {
if (ifNoneMatchTags != null) if (ifNoneMatchTags != null)
throw new IllegalArgumentException("ifNoneMatchTags"); throw new IllegalArgumentException("ifNoneMatchTags");
return Util.convertObjectHead(connection.blobMetadata(bucketName, objectKey)); return Util.convertObjectHead(connection.headObject(bucketName, objectKey));
} catch (Exception e) { } catch (Exception e) {
Utils.<S3ServiceException> rethrowIfRuntimeOrSameType(e); Utils.<S3ServiceException> rethrowIfRuntimeOrSameType(e);
throw new S3ServiceException(String.format("error retrieving object head: %1$s:%2$s", throw new S3ServiceException(String.format("error retrieving object head: %1$s:%2$s",
@ -241,7 +242,7 @@ public class JCloudsS3Service extends S3Service {
try { try {
GetOptions options = Util.convertGetObjectOptions(ifModifiedSince, ifUnmodifiedSince, GetOptions options = Util.convertGetObjectOptions(ifModifiedSince, ifUnmodifiedSince,
ifMatchTags, ifNoneMatchTags); ifMatchTags, ifNoneMatchTags);
return Util.convertObject(connection.getBlob(bucketName, objectKey, options).get( return Util.convertObject(connection.getObject(bucketName, objectKey, options).get(
requestTimeoutMilliseconds, TimeUnit.MILLISECONDS)); requestTimeoutMilliseconds, TimeUnit.MILLISECONDS));
} catch (Exception e) { } catch (Exception e) {
Utils.<S3ServiceException> rethrowIfRuntimeOrSameType(e); Utils.<S3ServiceException> rethrowIfRuntimeOrSameType(e);
@ -265,7 +266,8 @@ public class JCloudsS3Service extends S3Service {
@Override @Override
protected S3Bucket[] listAllBucketsImpl() throws S3ServiceException { protected S3Bucket[] listAllBucketsImpl() throws S3ServiceException {
try { try {
SortedSet<org.jclouds.aws.s3.domain.BucketMetadata> jcBucketList = connection.listContainers(); SortedSet<org.jclouds.aws.s3.domain.BucketMetadata> jcBucketList = connection
.listOwnedBuckets();
return Util.convertBuckets(jcBucketList); return Util.convertBuckets(jcBucketList);
} catch (Exception e) { } catch (Exception e) {
Utils.<S3ServiceException> rethrowIfRuntimeOrSameType(e); Utils.<S3ServiceException> rethrowIfRuntimeOrSameType(e);
@ -285,7 +287,7 @@ public class JCloudsS3Service extends S3Service {
ListBucketOptions options = Util.convertListObjectOptions(prefix, priorLastKey, ListBucketOptions options = Util.convertListObjectOptions(prefix, priorLastKey,
delimiter, maxListingLength); delimiter, maxListingLength);
jcBucket = connection.listBlobs(bucketName, options).get(requestTimeoutMilliseconds, jcBucket = connection.listBucket(bucketName, options).get(requestTimeoutMilliseconds,
TimeUnit.MILLISECONDS); TimeUnit.MILLISECONDS);
jsObjects.addAll(Arrays.asList(Util.convertObjectHeads(jcBucket))); jsObjects.addAll(Arrays.asList(Util.convertObjectHeads(jcBucket)));
@ -325,7 +327,7 @@ public class JCloudsS3Service extends S3Service {
throws S3ServiceException { throws S3ServiceException {
try { try {
org.jclouds.aws.s3.domain.AccessControlList jcACL = Util.convertAccessControlList(jsACL); org.jclouds.aws.s3.domain.AccessControlList jcACL = Util.convertAccessControlList(jsACL);
connection.putContainerACL(bucketName, jcACL).get(requestTimeoutMilliseconds, connection.putBucketACL(bucketName, jcACL).get(requestTimeoutMilliseconds,
TimeUnit.MILLISECONDS); TimeUnit.MILLISECONDS);
} catch (Exception e) { } catch (Exception e) {
Utils.<S3ServiceException> rethrowIfRuntimeOrSameType(e); Utils.<S3ServiceException> rethrowIfRuntimeOrSameType(e);
@ -338,7 +340,7 @@ public class JCloudsS3Service extends S3Service {
throws S3ServiceException { throws S3ServiceException {
try { try {
org.jclouds.aws.s3.domain.AccessControlList jcACL = Util.convertAccessControlList(jsACL); 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); TimeUnit.MILLISECONDS);
} catch (Exception e) { } catch (Exception e) {
Utils.<S3ServiceException> rethrowIfRuntimeOrSameType(e); Utils.<S3ServiceException> rethrowIfRuntimeOrSameType(e);
@ -351,7 +353,7 @@ public class JCloudsS3Service extends S3Service {
try { try {
PutObjectOptions options = Util.convertPutObjectOptions(jsObject.getAcl()); PutObjectOptions options = Util.convertPutObjectOptions(jsObject.getAcl());
org.jclouds.aws.s3.domain.S3Object jcObject = Util.convertObject(jsObject); 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); requestTimeoutMilliseconds, TimeUnit.MILLISECONDS);
jsObject.setMd5Hash(eTag); jsObject.setMd5Hash(eTag);
return jsObject; return jsObject;

View File

@ -44,7 +44,7 @@ import java.util.concurrent.TimeoutException;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import org.apache.commons.io.IOUtils; 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.BucketMetadata;
import org.jclouds.aws.s3.domain.ListBucketResponse; import org.jclouds.aws.s3.domain.ListBucketResponse;
import org.jclouds.aws.s3.domain.ObjectMetadata; import org.jclouds.aws.s3.domain.ObjectMetadata;
@ -82,7 +82,7 @@ import com.google.common.collect.Iterators;
@Test(groups = { "live" }, testName = "jets3t.JCloudsS3ServiceIntegrationTest") @Test(groups = { "live" }, testName = "jets3t.JCloudsS3ServiceIntegrationTest")
public class JCloudsS3ServiceLiveTest public class JCloudsS3ServiceLiveTest
extends extends
BaseBlobStoreIntegrationTest<S3BlobStore, BucketMetadata, ObjectMetadata, org.jclouds.aws.s3.domain.S3Object> { BaseBlobStoreIntegrationTest<S3Connection, BucketMetadata, ObjectMetadata, org.jclouds.aws.s3.domain.S3Object> {
AWSCredentials credentials; AWSCredentials credentials;
S3Service service; S3Service service;
@ -112,7 +112,7 @@ public class JCloudsS3ServiceLiveTest
try { try {
S3Bucket bucket = service.createBucket(new S3Bucket(bucketName)); S3Bucket bucket = service.createBucket(new S3Bucket(bucketName));
assertEquals(bucket.getName(), bucketName); assertEquals(bucket.getName(), bucketName);
assertTrue(context.getApi().containerExists(bucketName)); assertTrue(context.getApi().bucketExists(bucketName));
} finally { } finally {
returnContainer(bucketName); returnContainer(bucketName);
} }
@ -125,7 +125,7 @@ public class JCloudsS3ServiceLiveTest
service.deleteBucket(bucketName); service.deleteBucket(bucketName);
assertEventually(new Runnable() { assertEventually(new Runnable() {
public void run() { public void run() {
assertFalse(context.getApi().containerExists(bucketName)); assertFalse(context.getApi().bucketExists(bucketName));
} }
}); });
} }
@ -212,7 +212,7 @@ public class JCloudsS3ServiceLiveTest
S3Bucket[] jsBuckets = service.listAllBuckets(); S3Bucket[] jsBuckets = service.listAllBuckets();
SortedSet<org.jclouds.aws.s3.domain.BucketMetadata> jcBuckets = context.getApi() SortedSet<org.jclouds.aws.s3.domain.BucketMetadata> jcBuckets = context.getApi()
.listContainers(); .listOwnedBuckets();
assert jsBuckets.length == jcBuckets.size(); assert jsBuckets.length == jcBuckets.size();
@ -363,7 +363,7 @@ public class JCloudsS3ServiceLiveTest
// Upload empty object // Upload empty object
requestObject = new S3Object(objectKey); requestObject = new S3Object(objectKey);
jsResultObject = service.putObject(new S3Bucket(bucketName), requestObject); 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); // TODO null keys from s3object! assertEquals(jcObject.getKey(), objectKey);
assertEquals(jcObject.getMetadata().getSize(), 0); assertEquals(jcObject.getMetadata().getSize(), 0);
assertEquals(jcObject.getMetadata().getContentType(), MediaType.APPLICATION_OCTET_STREAM); assertEquals(jcObject.getMetadata().getContentType(), MediaType.APPLICATION_OCTET_STREAM);
@ -374,7 +374,7 @@ public class JCloudsS3ServiceLiveTest
// Upload unicode-named object // Upload unicode-named object
requestObject = new S3Object("Ÿn<EFBFBD>˜dŽ-object"); requestObject = new S3Object("Ÿn<EFBFBD>˜dŽ-object");
jsResultObject = service.putObject(new S3Bucket(bucketName), requestObject); 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); TimeUnit.SECONDS);
// TODO null keys from s3object! assertEquals(jcObject.getKey(), requestObject.getKey()); // TODO null keys from s3object! assertEquals(jcObject.getKey(), requestObject.getKey());
assertEquals(jcObject.getMetadata().getSize(), 0); assertEquals(jcObject.getMetadata().getSize(), 0);
@ -387,7 +387,7 @@ public class JCloudsS3ServiceLiveTest
String data = "This is my Ÿn<6E>˜dŽ data"; String data = "This is my Ÿn<6E>˜dŽ data";
requestObject = new S3Object(objectKey, data); requestObject = new S3Object(objectKey, data);
jsResultObject = service.putObject(new S3Bucket(bucketName), requestObject); 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); assertEquals(jcObject.getMetadata().getSize(), data.getBytes("UTF-8").length);
assertTrue(jcObject.getMetadata().getContentType().startsWith("text/plain")); assertTrue(jcObject.getMetadata().getContentType().startsWith("text/plain"));
assertEquals(jsResultObject.getContentLength(), data.getBytes("UTF-8").length); assertEquals(jsResultObject.getContentLength(), data.getBytes("UTF-8").length);
@ -397,7 +397,7 @@ public class JCloudsS3ServiceLiveTest
requestObject = new S3Object(objectKey); requestObject = new S3Object(objectKey);
requestObject.addMetadata("x-amz-meta-" + "my-metadata-1", "value-1"); requestObject.addMetadata("x-amz-meta-" + "my-metadata-1", "value-1");
jsResultObject = service.putObject(new S3Bucket(bucketName), requestObject); 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( assertEquals(Iterables.getLast(jcObject.getMetadata().getUserMetadata().get(
"my-metadata-1")), "value-1"); "my-metadata-1")), "value-1");
assertEquals(jsResultObject.getMetadata("x-amz-meta-" + "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 = new S3Object(objectKey);
requestObject.setAcl(AccessControlList.REST_CANNED_PUBLIC_READ); requestObject.setAcl(AccessControlList.REST_CANNED_PUBLIC_READ);
jsResultObject = service.putObject(new S3Bucket(bucketName), requestObject); 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); bucketName, objectKey).get(10, TimeUnit.SECONDS);
assertTrue(jcACL.hasPermission(GroupGranteeURI.ALL_USERS, Permission.READ)); assertTrue(jcACL.hasPermission(GroupGranteeURI.ALL_USERS, Permission.READ));
assertTrue(jcACL.hasPermission(jcACL.getOwner().getId(), Permission.FULL_CONTROL)); assertTrue(jcACL.hasPermission(jcACL.getOwner().getId(), Permission.FULL_CONTROL));
@ -422,7 +422,7 @@ public class JCloudsS3ServiceLiveTest
data = "Here is some d‡tˆ for you"; data = "Here is some d‡tˆ for you";
requestObject.setDataInputStream(new ByteArrayInputStream(data.getBytes("UTF-8"))); requestObject.setDataInputStream(new ByteArrayInputStream(data.getBytes("UTF-8")));
jsResultObject = service.putObject(new S3Bucket(bucketName), requestObject); 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"))); assertTrue(jsResultObject.verifyData(data.getBytes("UTF-8")));
assertEquals(jsResultObject.getMd5HashAsHex(), HttpUtils.toHexString(jcObject assertEquals(jsResultObject.getMd5HashAsHex(), HttpUtils.toHexString(jcObject
.getMetadata().getETag())); .getMetadata().getETag()));
@ -458,7 +458,7 @@ public class JCloudsS3ServiceLiveTest
destinationObject = new S3Object(destinationObjectKey); destinationObject = new S3Object(destinationObjectKey);
copyResult = service.copyObject(bucketName, sourceObjectKey, bucketName, copyResult = service.copyObject(bucketName, sourceObjectKey, bucketName,
destinationObject, false); destinationObject, false);
jcDestinationObject = context.getApi().getBlob(bucketName, destinationObject.getKey()) jcDestinationObject = context.getApi().getObject(bucketName, destinationObject.getKey())
.get(10, TimeUnit.SECONDS); .get(10, TimeUnit.SECONDS);
// TODO null keys from s3object! assertEquals(jcDestinationObject.getKey(), // TODO null keys from s3object! assertEquals(jcDestinationObject.getKey(),
// destinationObjectKey); // destinationObjectKey);
@ -467,7 +467,7 @@ public class JCloudsS3ServiceLiveTest
assertEquals(copyResult.get("ETag"), HttpUtils.toHexString(jcDestinationObject assertEquals(copyResult.get("ETag"), HttpUtils.toHexString(jcDestinationObject
.getMetadata().getETag())); .getMetadata().getETag()));
// Test destination ACL is unchanged (ie private) // 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); bucketName, destinationObject.getKey()).get(10, TimeUnit.SECONDS);
assertEquals(jcACL.getGrants().size(), 1); assertEquals(jcACL.getGrants().size(), 1);
assertTrue(jcACL.hasPermission(jcACL.getOwner().getId(), Permission.FULL_CONTROL)); assertTrue(jcACL.hasPermission(jcACL.getOwner().getId(), Permission.FULL_CONTROL));
@ -477,12 +477,12 @@ public class JCloudsS3ServiceLiveTest
destinationObject.addMetadata("x-amz-meta-" + metadataName, destinationMetadataValue); destinationObject.addMetadata("x-amz-meta-" + metadataName, destinationMetadataValue);
copyResult = service.copyObject(bucketName, sourceObjectKey, bucketName, copyResult = service.copyObject(bucketName, sourceObjectKey, bucketName,
destinationObject, true); destinationObject, true);
jcDestinationObject = context.getApi().getBlob(bucketName, destinationObject.getKey()) jcDestinationObject = context.getApi().getObject(bucketName, destinationObject.getKey())
.get(10, TimeUnit.SECONDS); .get(10, TimeUnit.SECONDS);
assertEquals(Iterators.getLast(jcDestinationObject.getMetadata().getUserMetadata().get( assertEquals(Iterators.getLast(jcDestinationObject.getMetadata().getUserMetadata().get(
metadataName).iterator()), destinationMetadataValue); metadataName).iterator()), destinationMetadataValue);
// Test destination ACL is unchanged (ie private) // 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); TimeUnit.SECONDS);
assertEquals(jcACL.getGrants().size(), 1); assertEquals(jcACL.getGrants().size(), 1);
assertTrue(jcACL.hasPermission(jcACL.getOwner().getId(), Permission.FULL_CONTROL)); assertTrue(jcACL.hasPermission(jcACL.getOwner().getId(), Permission.FULL_CONTROL));
@ -493,7 +493,7 @@ public class JCloudsS3ServiceLiveTest
copyResult = service.copyObject(bucketName, sourceObjectKey, bucketName, copyResult = service.copyObject(bucketName, sourceObjectKey, bucketName,
destinationObject, false); destinationObject, false);
// Test destination ACL is changed (ie public-read) // 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); TimeUnit.SECONDS);
assertEquals(jcACL.getGrants().size(), 2); assertEquals(jcACL.getGrants().size(), 2);
assertTrue(jcACL.hasPermission(jcACL.getOwner().getId(), Permission.FULL_CONTROL)); assertTrue(jcACL.hasPermission(jcACL.getOwner().getId(), Permission.FULL_CONTROL));
@ -673,7 +673,7 @@ public class JCloudsS3ServiceLiveTest
multiService.putObjects(bucket, objects); multiService.putObjects(bucket, objects);
assertEquals(countOfUploadCompletions[0], OBJECT_COUNT); assertEquals(countOfUploadCompletions[0], OBJECT_COUNT);
ListBucketResponse theBucket = context.getApi().listBlobs(bucketName).get(10, ListBucketResponse theBucket = context.getApi().listBucket(bucketName).get(10,
TimeUnit.SECONDS); TimeUnit.SECONDS);
assertEquals(theBucket.size(), OBJECT_COUNT); assertEquals(theBucket.size(), OBJECT_COUNT);

View File

@ -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); org.jclouds.aws.s3.domain.S3Object object = new org.jclouds.aws.s3.domain.S3Object(key);
object.getMetadata().setContentType(contentType); object.getMetadata().setContentType(contentType);
object.setData(data); 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 @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); org.jclouds.aws.s3.domain.S3Object object = new org.jclouds.aws.s3.domain.S3Object(key);
object.getMetadata().setContentType(contentType); object.getMetadata().setContentType(contentType);
object.setData(data); 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 @Override
@ -76,7 +76,7 @@ public abstract class BaseJCloudsPerformanceLiveTest extends BasePerformanceLive
object.getMetadata().setContentType(contentType); object.getMetadata().setContentType(contentType);
object.setData(data); object.setData(data);
object.getMetadata().setSize(data.available()); 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 @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); org.jclouds.aws.s3.domain.S3Object object = new org.jclouds.aws.s3.domain.S3Object(key);
object.getMetadata().setContentType(contentType); object.getMetadata().setContentType(contentType);
object.setData(data); 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;
} }
} }

View File

@ -55,7 +55,7 @@ import org.testng.annotations.Test;
* @author Adrian Cole * @author Adrian Cole
*/ */
public abstract class BasePerformanceLiveTest extends public abstract class BasePerformanceLiveTest extends
BaseBlobStoreIntegrationTest<S3BlobStore, BucketMetadata, ObjectMetadata, S3Object> { BaseBlobStoreIntegrationTest<S3Connection, BucketMetadata, ObjectMetadata, S3Object> {
protected int timeoutSeconds = 10; protected int timeoutSeconds = 10;
protected int loopCount = 100; protected int loopCount = 100;
protected ExecutorService exec; protected ExecutorService exec;
@ -94,7 +94,7 @@ public abstract class BasePerformanceLiveTest extends
protected String createScratchContainerInEU() throws InterruptedException, ExecutionException, protected String createScratchContainerInEU() throws InterruptedException, ExecutionException,
TimeoutException { TimeoutException {
String containerName = getScratchContainerName(); String containerName = getScratchContainerName();
context.getApi().createContainer(containerName, createIn(LocationConstraint.EU)).get(30, context.getApi().putBucketIfNotExists(containerName, createIn(LocationConstraint.EU)).get(30,
TimeUnit.SECONDS); TimeUnit.SECONDS);
return containerName; return containerName;
} }

View File

@ -66,14 +66,14 @@ public class MainApp {
try { try {
// Create Bucket // 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); BlobMap blobMap = context.createBlobMap(bucketName);
Blob blob = context.newBlob("test"); Blob blob = context.newBlob("test");
blob.setData("testdata"); blob.setData("testdata");
blobMap.put("test", blob); blobMap.put("test", blob);
// List bucket // 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", bucketObj));
System.out.println(String.format(": %1$s entries%n", context.createInputStreamMap( System.out.println(String.format(": %1$s entries%n", context.createInputStreamMap(
bucketObj.getName()).size())); bucketObj.getName()).size()));

View File

@ -86,7 +86,7 @@ public class GetAllBucketsController extends HttpServlet {
private void addMyBucketsToRequest(HttpServletRequest request) throws InterruptedException, private void addMyBucketsToRequest(HttpServletRequest request) throws InterruptedException,
ExecutionException, TimeoutException { ExecutionException, TimeoutException {
System.err.println(context.getAccount() + ":" + context.getEndPoint()); System.err.println(context.getAccount() + ":" + context.getEndPoint());
SortedSet<BucketMetadata> myBucketMetadata = context.getApi().listContainers(); SortedSet<BucketMetadata> myBucketMetadata = context.getApi().listOwnedBuckets();
SortedSet<BucketResult> myBuckets = Sets.newTreeSet(Iterables.transform(myBucketMetadata, SortedSet<BucketResult> myBuckets = Sets.newTreeSet(Iterables.transform(myBucketMetadata,
metadataToBucketResultProvider.get())); metadataToBucketResultProvider.get()));
request.setAttribute("buckets", myBuckets); request.setAttribute("buckets", myBuckets);

View File

@ -108,14 +108,14 @@ public class JCloudsServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException { throws ServletException, IOException {
try { try {
SortedSet<BucketMetadata> myBucketMetadata = context.getApi().listContainers(); SortedSet<BucketMetadata> myBucketMetadata = context.getApi().listOwnedBuckets();
SortedSet<BucketResult> myBuckets = Sets.newTreeSet(); SortedSet<BucketResult> myBuckets = Sets.newTreeSet();
for (BucketMetadata metadata : myBucketMetadata) { for (BucketMetadata metadata : myBucketMetadata) {
BucketResult result = new BucketResult(); BucketResult result = new BucketResult();
result.setName(metadata.getName()); result.setName(metadata.getName());
try { try {
try { try {
ListBucketResponse bucket = context.getApi().listBlobs(metadata.getName()).get( ListBucketResponse bucket = context.getApi().listBucket(metadata.getName()).get(
10, TimeUnit.SECONDS); 10, TimeUnit.SECONDS);
result.setSize(bucket.size() + ""); result.setSize(bucket.size() + "");
} catch (ContainerNotFoundException ex) { } catch (ContainerNotFoundException ex) {

View File

@ -0,0 +1,316 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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.
* <p/>
* 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 <a href="http://msdn.microsoft.com/en-us/library/dd135733.aspx" />
* @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.
* <p />
* 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<ContainerMetadata> 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.
* <p/>
* 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<Boolean> 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. <h4>Remarks</h4>
*
*
* 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.
* <p/>
* You may also set metadata for a container at the time it is created.
* <p/>
* 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<String, String> 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.
* <p/>
* 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<Void> 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.
* <p/>
* 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<Boolean> 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. <h4>Remarks</h4>
* 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<Boolean> deleteRootContainer();
/**
* The List Blobs operation enumerates the list of blobs under the specified container.
* <p/>
* <h4>Authorization</h4>
*
* If the container's access control list (ACL) is set to allow anonymous access, any client may
* call this operation.
* <h4>Remarks</h4>
*
* 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.
* <p/>
* Note that the value of NextMarker should be treated as opaque.
* <p/>
* 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.
* <p/>
* 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.
* <p/>
* Note that each BlobPrefix element returned counts toward the maximum result, just as each Blob
* element does.
* <p/>
* Blobs are listed in alphabetical order in the response body.
*/
@GET
@XMLResponseParser(ContainerNameEnumerationResultsHandler.class)
@Path("{container}")
@QueryParams(keys = { "restype", "comp" }, values = { "container", "list" })
Future<ListBlobsResponse> listBlobs(@PathParam("container") String container,
ListBlobsOptions... options);
@GET
@XMLResponseParser(ContainerNameEnumerationResultsHandler.class)
@Path("$root")
@QueryParams(keys = { "restype", "comp" }, values = { "container", "list" })
Future<ListBlobsResponse> listBlobs(ListBlobsOptions... options);
/**
* The Put Blob operation creates a new blob or updates the content of an existing blob.
* <p/>
* 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.
* <p/>
* <h4>Remarks</h4>
* 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.
* <p/>
* 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.
* <p/>
* 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<byte[]> 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<Blob> 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<String, String> 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<Void> deleteBlob(@PathParam("container") String container, @PathParam("key") String key);
}

View File

@ -39,6 +39,6 @@ import org.jclouds.cloud.CloudContext;
* *
*/ */
public interface AzureBlobContext extends public interface AzureBlobContext extends
BlobStoreContext<AzureBlobStore, ContainerMetadata, BlobMetadata, Blob> { BlobStoreContext<AzureBlobConnection, ContainerMetadata, BlobMetadata, Blob> {
} }

View File

@ -60,7 +60,7 @@ import com.google.inject.TypeLiteral;
* @see AzureBlobContext * @see AzureBlobContext
*/ */
public class AzureBlobContextBuilder extends public class AzureBlobContextBuilder extends
BlobStoreContextBuilder<AzureBlobStore, ContainerMetadata, BlobMetadata, Blob> { BlobStoreContextBuilder<AzureBlobConnection, ContainerMetadata, BlobMetadata, Blob> {
@Override @Override
public AzureBlobContext buildContext() { public AzureBlobContext buildContext() {
@ -68,7 +68,7 @@ public class AzureBlobContextBuilder extends
} }
public AzureBlobContextBuilder(Properties props) { public AzureBlobContextBuilder(Properties props) {
super(new TypeLiteral<AzureBlobStore>() { super(new TypeLiteral<AzureBlobConnection>() {
}, new TypeLiteral<ContainerMetadata>() { }, new TypeLiteral<ContainerMetadata>() {
}, new TypeLiteral<BlobMetadata>() { }, new TypeLiteral<BlobMetadata>() {
}, new TypeLiteral<Blob>() { }, new TypeLiteral<Blob>() {
@ -78,6 +78,12 @@ public class AzureBlobContextBuilder extends
"https://{account}.blob.core.windows.net"); "https://{account}.blob.core.windows.net");
} }
// @Override
// protected void addBlobStoreModule(List<Module> modules) {
// modules.add(BlobStoreMapsModule.Builder.newBuilder(containerMetadataType, blobMetadataType,
// blobType).withClearContainerStrategy(RecreateClearContainerStrategy.class).build());
// }
public AzureBlobContextBuilder(String id, String secret) { public AzureBlobContextBuilder(String id, String secret) {
this(new Properties()); this(new Properties());
properties.setProperty(AzureStorageConstants.PROPERTY_AZURESTORAGE_ACCOUNT, checkNotNull(id, properties.setProperty(AzureStorageConstants.PROPERTY_AZURESTORAGE_ACCOUNT, checkNotNull(id,
@ -152,7 +158,7 @@ public class AzureBlobContextBuilder extends
@Override @Override
public AzureBlobContextBuilder withSaxDebug() { public AzureBlobContextBuilder withSaxDebug() {
return (AzureBlobContextBuilder) (AzureBlobContextBuilder) super.withSaxDebug(); return (AzureBlobContextBuilder) super.withSaxDebug();
} }
@Override @Override

View File

@ -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.BlobMetadata;
import org.jclouds.azure.storage.blob.domain.ContainerMetadata; import org.jclouds.azure.storage.blob.domain.ContainerMetadata;
import org.jclouds.azure.storage.blob.domain.ListBlobsResponse; 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.ParseBlobFromHeadersAndHttpContent;
import org.jclouds.azure.storage.blob.functions.ParseBlobMetadataFromHeaders; import org.jclouds.azure.storage.blob.functions.ParseBlobMetadataFromHeaders;
import org.jclouds.azure.storage.blob.functions.ReturnTrueIfContainerAlreadyExists; import org.jclouds.azure.storage.blob.functions.ReturnTrueIfContainerAlreadyExists;
import org.jclouds.azure.storage.blob.options.CreateContainerOptions; import org.jclouds.azure.storage.blob.options.CreateContainerOptions;
import org.jclouds.azure.storage.blob.xml.AccountNameEnumerationResultsHandler; import org.jclouds.azure.storage.blob.xml.AccountNameEnumerationResultsHandler;
import org.jclouds.azure.storage.blob.xml.ContainerNameEnumerationResultsHandler; import org.jclouds.azure.storage.blob.xml.AddMD5ToListBlobsResponse;
import org.jclouds.azure.storage.domain.BoundedSortedSet;
import org.jclouds.azure.storage.filters.SharedKeyAuthentication; 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.options.ListOptions;
import org.jclouds.azure.storage.reference.AzureStorageHeaders; import org.jclouds.azure.storage.reference.AzureStorageHeaders;
import org.jclouds.blobstore.BlobStore; import org.jclouds.blobstore.BlobStore;
import org.jclouds.blobstore.binders.BlobBinder;
import org.jclouds.blobstore.functions.BlobKey; import org.jclouds.blobstore.functions.BlobKey;
import org.jclouds.blobstore.functions.ReturnVoidOnNotFoundOr404;
import org.jclouds.blobstore.functions.ThrowKeyNotFoundOn404; import org.jclouds.blobstore.functions.ThrowKeyNotFoundOn404;
import org.jclouds.http.functions.ParseETagHeader; import org.jclouds.http.functions.ParseETagHeader;
import org.jclouds.http.functions.ReturnFalseOn404; import org.jclouds.http.functions.ReturnFalseOn404;
import org.jclouds.http.functions.ReturnTrueOn404;
import org.jclouds.http.options.GetOptions; import org.jclouds.http.options.GetOptions;
import org.jclouds.rest.Endpoint; import org.jclouds.rest.Endpoint;
import org.jclouds.rest.EntityParam; import org.jclouds.rest.EntityParam;
@ -99,12 +97,6 @@ public interface AzureBlobStore extends BlobStore<ContainerMetadata, BlobMetadat
@QueryParams(keys = "comp", values = "list") @QueryParams(keys = "comp", values = "list")
SortedSet<ContainerMetadata> listContainers(); SortedSet<ContainerMetadata> listContainers();
@GET
@XMLResponseParser(AccountNameEnumerationResultsHandler.class)
@Path("/")
@QueryParams(keys = "comp", values = "list")
BoundedSortedSet<ContainerMetadata> listContainers(ListOptions listOptions);
@HEAD @HEAD
@Path("{container}") @Path("{container}")
@ExceptionParser(ReturnFalseOn404.class) @ExceptionParser(ReturnFalseOn404.class)
@ -127,13 +119,6 @@ public interface AzureBlobStore extends BlobStore<ContainerMetadata, BlobMetadat
@QueryParams(keys = "restype", values = "container") @QueryParams(keys = "restype", values = "container")
Future<Boolean> createContainer(@PathParam("container") String container); Future<Boolean> createContainer(@PathParam("container") String container);
@PUT
@Path("{container}")
@QueryParams(keys = "restype", values = "container")
@ExceptionParser(ReturnTrueIfContainerAlreadyExists.class)
Future<Boolean> createContainer(@PathParam("container") String container,
CreateContainerOptions options);
/** /**
* The Delete Container operation marks the specified container for deletion. The container and * The Delete Container operation marks the specified container for deletion. The container and
* any blobs contained within it are later deleted during garbage collection. * any blobs contained within it are later deleted during garbage collection.
@ -149,68 +134,22 @@ public interface AzureBlobStore extends BlobStore<ContainerMetadata, BlobMetadat
*/ */
@DELETE @DELETE
@Path("{container}") @Path("{container}")
@ExceptionParser(ReturnTrueOn404.class) @ExceptionParser(ReturnVoidOnNotFoundOr404.class)
@QueryParams(keys = "restype", values = "container") @QueryParams(keys = "restype", values = "container")
Future<Boolean> deleteContainer(@PathParam("container") String container); Future<Void> 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.
* <p/>
* 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<Boolean> createRootContainer();
@PUT
@Path("$root")
@ExceptionParser(ReturnTrueIfContainerAlreadyExists.class)
@QueryParams(keys = "restype", values = "container")
Future<Boolean> createRootContainer(CreateOptions options);
/**
*
* @see deleteContainer(String)
* @see createRootContainer(CreateContainerOptions)
*/
@DELETE
@Path("$root")
@ExceptionParser(ReturnTrueOn404.class)
@QueryParams(keys = "restype", values = "container")
Future<Boolean> deleteRootContainer();
@GET @GET
@XMLResponseParser(ContainerNameEnumerationResultsHandler.class) @XMLResponseParser(AddMD5ToListBlobsResponse.class)
@Path("{container}") @Path("{container}")
@QueryParams(keys = { "restype", "comp" }, values = { "container", "list" }) @QueryParams(keys = { "restype", "comp" }, values = { "container", "list" })
Future<ListBlobsResponse> listBlobs(@PathParam("container") String container); Future<ListBlobsResponse> listBlobs(@PathParam("container") String container);
@GET
@XMLResponseParser(ContainerNameEnumerationResultsHandler.class)
@Path("$root")
@QueryParams(keys = { "restype", "comp" }, values = { "container", "list" })
Future<ListBlobsResponse> listBlobs();
// @GET
// @XMLResponseParser(ContainerNameEnumerationResultsHandler.class)
// @Path("{container}")
// @QueryParams(keys = { "restype", "comp" }, values = { "container", "list" })
// Future<ListBlobsResponse> listBlobs(@PathParam("container") String container,
// ListBlobsOptions options);
@PUT @PUT
@Path("{container}/{key}") @Path("{container}/{key}")
@ResponseParser(ParseETagHeader.class) @ResponseParser(ParseETagHeader.class)
Future<byte[]> putBlob(@PathParam("container") String container, Future<byte[]> putBlob(
@PathParam("key") @ParamParser(BlobKey.class) @EntityParam(BlobBinder.class) Blob object); @PathParam("container") String container,
@PathParam("key") @ParamParser(BlobKey.class) @EntityParam(MD5EnforcingBlobBinder.class) Blob object);
@GET @GET
@ResponseParser(ParseBlobFromHeadersAndHttpContent.class) @ResponseParser(ParseBlobFromHeadersAndHttpContent.class)
@ -234,8 +173,8 @@ public interface AzureBlobStore extends BlobStore<ContainerMetadata, BlobMetadat
BlobMetadata blobMetadata(@PathParam("container") String container, @PathParam("key") String key); BlobMetadata blobMetadata(@PathParam("container") String container, @PathParam("key") String key);
@DELETE @DELETE
@ExceptionParser(ReturnTrueOn404.class) @ExceptionParser(ReturnVoidOnNotFoundOr404.class)
@Path("{container}/{key}") @Path("{container}/{key}")
Future<Boolean> removeBlob(@PathParam("container") String container, @PathParam("key") String key); Future<Void> removeBlob(@PathParam("container") String container, @PathParam("key") String key);
} }

View File

@ -0,0 +1,63 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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
* <p/>
*
* @see <a href="http://msdn.microsoft.com/en-us/library/dd135733.aspx" />
* @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);
}

View File

@ -44,7 +44,8 @@ public class BlobBinder extends org.jclouds.blobstore.binders.BlobBinder {
public void addEntityToRequest(Object entity, HttpRequest request) { public void addEntityToRequest(Object entity, HttpRequest request) {
Blob<?> object = (Blob<?>) entity; Blob<?> object = (Blob<?>) entity;
checkArgument(object.getMetadata().getSize() >= 0, "size must be set"); 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) { if (object.getMetadata() instanceof BlobMetadata) {
BlobMetadata md = (BlobMetadata) object.getMetadata(); BlobMetadata md = (BlobMetadata) object.getMetadata();

View File

@ -30,12 +30,14 @@ import javax.inject.Named;
import javax.inject.Provider; import javax.inject.Provider;
import org.jclouds.azure.storage.AzureBlob; 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.AzureBlobContext;
import org.jclouds.azure.storage.blob.AzureBlobStore; import org.jclouds.azure.storage.blob.AzureBlobStore;
import org.jclouds.azure.storage.blob.domain.Blob; import org.jclouds.azure.storage.blob.domain.Blob;
import org.jclouds.azure.storage.blob.domain.BlobMetadata; import org.jclouds.azure.storage.blob.domain.BlobMetadata;
import org.jclouds.azure.storage.blob.domain.ContainerMetadata; import org.jclouds.azure.storage.blob.domain.ContainerMetadata;
import org.jclouds.azure.storage.reference.AzureStorageConstants; import org.jclouds.azure.storage.reference.AzureStorageConstants;
import org.jclouds.blobstore.BlobStore;
import org.jclouds.blobstore.BlobStoreContextImpl; import org.jclouds.blobstore.BlobStoreContextImpl;
import org.jclouds.blobstore.BlobMap.Factory; import org.jclouds.blobstore.BlobMap.Factory;
import org.jclouds.lifecycle.Closer; import org.jclouds.lifecycle.Closer;
@ -55,16 +57,17 @@ public class AzureBlobContextModule extends AbstractModule {
} }
public static class AzureBlobContextImpl extends public static class AzureBlobContextImpl extends
BlobStoreContextImpl<AzureBlobStore, ContainerMetadata, BlobMetadata, Blob> implements BlobStoreContextImpl<AzureBlobConnection, ContainerMetadata, BlobMetadata, Blob>
AzureBlobContext { implements AzureBlobContext {
@Inject @Inject
AzureBlobContextImpl(Factory<BlobMetadata, Blob> blobMapFactory, AzureBlobContextImpl(Factory<BlobMetadata, Blob> blobMapFactory,
org.jclouds.blobstore.InputStreamMap.Factory<BlobMetadata> inputStreamMapFactory, org.jclouds.blobstore.InputStreamMap.Factory<BlobMetadata> inputStreamMapFactory,
Closer closer, Provider<Blob> blobProvider, AzureBlobStore defaultApi, Closer closer, Provider<Blob> blobProvider,
@AzureBlob URI endPoint, BlobStore<ContainerMetadata, BlobMetadata, Blob> blobStore,
AzureBlobConnection defaultApi, @AzureBlob URI endPoint,
@Named(AzureStorageConstants.PROPERTY_AZURESTORAGE_ACCOUNT) String account) { @Named(AzureStorageConstants.PROPERTY_AZURESTORAGE_ACCOUNT) String account) {
super(blobMapFactory, inputStreamMapFactory, closer, blobProvider, defaultApi, endPoint, super(blobMapFactory, inputStreamMapFactory, closer, blobProvider, blobStore, defaultApi,
account); endPoint, account);
} }
} }

View File

@ -29,11 +29,20 @@ import javax.inject.Named;
import javax.inject.Singleton; import javax.inject.Singleton;
import org.jclouds.azure.storage.AzureBlob; 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.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.blob.reference.AzureBlobConstants;
import org.jclouds.azure.storage.config.RestAzureStorageConnectionModule; import org.jclouds.azure.storage.config.RestAzureStorageConnectionModule;
import org.jclouds.blobstore.BlobStore;
import org.jclouds.cloud.ConfiguresCloudConnection; import org.jclouds.cloud.ConfiguresCloudConnection;
import org.jclouds.http.HttpRetryHandler;
import org.jclouds.http.RequiresHttp; import org.jclouds.http.RequiresHttp;
import org.jclouds.http.annotation.ClientError;
import org.jclouds.rest.RestClientFactory; import org.jclouds.rest.RestClientFactory;
import com.google.inject.Provides; import com.google.inject.Provides;
@ -62,8 +71,26 @@ public class RestAzureBlobStoreModule extends RestAzureStorageConnectionModule {
@Provides @Provides
@Singleton @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<ContainerMetadata, BlobMetadata, Blob> provideAzureBlobStore(
RestClientFactory factory) {
return factory.create(AzureBlobStore.class); return factory.create(AzureBlobStore.class);
} }
@Override
protected void bindRetryHandlers() {
bind(HttpRetryHandler.class).annotatedWith(ClientError.class).to(
AzureBlobClientErrorRetryHandler.class);
}
} }

View File

@ -28,6 +28,9 @@ import java.util.Arrays;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
/** /**
* *
* @author Adrian Cole * @author Adrian Cole
@ -37,21 +40,22 @@ public class ContainerMetadata extends org.jclouds.blobstore.domain.ContainerMet
private URI url; private URI url;
private DateTime lastModified; private DateTime lastModified;
private byte[] eTag; private byte[] eTag;
private Multimap<String, String> userMetadata = HashMultimap.create();
@Override @Override
public int hashCode() { public int hashCode() {
final int prime = 31; final int prime = 31;
int result = super.hashCode(); int result = super.hashCode();
result = prime * result + Arrays.hashCode(eTag); result = prime * result + Arrays.hashCode(eTag);
result = prime * result + ((lastModified == null) ? 0 : lastModified.hashCode()); result = prime * result + ((getLastModified() == null) ? 0 : getLastModified().hashCode());
result = prime * result + ((url == null) ? 0 : url.hashCode()); result = prime * result + ((getUrl() == null) ? 0 : getUrl().hashCode());
return result; return result;
} }
@Override @Override
public String toString() { public String toString() {
return "ContainerMetadata [eTag=" + Arrays.toString(eTag) + ", lastModified=" + lastModified return "ContainerMetadata [eTag=" + Arrays.toString(eTag) + ", lastModified="
+ ", url=" + url + ", name=" + name + "]"; + getLastModified() + ", url=" + getUrl() + ", name=" + name + "]";
} }
@Override @Override
@ -65,22 +69,22 @@ public class ContainerMetadata extends org.jclouds.blobstore.domain.ContainerMet
ContainerMetadata other = (ContainerMetadata) obj; ContainerMetadata other = (ContainerMetadata) obj;
if (!Arrays.equals(eTag, other.eTag)) if (!Arrays.equals(eTag, other.eTag))
return false; return false;
if (lastModified == null) { if (getLastModified() == null) {
if (other.lastModified != null) if (other.getLastModified() != null)
return false; return false;
} else if (!lastModified.equals(other.lastModified)) } else if (!getLastModified().equals(other.getLastModified()))
return false; return false;
if (url == null) { if (getUrl() == null) {
if (other.url != null) if (other.getUrl() != null)
return false; return false;
} else if (!url.equals(other.url)) } else if (!getUrl().equals(other.getUrl()))
return false; return false;
return true; return true;
} }
public ContainerMetadata(URI url, DateTime lastModified, byte[] eTag) { public ContainerMetadata(URI url, DateTime lastModified, byte[] eTag) {
super(url.getPath().substring(1)); super(url.getPath().substring(1));
this.url = url; this.setUrl(url);
this.lastModified = lastModified; this.lastModified = lastModified;
this.eTag = eTag; this.eTag = eTag;
} }
@ -105,4 +109,24 @@ public class ContainerMetadata extends org.jclouds.blobstore.domain.ContainerMet
return eTag; return eTag;
} }
public void setUserMetadata(Multimap<String, String> userMetadata) {
this.userMetadata = userMetadata;
}
public Multimap<String, String> 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;
}
} }

View File

@ -0,0 +1,55 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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);
}
}

View File

@ -0,0 +1,120 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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<HttpResponse, ContainerMetadata>, 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<String, String> 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;
}
}

View File

@ -0,0 +1,92 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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;
}
}

View File

@ -0,0 +1,62 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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 <a href="http://msdn.microsoft.com/en-us/library/dd135734.aspx#samplerequestandresponse" />
* @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;
}
}

View File

@ -61,10 +61,10 @@ import com.google.common.collect.Iterables;
* *
* @author Adrian Cole * @author Adrian Cole
*/ */
@Test(groups = "live", sequential = true, testName = "cloudservers.AzureBlobStoreLiveTest") @Test(groups = "live", sequential = true, testName = "azureblob.AzureBlobConnectionLiveTest")
public class AzureBlobStoreLiveTest { public class AzureBlobConnectionLiveTest {
protected AzureBlobStore connection; protected AzureBlobConnection connection;
private String containerPrefix = System.getProperty("user.name") + "-azureblob"; private String containerPrefix = System.getProperty("user.name") + "-azureblob";
@ -215,14 +215,14 @@ public class AzureBlobStoreLiveTest {
@Test @Test
public void testDeleteOneContainer() throws Exception { 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", @Test(timeOut = 5 * 60 * 1000, dependsOnMethods = { "testListOwnedContainers",
"testObjectOperations" }) "testObjectOperations" })
public void testDeleteContainer() throws Exception { public void testDeleteContainer() throws Exception {
assert connection.deleteContainer(privateContainer).get(10, TimeUnit.SECONDS); connection.deleteContainer(privateContainer).get(10, TimeUnit.SECONDS);
assert connection.deleteContainer(publicContainer).get(10, TimeUnit.SECONDS); connection.deleteContainer(publicContainer).get(10, TimeUnit.SECONDS);
// TODO loop for up to 30 seconds checking if they are really gone // 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 // Test HEAD of missing object
try { try {
connection.blobMetadata(privateContainer, "non-existent-object"); connection.getBlobProperties(privateContainer, "non-existent-object");
assert false; assert false;
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
// Test HEAD of object // Test HEAD of object
BlobMetadata metadata = connection.blobMetadata(privateContainer, object.getKey()); BlobMetadata metadata = connection.getBlobProperties(privateContainer, object.getKey());
// TODO assertEquals(metadata.getKey(), 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 // 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 // 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); // TimeUnit.SECONDS);
// assertEquals(IOUtils.toString((InputStream) getBlob.getData()), data.substring(8)); // assertEquals(IOUtils.toString((InputStream) getBlob.getData()), data.substring(8));
assertTrue(connection.removeBlob(privateContainer, "object").get(10, TimeUnit.SECONDS)); connection.deleteBlob(privateContainer, "object").get(10, TimeUnit.SECONDS);
assertTrue(connection.removeBlob(privateContainer, "chunked-object") connection.deleteBlob(privateContainer, "chunked-object").get(10, TimeUnit.SECONDS);
.get(10, TimeUnit.SECONDS));
} }
} }

View File

@ -0,0 +1,357 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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<AzureBlobConnection>>() {
}));
}
JaxrsAnnotationProcessor<AzureBlobConnection> processor;
}

View File

@ -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.RestAzureBlobStoreModule;
import org.jclouds.azure.storage.blob.config.StubAzureBlobStoreModule; import org.jclouds.azure.storage.blob.config.StubAzureBlobStoreModule;
import org.jclouds.azure.storage.blob.config.AzureBlobContextModule.AzureBlobContextImpl; 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.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 org.testng.annotations.Test;
import com.google.inject.Injector; import com.google.inject.Injector;
@ -60,22 +62,26 @@ public class AzureBlobContextBuilderTest {
} }
public void testBuildContext() { public void testBuildContext() {
CloudContext<AzureBlobStore> context = new AzureBlobContextBuilder("id", "secret") AzureBlobContext context = new AzureBlobContextBuilder("id", HttpUtils
.withModules(new StubAzureBlobStoreModule()).buildContext(); .toBase64String("secret".getBytes())).withModules(new StubAzureBlobStoreModule())
.buildContext();
assertEquals(context.getClass(), AzureBlobContextImpl.class); assertEquals(context.getClass(), AzureBlobContextImpl.class);
assertEquals(context.getApi().getClass(), StubAzureBlobConnection.class);
assertEquals(context.getBlobStore().getClass(), StubBlobStore.class);
assertEquals(context.getAccount(), "id"); assertEquals(context.getAccount(), "id");
assertEquals(context.getEndPoint(), URI.create("https://id.blob.core.windows.net")); assertEquals(context.getEndPoint(), URI.create("https://id.blob.core.windows.net"));
} }
public void testBuildInjector() { public void testBuildInjector() {
Injector i = new AzureBlobContextBuilder("id", "secret").withModules( Injector i = new AzureBlobContextBuilder("id", HttpUtils.toBase64String("secret".getBytes()))
new StubAzureBlobStoreModule()).buildInjector(); .withModules(new StubAzureBlobStoreModule()).buildInjector();
assert i.getInstance(AzureBlobContext.class) != null; assert i.getInstance(AzureBlobContext.class) != null;
} }
protected void testAddContextModule() { protected void testAddContextModule() {
List<Module> modules = new ArrayList<Module>(); List<Module> modules = new ArrayList<Module>();
AzureBlobContextBuilder builder = new AzureBlobContextBuilder("id", "secret"); AzureBlobContextBuilder builder = new AzureBlobContextBuilder("id", HttpUtils
.toBase64String("secret".getBytes()));
builder.addContextModule(modules); builder.addContextModule(modules);
assertEquals(modules.size(), 1); assertEquals(modules.size(), 1);
assertEquals(modules.get(0).getClass(), AzureBlobContextModule.class); assertEquals(modules.get(0).getClass(), AzureBlobContextModule.class);
@ -83,7 +89,8 @@ public class AzureBlobContextBuilderTest {
protected void addConnectionModule() { protected void addConnectionModule() {
List<Module> modules = new ArrayList<Module>(); List<Module> modules = new ArrayList<Module>();
AzureBlobContextBuilder builder = new AzureBlobContextBuilder("id", "secret"); AzureBlobContextBuilder builder = new AzureBlobContextBuilder("id", HttpUtils
.toBase64String("secret".getBytes()));
builder.addConnectionModule(modules); builder.addConnectionModule(modules);
assertEquals(modules.size(), 1); assertEquals(modules.size(), 1);
assertEquals(modules.get(0).getClass(), RestAzureBlobStoreModule.class); assertEquals(modules.get(0).getClass(), RestAzureBlobStoreModule.class);

View File

@ -0,0 +1,76 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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<ContainerMetadata>() {
}, new TypeLiteral<BlobMetadata>() {
}, new TypeLiteral<Blob>() {
}).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);
}
}

View File

@ -23,41 +23,43 @@
*/ */
package org.jclouds.azure.storage.blob; 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 static org.testng.Assert.assertEquals;
import java.io.IOException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.net.URI; import java.net.URI;
import java.util.Collections; import java.util.Collections;
import javax.inject.Singleton;
import javax.ws.rs.HttpMethod; import javax.ws.rs.HttpMethod;
import javax.ws.rs.core.HttpHeaders;
import org.jclouds.azure.storage.AzureBlob; 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.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.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.WithinThreadExecutorService;
import org.jclouds.concurrent.config.ExecutorServiceModule; import org.jclouds.concurrent.config.ExecutorServiceModule;
import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpUtils; import org.jclouds.http.HttpUtils;
import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule; import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule;
import org.jclouds.http.functions.ParseETagHeader;
import org.jclouds.http.functions.ParseSax; import org.jclouds.http.functions.ParseSax;
import org.jclouds.http.functions.ReturnTrueIf2xx; 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.JaxrsAnnotationProcessor;
import org.jclouds.rest.config.JaxrsModule; import org.jclouds.rest.config.JaxrsModule;
import org.jclouds.util.Jsr330; import org.jclouds.util.Jsr330;
import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import com.google.common.collect.ImmutableMultimap;
import com.google.inject.AbstractModule; import com.google.inject.AbstractModule;
import com.google.inject.Guice; import com.google.inject.Guice;
import com.google.inject.Injector; import com.google.inject.Injector;
import com.google.inject.Key; import com.google.inject.Key;
import com.google.inject.Provides;
import com.google.inject.TypeLiteral; import com.google.inject.TypeLiteral;
/** /**
@ -65,7 +67,7 @@ import com.google.inject.TypeLiteral;
* *
* @author Adrian Cole * @author Adrian Cole
*/ */
@Test(groups = "unit", testName = "cloudservers.AzureBlobStoreTest") @Test(groups = "unit", testName = "azureblob.AzureBlobStoreTest")
public class AzureBlobStoreTest { public class AzureBlobStoreTest {
public void testListContainers() throws SecurityException, NoSuchMethodException { public void testListContainers() throws SecurityException, NoSuchMethodException {
@ -85,27 +87,6 @@ public class AzureBlobStoreTest {
assertEquals(processor.createExceptionParserOrNullIfNotFound(method), null); 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 { public void testCreateContainer() throws SecurityException, NoSuchMethodException {
Method method = AzureBlobStore.class.getMethod("createContainer", String.class); Method method = AzureBlobStore.class.getMethod("createContainer", String.class);
@ -137,94 +118,10 @@ public class AzureBlobStoreTest {
assertEquals(httpMethod.getHeaders().get("x-ms-version"), Collections assertEquals(httpMethod.getHeaders().get("x-ms-version"), Collections
.singletonList("2009-07-17")); .singletonList("2009-07-17"));
assertEquals(processor.createResponseParser(method, httpMethod, null).getClass(), assertEquals(processor.createResponseParser(method, httpMethod, null).getClass(),
ReturnTrueIf2xx.class); ReturnVoidIf2xx.class);
// TODO check generic type of response parser // TODO check generic type of response parser
assertEquals(processor.createExceptionParserOrNullIfNotFound(method).getClass(), assertEquals(processor.createExceptionParserOrNullIfNotFound(method).getClass(),
ReturnTrueOn404.class); ReturnVoidOnNotFoundOr404.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);
} }
public void testListBlobs() throws SecurityException, NoSuchMethodException { public void testListBlobs() throws SecurityException, NoSuchMethodException {
@ -244,21 +141,29 @@ public class AzureBlobStoreTest {
assertEquals(processor.createExceptionParserOrNullIfNotFound(method), null); assertEquals(processor.createExceptionParserOrNullIfNotFound(method), null);
} }
public void testListRootBlobs() throws SecurityException, NoSuchMethodException { public void testPutBlob() throws SecurityException, NoSuchMethodException, IOException {
Method method = AzureBlobStore.class.getMethod("listBlobs"); 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().getHost(), "myaccount.blob.core.windows.net");
assertEquals(httpMethod.getEndpoint().getPath(), "/$root"); assertEquals(httpMethod.getEndpoint().getPath(), "/mycontainer/test");
assertEquals(httpMethod.getEndpoint().getQuery(), "restype=container&comp=list"); assertEquals(httpMethod.getEndpoint().getQuery(), null);
assertEquals(httpMethod.getMethod(), HttpMethod.GET); assertEquals(httpMethod.getMethod(), HttpMethod.PUT);
assertEquals(httpMethod.getHeaders().size(), 1); assertEquals(httpMethod.getHeaders().size(), 4);
assertEquals(httpMethod.getHeaders().get("x-ms-version"), Collections assertEquals(httpMethod.getHeaders().get("x-ms-version"), Collections
.singletonList("2009-07-17")); .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(), assertEquals(processor.createResponseParser(method, httpMethod, null).getClass(),
ParseSax.class); ParseETagHeader.class);
// TODO check generic type of response parser
assertEquals(processor.createExceptionParserOrNullIfNotFound(method), null);
} }
@BeforeClass @BeforeClass
@ -274,7 +179,22 @@ public class AzureBlobStoreTest {
bindConstant().annotatedWith( bindConstant().annotatedWith(
Jsr330.named(AzureStorageConstants.PROPERTY_AZURESTORAGE_KEY)).to( Jsr330.named(AzureStorageConstants.PROPERTY_AZURESTORAGE_KEY)).to(
HttpUtils.toBase64String("key".getBytes())); 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 JaxrsModule(), new ExecutorServiceModule(new WithinThreadExecutorService()),
new JavaUrlHttpCommandExecutorServiceModule()); new JavaUrlHttpCommandExecutorServiceModule());
processor = injector.getInstance(Key processor = injector.getInstance(Key

View File

@ -0,0 +1,94 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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);
}
}

View File

@ -28,9 +28,13 @@ import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import org.jclouds.azure.storage.AzureBlob; 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.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.cloud.ConfiguresCloudConnection;
import org.jclouds.http.functions.config.ParserModule; import org.jclouds.http.functions.config.ParserModule;
@ -51,7 +55,10 @@ public class StubAzureBlobStoreModule extends AbstractModule {
install(new ParserModule()); install(new ParserModule());
bind(new TypeLiteral<Map<String, Map<String, Blob>>>() { bind(new TypeLiteral<Map<String, Map<String, Blob>>>() {
}).toInstance(map); }).toInstance(map);
bind(AzureBlobStore.class).to(StubAzureBlobStore.class).asEagerSingleton(); bind(new TypeLiteral<BlobStore<ContainerMetadata, BlobMetadata, Blob>>() {
}).to(new TypeLiteral<StubBlobStore<ContainerMetadata, BlobMetadata, Blob>>() {
}).asEagerSingleton();
bind(AzureBlobConnection.class).to(StubAzureBlobConnection.class).asEagerSingleton();
bind(URI.class).annotatedWith(AzureBlob.class).toInstance( bind(URI.class).annotatedWith(AzureBlob.class).toInstance(
URI.create("https://id.blob.core.windows.net")); URI.create("https://id.blob.core.windows.net"));
} }

View File

@ -23,7 +23,7 @@
*/ */
package org.jclouds.azure.storage.blob.integration; 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.Blob;
import org.jclouds.azure.storage.blob.domain.BlobMetadata; import org.jclouds.azure.storage.blob.domain.BlobMetadata;
import org.jclouds.azure.storage.blob.domain.ContainerMetadata; import org.jclouds.azure.storage.blob.domain.ContainerMetadata;
@ -33,11 +33,8 @@ import org.testng.annotations.Test;
/** /**
* @author Adrian Cole * @author Adrian Cole
*/ */
@Test(groups = { "integration", "live" }, testName = "cloudfiles.AzureBlobContainerIntegrationTest") @Test(groups = { "integration", "live" }, testName = "azureblob.AzureBlobContainerIntegrationTest")
public class AzureBlobContainerIntegrationTest extends public class AzureBlobContainerIntegrationTest extends
BaseContainerIntegrationTest<AzureBlobStore, ContainerMetadata, BlobMetadata, Blob> { BaseContainerIntegrationTest<AzureBlobConnection, ContainerMetadata, BlobMetadata, Blob> {
@Test(groups = { "integration", "live" }, enabled = false)
public void deleteContainerIfEmptyButHasContents() throws Exception {
// azure recursively deletes containers in the background.
}
} }

View File

@ -23,7 +23,7 @@
*/ */
package org.jclouds.azure.storage.blob.integration; 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.Blob;
import org.jclouds.azure.storage.blob.domain.BlobMetadata; import org.jclouds.azure.storage.blob.domain.BlobMetadata;
import org.jclouds.azure.storage.blob.domain.ContainerMetadata; import org.jclouds.azure.storage.blob.domain.ContainerMetadata;
@ -33,8 +33,8 @@ import org.testng.annotations.Test;
/** /**
* @author Adrian Cole * @author Adrian Cole
*/ */
@Test(groups = { "live" }, testName = "cloudfiles.AzureBlobContainerLiveTest") @Test(groups = { "live" }, testName = "azureblob.AzureBlobContainerLiveTest")
public class AzureBlobContainerLiveTest extends public class AzureBlobContainerLiveTest extends
BaseContainerLiveTest<AzureBlobStore, ContainerMetadata, BlobMetadata, Blob> { BaseContainerLiveTest<AzureBlobConnection, ContainerMetadata, BlobMetadata, Blob> {
} }

View File

@ -23,10 +23,7 @@
*/ */
package org.jclouds.azure.storage.blob.integration; package org.jclouds.azure.storage.blob.integration;
import java.util.concurrent.ExecutionException; import org.jclouds.azure.storage.blob.AzureBlobConnection;
import java.util.concurrent.TimeoutException;
import org.jclouds.azure.storage.blob.AzureBlobStore;
import org.jclouds.azure.storage.blob.domain.Blob; import org.jclouds.azure.storage.blob.domain.Blob;
import org.jclouds.azure.storage.blob.domain.BlobMetadata; import org.jclouds.azure.storage.blob.domain.BlobMetadata;
import org.jclouds.azure.storage.blob.domain.ContainerMetadata; import org.jclouds.azure.storage.blob.domain.ContainerMetadata;
@ -36,31 +33,9 @@ import org.testng.annotations.Test;
/** /**
* @author Adrian Cole * @author Adrian Cole
*/ */
@Test(groups = { "integration", "live" }, testName = "cloudfiles.AzureBlobInputStreamMapIntegrationTest") @Test(groups = { "integration", "live" }, testName = "azureblob.AzureBlobInputStreamMapIntegrationTest")
public class AzureBlobInputStreamMapIntegrationTest extends public class AzureBlobInputStreamMapIntegrationTest
BaseInputStreamMapIntegrationTest<AzureBlobStore, ContainerMetadata, BlobMetadata, Blob> { extends
BaseInputStreamMapIntegrationTest<AzureBlobConnection, ContainerMetadata, BlobMetadata, Blob> {
@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
}
} }

View File

@ -30,7 +30,7 @@ import java.util.Collections;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException; 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.Blob;
import org.jclouds.azure.storage.blob.domain.BlobMetadata; import org.jclouds.azure.storage.blob.domain.BlobMetadata;
import org.jclouds.azure.storage.blob.domain.ContainerMetadata; import org.jclouds.azure.storage.blob.domain.ContainerMetadata;
@ -41,9 +41,9 @@ import org.testng.annotations.Test;
/** /**
* @author Adrian Cole * @author Adrian Cole
*/ */
@Test(groups = { "integration", "live" }, testName = "cloudfiles.AzureBlobIntegrationTest") @Test(groups = { "integration", "live" }, testName = "azureblob.AzureBlobIntegrationTest")
public class AzureBlobIntegrationTest extends public class AzureBlobIntegrationTest extends
BaseBlobIntegrationTest<AzureBlobStore, ContainerMetadata, BlobMetadata, Blob> { BaseBlobIntegrationTest<AzureBlobConnection, ContainerMetadata, BlobMetadata, Blob> {
@Override @Override
@Test(enabled = false) @Test(enabled = false)
@ -68,7 +68,7 @@ public class AzureBlobIntegrationTest extends
protected void validateMetadata(BlobMetadata metadata) { protected void validateMetadata(BlobMetadata metadata) {
assertEquals(metadata.getContentType(), "text/plain"); 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 // 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 // tradeoff, as a container list will contain the correct size of the objects in an
// inexpensive fashion // inexpensive fashion

View File

@ -23,7 +23,7 @@
*/ */
package org.jclouds.azure.storage.blob.integration; 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.Blob;
import org.jclouds.azure.storage.blob.domain.BlobMetadata; import org.jclouds.azure.storage.blob.domain.BlobMetadata;
import org.jclouds.azure.storage.blob.domain.ContainerMetadata; import org.jclouds.azure.storage.blob.domain.ContainerMetadata;
@ -33,8 +33,8 @@ import org.testng.annotations.Test;
/** /**
* @author Adrian Cole * @author Adrian Cole
*/ */
@Test(groups = { "live" }, testName = "cloudfiles.AzureBlobLiveTest") @Test(groups = { "live" }, testName = "azureblob.AzureBlobLiveTest")
public class AzureBlobLiveTest extends public class AzureBlobLiveTest extends
BaseBlobLiveTest<AzureBlobStore, ContainerMetadata, BlobMetadata, Blob> { BaseBlobLiveTest<AzureBlobConnection, ContainerMetadata, BlobMetadata, Blob> {
} }

View File

@ -23,10 +23,7 @@
*/ */
package org.jclouds.azure.storage.blob.integration; package org.jclouds.azure.storage.blob.integration;
import java.util.concurrent.ExecutionException; import org.jclouds.azure.storage.blob.AzureBlobConnection;
import java.util.concurrent.TimeoutException;
import org.jclouds.azure.storage.blob.AzureBlobStore;
import org.jclouds.azure.storage.blob.domain.Blob; import org.jclouds.azure.storage.blob.domain.Blob;
import org.jclouds.azure.storage.blob.domain.BlobMetadata; import org.jclouds.azure.storage.blob.domain.BlobMetadata;
import org.jclouds.azure.storage.blob.domain.ContainerMetadata; import org.jclouds.azure.storage.blob.domain.ContainerMetadata;
@ -36,12 +33,8 @@ import org.testng.annotations.Test;
/** /**
* @author Adrian Cole * @author Adrian Cole
*/ */
@Test(groups = { "integration", "live" }, testName = "cloudfiles.AzureBlobMapIntegrationTest") @Test(groups = { "integration", "live" }, testName = "azureblob.AzureBlobMapIntegrationTest")
public class AzureBlobMapIntegrationTest extends public class AzureBlobMapIntegrationTest extends
BaseBlobMapIntegrationTest<AzureBlobStore, ContainerMetadata, BlobMetadata, Blob> { BaseBlobMapIntegrationTest<AzureBlobConnection, ContainerMetadata, BlobMetadata, Blob> {
@Override
public void testContains() throws InterruptedException, ExecutionException, TimeoutException {
// http://code.google.com/p/jclouds/issues/detail?id=90
}
} }

View File

@ -23,7 +23,7 @@
*/ */
package org.jclouds.azure.storage.blob.integration; 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.Blob;
import org.jclouds.azure.storage.blob.domain.BlobMetadata; import org.jclouds.azure.storage.blob.domain.BlobMetadata;
import org.jclouds.azure.storage.blob.domain.ContainerMetadata; import org.jclouds.azure.storage.blob.domain.ContainerMetadata;
@ -33,8 +33,8 @@ import org.testng.annotations.Test;
/** /**
* @author Adrian Cole * @author Adrian Cole
*/ */
@Test(groups = { "integration", "live" }, testName = "cloudfiles.AzureBlobServiceIntegrationTest") @Test(groups = { "integration", "live" }, testName = "azureblob.AzureBlobServiceIntegrationTest")
public class AzureBlobServiceIntegrationTest extends public class AzureBlobServiceIntegrationTest extends
BaseServiceIntegrationTest<AzureBlobStore, ContainerMetadata, BlobMetadata, Blob> { BaseServiceIntegrationTest<AzureBlobConnection, ContainerMetadata, BlobMetadata, Blob> {
} }

View File

@ -23,9 +23,9 @@
*/ */
package org.jclouds.azure.storage.blob.integration; 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.AzureBlobContextBuilder;
import org.jclouds.azure.storage.blob.AzureBlobContextFactory; 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.config.StubAzureBlobStoreModule;
import org.jclouds.azure.storage.blob.domain.Blob; import org.jclouds.azure.storage.blob.domain.Blob;
import org.jclouds.azure.storage.blob.domain.BlobMetadata; import org.jclouds.azure.storage.blob.domain.BlobMetadata;
@ -41,17 +41,17 @@ import com.google.inject.Module;
* @author Adrian Cole * @author Adrian Cole
*/ */
public class AzureBlobTestInitializer extends public class AzureBlobTestInitializer extends
BaseTestInitializer<AzureBlobStore, ContainerMetadata, BlobMetadata, Blob> { BaseTestInitializer<AzureBlobConnection, ContainerMetadata, BlobMetadata, Blob> {
@Override @Override
protected BlobStoreContext<AzureBlobStore, ContainerMetadata, BlobMetadata, Blob> createLiveContext( protected BlobStoreContext<AzureBlobConnection, ContainerMetadata, BlobMetadata, Blob> createLiveContext(
Module configurationModule, String url, String app, String account, String key) { Module configurationModule, String url, String app, String account, String key) {
return new AzureBlobContextBuilder(account, key).withSaxDebug().relaxSSLHostname() return new AzureBlobContextBuilder(account, key).withSaxDebug().relaxSSLHostname()
.withModules(configurationModule, new Log4JLoggingModule()).buildContext(); .withModules(configurationModule, new Log4JLoggingModule()).buildContext();
} }
@Override @Override
protected BlobStoreContext<AzureBlobStore, ContainerMetadata, BlobMetadata, Blob> createStubContext() { protected BlobStoreContext<AzureBlobConnection, ContainerMetadata, BlobMetadata, Blob> createStubContext() {
return AzureBlobContextFactory.createContext("user", "pass", new StubAzureBlobStoreModule()); return AzureBlobContextFactory.createContext("user", "pass", new StubAzureBlobStoreModule());
} }
} }

View File

@ -32,6 +32,7 @@ import java.util.concurrent.Future;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Provider; import javax.inject.Provider;
import org.jclouds.azure.storage.blob.AzureBlobConnection;
import org.jclouds.azure.storage.blob.AzureBlobStore; import org.jclouds.azure.storage.blob.AzureBlobStore;
import org.jclouds.azure.storage.blob.domain.Blob; import org.jclouds.azure.storage.blob.domain.Blob;
import org.jclouds.azure.storage.blob.domain.BlobMetadata; 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.ListBlobsResponse;
import org.jclouds.azure.storage.blob.domain.TreeSetListBlobsResponse; import org.jclouds.azure.storage.blob.domain.TreeSetListBlobsResponse;
import org.jclouds.azure.storage.blob.options.CreateContainerOptions; 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.domain.BoundedSortedSet;
import org.jclouds.azure.storage.options.CreateOptions;
import org.jclouds.azure.storage.options.ListOptions; import org.jclouds.azure.storage.options.ListOptions;
import org.jclouds.blobstore.ContainerNotFoundException; import org.jclouds.blobstore.ContainerNotFoundException;
import org.jclouds.blobstore.integration.internal.StubBlobStore; import org.jclouds.blobstore.integration.internal.StubBlobStore;
import org.jclouds.http.options.GetOptions;
import org.jclouds.util.DateService; import org.jclouds.util.DateService;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
/** /**
@ -55,40 +58,16 @@ import com.google.common.collect.Sets;
* *
* @author Adrian Cole * @author Adrian Cole
*/ */
public class StubAzureBlobStore extends StubBlobStore<ContainerMetadata, BlobMetadata, Blob> public class StubAzureBlobConnection extends StubBlobStore<ContainerMetadata, BlobMetadata, Blob>
implements AzureBlobStore { implements AzureBlobConnection {
@Inject @Inject
protected StubAzureBlobStore(Map<String, Map<String, Blob>> containerToBlobs, protected StubAzureBlobConnection(Map<String, Map<String, Blob>> containerToBlobs,
DateService dateService, Provider<ContainerMetadata> containerMetaProvider, DateService dateService, Provider<ContainerMetadata> containerMetaProvider,
Provider<Blob> blobProvider) { Provider<Blob> blobProvider) {
super(containerToBlobs, dateService, containerMetaProvider, blobProvider); super(containerToBlobs, dateService, containerMetaProvider, blobProvider);
} }
public BoundedSortedSet<ContainerMetadata> listContainers(ListOptions options) {
return null;
}
public Future<Boolean> createContainer(String container, CreateContainerOptions options) {
return null;
}
public Future<Boolean> createRootContainer() {
return null;
}
public Future<Boolean> createRootContainer(CreateOptions options) {
return null;
}
public Future<Boolean> deleteRootContainer() {
return null;
}
public Future<ListBlobsResponse> listBlobs() {
return null;
}
public Future<ListBlobsResponse> listBlobs(final String name) { public Future<ListBlobsResponse> listBlobs(final String name) {
return new FutureBase<ListBlobsResponse>() { return new FutureBase<ListBlobsResponse>() {
public ListBlobsResponse get() throws InterruptedException, ExecutionException { public ListBlobsResponse get() throws InterruptedException, ExecutionException {
@ -109,4 +88,51 @@ public class StubAzureBlobStore extends StubBlobStore<ContainerMetadata, BlobMet
}; };
} }
public Future<Boolean> createContainer(String container, CreateContainerOptions... options) {
throw new UnsupportedOperationException();
}
public Future<Void> deleteBlob(String container, String key) {
throw new UnsupportedOperationException();
}
public Future<Boolean> deleteRootContainer() {
throw new UnsupportedOperationException();
}
public Future<Blob> getBlob(String container, String key, GetOptions... options) {
throw new UnsupportedOperationException();
}
public BlobMetadata getBlobProperties(String container, String key) {
throw new UnsupportedOperationException();
}
public Future<ListBlobsResponse> listBlobs(String container, ListBlobsOptions... options) {
throw new UnsupportedOperationException();
}
public Future<ListBlobsResponse> listBlobs(ListBlobsOptions... options) {
throw new UnsupportedOperationException();
}
public BoundedSortedSet<ContainerMetadata> listContainers(ListOptions... listOptions) {
throw new UnsupportedOperationException();
}
public Future<Boolean> createRootContainer(CreateContainerOptions... options) {
throw new UnsupportedOperationException();
}
public ContainerMetadata getContainerProperties(String container) {
throw new UnsupportedOperationException();
}
public void setContainerMetadata(String container, Multimap<String, String> metadata) {
throw new UnsupportedOperationException();
}
public void setBlobMetadata(String container, String key, Multimap<String, String> metadata) {
throw new UnsupportedOperationException();
}
} }

View File

@ -0,0 +1,133 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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<ListBlobsResponse> result = (BoundedSortedSet<ListBlobsResponse>) factory
.create(injector.getInstance(ContainerNameEnumerationResultsHandler.class))
.parse(is);
assertEquals(result, list);
}
}

View File

@ -84,9 +84,11 @@ public class AzureQueueConnectionLiveTest {
created = connection.createQueue(privateQueue, CreateOptions.Builder created = connection.createQueue(privateQueue, CreateOptions.Builder
.withMetadata(ImmutableMultimap.of("foo", "bar"))); .withMetadata(ImmutableMultimap.of("foo", "bar")));
} catch (UndeclaredThrowableException e) { } catch (UndeclaredThrowableException e) {
if (e.getCause().getCause() instanceof HttpResponseException) {
HttpResponseException htpe = (HttpResponseException) e.getCause().getCause(); HttpResponseException htpe = (HttpResponseException) e.getCause().getCause();
if (htpe.getResponse().getStatusCode() == 409) if (htpe.getResponse().getStatusCode() == 409)
continue; continue;
}
throw e; throw e;
} }
} }

View File

@ -42,13 +42,11 @@ public interface BlobStore<C extends ContainerMetadata, M extends BlobMetadata,
Future<Boolean> createContainer(String container); Future<Boolean> createContainer(String container);
/** /**
* if supported, this will delete a container recursively. Otherwise, it will return false, if * This will delete a container recursively.
* the container could not be deleted because it is not empty.
* *
* @param container * @param container
* @return false if container cannot be deleted because it is not empty
*/ */
Future<Boolean> deleteContainer(String container); Future<Void> deleteContainer(String container);
Future<? extends SortedSet<M>> listBlobs(String container); Future<? extends SortedSet<M>> listBlobs(String container);
@ -60,6 +58,6 @@ public interface BlobStore<C extends ContainerMetadata, M extends BlobMetadata,
M blobMetadata(String container, String key); M blobMetadata(String container, String key);
Future<Boolean> removeBlob(String container, String key); Future<Void> removeBlob(String container, String key);
} }

View File

@ -38,7 +38,7 @@ import com.google.inject.ImplementedBy;
* *
*/ */
@ImplementedBy(BlobStoreContextImpl.class) @ImplementedBy(BlobStoreContextImpl.class)
public interface BlobStoreContext<S extends BlobStore<C, M, B>, C extends ContainerMetadata, M extends BlobMetadata, B extends Blob<M>> public interface BlobStoreContext<S, C extends ContainerMetadata, M extends BlobMetadata, B extends Blob<M>>
extends CloudContext<S> { extends CloudContext<S> {
/** /**
@ -55,5 +55,7 @@ public interface BlobStoreContext<S extends BlobStore<C, M, B>, C extends Contai
*/ */
BlobMap<M, B> createBlobMap(String container); BlobMap<M, B> createBlobMap(String container);
BlobStore<C, M, B> getBlobStore();
B newBlob(String key); B newBlob(String key);
} }

View File

@ -23,6 +23,7 @@
*/ */
package org.jclouds.blobstore; package org.jclouds.blobstore;
import java.util.List;
import java.util.Properties; import java.util.Properties;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
@ -39,7 +40,7 @@ import com.google.inject.util.Types;
/** /**
* @author Adrian Cole * @author Adrian Cole
*/ */
public abstract class BlobStoreContextBuilder<S extends BlobStore<C, M, B>, C extends ContainerMetadata, M extends BlobMetadata, B extends Blob<M>> public abstract class BlobStoreContextBuilder<S, C extends ContainerMetadata, M extends BlobMetadata, B extends Blob<M>>
extends CloudContextBuilder<S> { extends CloudContextBuilder<S> {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
@ -141,8 +142,12 @@ public abstract class BlobStoreContextBuilder<S extends BlobStore<C, M, B>, C ex
this.containerMetadataType = containerMetadataType; this.containerMetadataType = containerMetadataType;
this.blobMetadataType = blobMetadataType; this.blobMetadataType = blobMetadataType;
this.blobType = blobType; this.blobType = blobType;
modules.add(BlobStoreMapsModule.Builder.newBuilder(connectionType, addBlobStoreModule(modules);
containerMetadataType, blobMetadataType, blobType).build()); }
protected void addBlobStoreModule(List<Module> modules) {
modules.add(BlobStoreMapsModule.Builder.newBuilder(containerMetadataType, blobMetadataType,
blobType).build());
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")

View File

@ -36,19 +36,22 @@ import org.jclouds.lifecycle.Closer;
/** /**
* @author Adrian Cole * @author Adrian Cole
*/ */
public class BlobStoreContextImpl<S extends BlobStore<C, M, B>, C extends ContainerMetadata, M extends BlobMetadata, B extends Blob<M>> public class BlobStoreContextImpl<S, C extends ContainerMetadata, M extends BlobMetadata, B extends Blob<M>>
extends CloudContextImpl<S> implements BlobStoreContext<S, C, M, B> { extends CloudContextImpl<S> implements BlobStoreContext<S, C, M, B> {
private final BlobMap.Factory<M, B> blobMapFactory; private final BlobMap.Factory<M, B> blobMapFactory;
private final InputStreamMap.Factory<M> inputStreamMapFactory; private final InputStreamMap.Factory<M> inputStreamMapFactory;
private final Provider<B> blobProvider; private final Provider<B> blobProvider;
private final BlobStore<C, M, B> blobStore;
public BlobStoreContextImpl(BlobMap.Factory<M, B> blobMapFactory, public BlobStoreContextImpl(BlobMap.Factory<M, B> blobMapFactory,
InputStreamMap.Factory<M> inputStreamMapFactory, Closer closer, InputStreamMap.Factory<M> inputStreamMapFactory, Closer closer,
Provider<B> blobProvider, S defaultApi, URI endPoint, String account) { Provider<B> blobProvider, BlobStore<C, M, B> blobStore, S defaultApi, URI endPoint,
String account) {
super(closer, defaultApi, endPoint, account); super(closer, defaultApi, endPoint, account);
this.blobMapFactory = blobMapFactory; this.blobMapFactory = blobMapFactory;
this.inputStreamMapFactory = inputStreamMapFactory; this.inputStreamMapFactory = inputStreamMapFactory;
this.blobProvider = blobProvider; this.blobProvider = blobProvider;
this.blobStore = blobStore;
} }
public BlobMap<M, B> createBlobMap(String container) { public BlobMap<M, B> createBlobMap(String container) {
@ -66,4 +69,8 @@ public class BlobStoreContextImpl<S extends BlobStore<C, M, B>, C extends Contai
blob.getMetadata().setKey(key); blob.getMetadata().setKey(key);
return blob; return blob;
} }
public BlobStore<C, M, B> getBlobStore() {
return blobStore;
}
} }

View File

@ -23,16 +23,26 @@
*/ */
package org.jclouds.blobstore; package org.jclouds.blobstore;
import java.util.Map;
import java.util.Map.Entry;
import org.jclouds.blobstore.domain.Blob; import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.domain.BlobMetadata; import org.jclouds.blobstore.domain.BlobMetadata;
import org.jclouds.blobstore.domain.ContainerMetadata; import org.jclouds.blobstore.domain.ContainerMetadata;
import org.jclouds.blobstore.internal.BlobMapImpl; import org.jclouds.blobstore.internal.BlobMapImpl;
import org.jclouds.blobstore.internal.InputStreamMapImpl; 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.GetAllBlobMetadataStrategy;
import org.jclouds.blobstore.strategy.GetAllBlobsStrategy; import org.jclouds.blobstore.strategy.GetAllBlobsStrategy;
import org.jclouds.blobstore.strategy.internal.ContainerListGetAllBlobMetadataStrategy; 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.jclouds.blobstore.strategy.internal.RetryOnNotFoundGetAllBlobsStrategy;
import com.google.common.collect.Maps;
import com.google.inject.AbstractModule; import com.google.inject.AbstractModule;
import com.google.inject.Scopes; import com.google.inject.Scopes;
import com.google.inject.TypeLiteral; import com.google.inject.TypeLiteral;
@ -47,17 +57,12 @@ public class BlobStoreMapsModule extends AbstractModule {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private BlobStoreMapsModule(TypeLiteral blobMapFactoryType, TypeLiteral blobMapImplType, private BlobStoreMapsModule(TypeLiteral blobMapFactoryType, TypeLiteral blobMapImplType,
TypeLiteral inputStreamMapFactoryType, TypeLiteral inputStreamMapImplType, TypeLiteral inputStreamMapFactoryType, TypeLiteral inputStreamMapImplType,
TypeLiteral getAllBlobsStrategyType, TypeLiteral getAllBlobsStrategyImplType, Map<TypeLiteral, TypeLiteral> strategyImplMap) {
TypeLiteral getAllBlobMetadataStrategyType,
TypeLiteral getAllBlobMetadataStrategyImplType) {
this.blobMapFactoryType = blobMapFactoryType; this.blobMapFactoryType = blobMapFactoryType;
this.blobMapImplType = blobMapImplType; this.blobMapImplType = blobMapImplType;
this.inputStreamMapFactoryType = inputStreamMapFactoryType; this.inputStreamMapFactoryType = inputStreamMapFactoryType;
this.inputStreamMapImplType = inputStreamMapImplType; this.inputStreamMapImplType = inputStreamMapImplType;
this.getAllBlobsStrategyType = getAllBlobsStrategyType; this.strategyImplMap = strategyImplMap;
this.getAllBlobsStrategyImplType = getAllBlobsStrategyImplType;
this.getAllBlobMetadataStrategyType = getAllBlobMetadataStrategyType;
this.getAllBlobMetadataStrategyImplType = getAllBlobMetadataStrategyImplType;
} }
// code is unchecked here as we are getting types at runtime. Due to type erasure, we cannot pass // 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") @SuppressWarnings("unchecked")
protected final TypeLiteral inputStreamMapImplType; protected final TypeLiteral inputStreamMapImplType;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected final TypeLiteral getAllBlobsStrategyType; protected final Map<TypeLiteral, TypeLiteral> strategyImplMap;
@SuppressWarnings("unchecked")
protected final TypeLiteral getAllBlobsStrategyImplType;
@SuppressWarnings("unchecked")
protected final TypeLiteral getAllBlobMetadataStrategyType;
@SuppressWarnings("unchecked")
protected final TypeLiteral getAllBlobMetadataStrategyImplType;
public static class Builder<S extends BlobStore<C, M, B>, C extends ContainerMetadata, M extends BlobMetadata, B extends Blob<M>> { public static class Builder<S, C extends ContainerMetadata, M extends BlobMetadata, B extends Blob<M>> {
@SuppressWarnings("unused")
private final TypeLiteral<S> connectionType;
private final TypeLiteral<C> containerMetadataType; private final TypeLiteral<C> containerMetadataType;
private final TypeLiteral<M> blobMetadataType; private final TypeLiteral<M> blobMetadataType;
private final TypeLiteral<B> blobType; private final TypeLiteral<B> blobType;
@ -101,23 +98,33 @@ public class BlobStoreMapsModule extends AbstractModule {
private TypeLiteral getAllBlobMetadataStrategyType; private TypeLiteral getAllBlobMetadataStrategyType;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private TypeLiteral getAllBlobMetadataStrategyImplType; 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<S> connectionType, TypeLiteral<C> containerMetadataType, private Builder(TypeLiteral<C> containerMetadataType, TypeLiteral<M> blobMetadataType,
TypeLiteral<M> blobMetadataType, TypeLiteral<B> blobType) { TypeLiteral<B> blobType) {
this.connectionType = connectionType;
this.containerMetadataType = containerMetadataType; this.containerMetadataType = containerMetadataType;
this.blobMetadataType = blobMetadataType; this.blobMetadataType = blobMetadataType;
this.blobType = blobType; this.blobType = blobType;
blobMapFactoryType = TypeLiteral.get(Types.newParameterizedTypeWithOwner(BlobMap.class, blobMapFactoryType = TypeLiteral.get(Types.newParameterizedTypeWithOwner(BlobMap.class,
BlobMap.Factory.class, blobMetadataType.getType(), blobType.getType())); BlobMap.Factory.class, blobMetadataType.getType(), blobType.getType()));
blobMapImplType = TypeLiteral.get(Types.newParameterizedType(BlobMapImpl.class, blobMapImplType = TypeLiteral.get(Types.newParameterizedType(BlobMapImpl.class,
connectionType.getType(), containerMetadataType.getType(), blobMetadataType containerMetadataType.getType(), blobMetadataType.getType(), blobType.getType()));
.getType(), blobType.getType()));
inputStreamMapFactoryType = TypeLiteral.get(Types.newParameterizedTypeWithOwner( inputStreamMapFactoryType = TypeLiteral.get(Types.newParameterizedTypeWithOwner(
InputStreamMap.class, InputStreamMap.Factory.class, blobMetadataType.getType())); InputStreamMap.class, InputStreamMap.Factory.class, blobMetadataType.getType()));
inputStreamMapImplType = TypeLiteral.get(Types.newParameterizedType( inputStreamMapImplType = TypeLiteral.get(Types.newParameterizedType(
InputStreamMapImpl.class, connectionType.getType(), containerMetadataType InputStreamMapImpl.class, containerMetadataType.getType(), blobMetadataType
.getType(), blobMetadataType.getType(), blobType.getType())); .getType(), blobType.getType()));
getAllBlobsStrategyType = TypeLiteral.get(Types.newParameterizedType( getAllBlobsStrategyType = TypeLiteral.get(Types.newParameterizedType(
GetAllBlobsStrategy.class, containerMetadataType.getType(), blobMetadataType GetAllBlobsStrategy.class, containerMetadataType.getType(), blobMetadataType
.getType(), blobType.getType())); .getType(), blobType.getType()));
@ -126,35 +133,95 @@ public class BlobStoreMapsModule extends AbstractModule {
getAllBlobMetadataStrategyType = TypeLiteral.get(Types.newParameterizedType( getAllBlobMetadataStrategyType = TypeLiteral.get(Types.newParameterizedType(
GetAllBlobMetadataStrategy.class, containerMetadataType.getType(), GetAllBlobMetadataStrategy.class, containerMetadataType.getType(),
blobMetadataType.getType(), blobType.getType())); blobMetadataType.getType(), blobType.getType()));
getAllBlobMetadataStrategyImplType = TypeLiteral.get(Types.newParameterizedType( setGetAllBlobMetadataStrategyImpl(ContainerListGetAllBlobMetadataStrategy.class);
ContainerListGetAllBlobMetadataStrategy.class, containerMetadataType.getType(),
blobMetadataType.getType(), blobType.getType())); 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<S, C, M, B> withGetAllBlobsStrategy(Class<?> getAllBlobsStrategyImplClass) { public Builder<S, C, M, B> withGetAllBlobsStrategy(Class<?> getAllBlobsStrategyImplClass) {
setGetAllBlobsStrategyImpl(getAllBlobsStrategyImplClass); setGetAllBlobsStrategyImpl(getAllBlobsStrategyImplClass);
return this; return this;
} }
public Builder<S, C, M, B> withGetAllBlobMetadataStrategy(
Class<?> getAllBlobMetadataStrategyImplClass) {
setGetAllBlobMetadataStrategyImpl(getAllBlobMetadataStrategyImplClass);
return this;
}
public Builder<S, C, M, B> withContainsValueStrategy(Class<?> containsValueStrategyImplClass) {
setContainsValueStrategyImpl(containsValueStrategyImplClass);
return this;
}
public Builder<S, C, M, B> withClearContainerStrategy(Class<?> clearContainerStrategyImplClass) {
setClearContainerStrategyImpl(clearContainerStrategyImplClass);
return this;
}
public Builder<S, C, M, B> withContainerCountStrategy(Class<?> containerCountStrategyImplClass) {
setContainerCountStrategyImpl(containerCountStrategyImplClass);
return this;
}
private void setGetAllBlobsStrategyImpl(Class<?> getAllBlobsStrategyImplClass) { private void setGetAllBlobsStrategyImpl(Class<?> getAllBlobsStrategyImplClass) {
getAllBlobsStrategyImplType = TypeLiteral.get(Types.newParameterizedType( getAllBlobsStrategyImplType = TypeLiteral.get(Types.newParameterizedType(
getAllBlobsStrategyImplClass, containerMetadataType.getType(), blobMetadataType getAllBlobsStrategyImplClass, containerMetadataType.getType(), blobMetadataType
.getType(), blobType.getType())); .getType(), blobType.getType()));
} }
public static <S extends BlobStore<C, M, B>, C extends ContainerMetadata, M extends BlobMetadata, B extends Blob<M>> Builder<S, C, M, B> newBuilder( private void setGetAllBlobMetadataStrategyImpl(Class<?> getAllBlobMetadataStrategyImplClass) {
TypeLiteral<S> connectionType, TypeLiteral<C> containerMetadataType, getAllBlobMetadataStrategyImplType = TypeLiteral.get(Types.newParameterizedType(
TypeLiteral<M> blobMetadataType, TypeLiteral<B> blobType) { getAllBlobMetadataStrategyImplClass, containerMetadataType.getType(),
return new Builder<S, C, M, B>(connectionType, containerMetadataType, blobMetadataType, blobMetadataType.getType(), blobType.getType()));
blobType);
} }
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 <S, C extends ContainerMetadata, M extends BlobMetadata, B extends Blob<M>> Builder<S, C, M, B> newBuilder(
TypeLiteral<C> containerMetadataType, TypeLiteral<M> blobMetadataType,
TypeLiteral<B> blobType) {
return new Builder<S, C, M, B>(containerMetadataType, blobMetadataType, blobType);
}
@SuppressWarnings("unchecked")
public BlobStoreMapsModule build() {
Map<TypeLiteral, TypeLiteral> 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, return new BlobStoreMapsModule(blobMapFactoryType, blobMapImplType,
inputStreamMapFactoryType, inputStreamMapImplType, getAllBlobsStrategyType, inputStreamMapFactoryType, inputStreamMapImplType, strategyImplMap);
getAllBlobsStrategyImplType, getAllBlobMetadataStrategyType,
getAllBlobMetadataStrategyImplType);
} }
} }
@ -167,9 +234,9 @@ public class BlobStoreMapsModule extends AbstractModule {
bind(inputStreamMapFactoryType).toProvider( bind(inputStreamMapFactoryType).toProvider(
FactoryProvider.newFactory(inputStreamMapFactoryType, inputStreamMapImplType)).in( FactoryProvider.newFactory(inputStreamMapFactoryType, inputStreamMapImplType)).in(
Scopes.SINGLETON); Scopes.SINGLETON);
bind(getAllBlobsStrategyType).to(getAllBlobsStrategyImplType).in(Scopes.SINGLETON); for (Entry<TypeLiteral, TypeLiteral> entry : strategyImplMap.entrySet()) {
bind(getAllBlobMetadataStrategyType).to(getAllBlobMetadataStrategyImplType).in( bind(entry.getKey()).to(entry.getValue()).in(Scopes.SINGLETON);
Scopes.SINGLETON); }
} }
} }

View File

@ -23,7 +23,7 @@
*/ */
package org.jclouds.blobstore.binders; 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 static org.jclouds.blobstore.reference.BlobStoreConstants.PROPERTY_USER_METADATA_PREFIX;
import javax.inject.Inject; import javax.inject.Inject;
@ -45,6 +45,7 @@ public class BlobBinder implements EntityBinder {
public void addEntityToRequest(Object entity, HttpRequest request) { public void addEntityToRequest(Object entity, HttpRequest request) {
Blob<?> object = (Blob<?>) entity; Blob<?> object = (Blob<?>) entity;
for (String key : object.getMetadata().getUserMetadata().keySet()) { for (String key : object.getMetadata().getUserMetadata().keySet()) {
request.getHeaders().putAll(key.startsWith(metadataPrefix) ? key : metadataPrefix + key, request.getHeaders().putAll(key.startsWith(metadataPrefix) ? key : metadataPrefix + key,
object.getMetadata().getUserMetadata().get(key)); object.getMetadata().getUserMetadata().get(key));

View File

@ -0,0 +1,111 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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<C extends ContainerMetadata, M extends BlobMetadata, B extends Blob<M>>
implements Function<Exception, Void>, RestContext {
static final Void v;
static {
Constructor<Void> 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<C, M, B> clear;
private final BlobStore<C, M, B> connection;
@Inject
protected
ClearAndDeleteIfNotEmpty(ClearContainerStrategy<C, M, B> clear, BlobStore<C, M, B> 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.<BlobRuntimeException> 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;
}
}

View File

@ -0,0 +1,64 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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<M extends BlobMetadata, B extends Blob<M>> implements
Function<Object, byte[]> {
protected final Provider<B> blobFactory;
@Inject
ObjectMD5(Provider<B> 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();
}
}

View File

@ -23,20 +23,35 @@
*/ */
package org.jclouds.blobstore.functions; package org.jclouds.blobstore.functions;
import java.lang.reflect.Constructor;
import org.jclouds.blobstore.ContainerNotFoundException; import org.jclouds.blobstore.ContainerNotFoundException;
import org.jclouds.blobstore.KeyNotFoundException; import org.jclouds.blobstore.KeyNotFoundException;
import org.jclouds.http.functions.ReturnTrueOn404; import org.jclouds.http.functions.ReturnTrueOn404;
import com.google.common.base.Function; import com.google.common.base.Function;
public class ReturnTrueOnNotFoundOr404 implements Function<Exception, Boolean> { public class ReturnVoidOnNotFoundOr404 implements Function<Exception, Void> {
static final Void v;
static {
Constructor<Void> 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(); ReturnTrueOn404 rto404 = new ReturnTrueOn404();
public Boolean apply(Exception from) { public Void apply(Exception from) {
if (from instanceof KeyNotFoundException||from instanceof ContainerNotFoundException) { if (from instanceof KeyNotFoundException || from instanceof ContainerNotFoundException) {
return true; return v;
} else { } else {
return rto404.apply(from); return rto404.apply(from) ? v : null;
} }
} }

View File

@ -26,15 +26,8 @@ package org.jclouds.blobstore.internal;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull; 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.Set;
import java.util.SortedSet; 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.Inject;
import javax.inject.Named; 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.BlobMetadata;
import org.jclouds.blobstore.domain.ContainerMetadata; import org.jclouds.blobstore.domain.ContainerMetadata;
import org.jclouds.blobstore.reference.BlobStoreConstants; 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.GetAllBlobMetadataStrategy;
import org.jclouds.blobstore.strategy.GetAllBlobsStrategy; import org.jclouds.blobstore.strategy.GetAllBlobsStrategy;
import org.jclouds.rest.BoundedSortedSet; import org.jclouds.rest.BoundedSortedSet;
@ -71,6 +67,9 @@ public abstract class BaseBlobMap<C extends ContainerMetadata, M extends BlobMet
protected final Provider<B> blobFactory; protected final Provider<B> blobFactory;
protected final GetAllBlobsStrategy<C, M, B> getAllBlobs; protected final GetAllBlobsStrategy<C, M, B> getAllBlobs;
protected final GetAllBlobMetadataStrategy<C, M, B> getAllBlobMetadata; protected final GetAllBlobMetadataStrategy<C, M, B> getAllBlobMetadata;
protected final ContainsValueStrategy<C, M, B> containsValueStrategy;
protected final ClearContainerStrategy<C, M, B> clearContainerStrategy;
protected final ContainerCountStrategy<C, M, B> containerCountStrategy;
/** /**
* maximum duration of an blob Request * maximum duration of an blob Request
@ -89,12 +88,18 @@ public abstract class BaseBlobMap<C extends ContainerMetadata, M extends BlobMet
@Inject @Inject
public BaseBlobMap(BlobStore<C, M, B> connection, Provider<B> blobFactory, public BaseBlobMap(BlobStore<C, M, B> connection, Provider<B> blobFactory,
GetAllBlobsStrategy<C, M, B> getAllBlobs, GetAllBlobsStrategy<C, M, B> getAllBlobs,
GetAllBlobMetadataStrategy<C, M, B> getAllBlobMetadata, @Assisted String containerName) { GetAllBlobMetadataStrategy<C, M, B> getAllBlobMetadata,
ContainsValueStrategy<C, M, B> containsValueStrategy,
ClearContainerStrategy<C, M, B> clearContainerStrategy,
ContainerCountStrategy<C, M, B> containerCountStrategy, @Assisted String containerName) {
this.connection = checkNotNull(connection, "connection"); this.connection = checkNotNull(connection, "connection");
this.containerName = checkNotNull(containerName, "container"); this.containerName = checkNotNull(containerName, "container");
this.blobFactory = checkNotNull(blobFactory, "blobFactory"); this.blobFactory = checkNotNull(blobFactory, "blobFactory");
this.getAllBlobs = checkNotNull(getAllBlobs, "getAllBlobs"); this.getAllBlobs = checkNotNull(getAllBlobs, "getAllBlobs");
this.getAllBlobMetadata = checkNotNull(getAllBlobMetadata, "getAllBlobMetadata"); 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!"); checkArgument(!containerName.equals(""), "container name must not be a blank string!");
} }
@ -106,30 +111,7 @@ public abstract class BaseBlobMap<C extends ContainerMetadata, M extends BlobMet
* @see BoundedSortedSet#getContents() * @see BoundedSortedSet#getContents()
*/ */
public int size() { public int size() {
return getAllBlobMetadata.execute(connection, containerName).size(); return (int) containerCountStrategy.execute(connection, containerName);
}
protected boolean containsETag(byte[] eTag) throws InterruptedException, ExecutionException,
TimeoutException {
for (BlobMetadata metadata : getAllBlobMetadata.execute(connection, containerName)) {
if (Arrays.equals(eTag, metadata.getETag()))
return true;
}
return false;
}
protected byte[] getMD5(Object value) throws IOException, FileNotFoundException,
InterruptedException, ExecutionException, TimeoutException {
Blob<?> 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();
} }
/** /**
@ -138,9 +120,7 @@ public abstract class BaseBlobMap<C extends ContainerMetadata, M extends BlobMet
* @see BlobStore#getBlob(String, String) * @see BlobStore#getBlob(String, String)
*/ */
protected Set<B> getAllBlobs() { protected Set<B> getAllBlobs() {
return getAllBlobs.execute(connection, containerName); return getAllBlobs.execute(connection, containerName);
} }
/** /**
@ -150,49 +130,11 @@ public abstract class BaseBlobMap<C extends ContainerMetadata, M extends BlobMet
* method. To reuse data from InputStreams, pass {@link java.io.InputStream}s inside {@link Blob}s * method. To reuse data from InputStreams, pass {@link java.io.InputStream}s inside {@link Blob}s
*/ */
public boolean containsValue(Object value) { public boolean containsValue(Object value) {
return eTagExistsMatchingMD5Of(value); return containsValueStrategy.execute(connection, containerName, value);
}
private boolean eTagExistsMatchingMD5Of(Object value) {
try {
byte[] eTag = getMD5(value);
return containsETag(eTag);
} catch (Exception e) {
Utils.<BlobRuntimeException> 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);
}
} }
public void clear() { public void clear() {
Set<Future<Boolean>> deletes = Sets.newHashSet(); clearContainerStrategy.execute(connection, containerName);
for (M md : getAllBlobMetadata.execute(connection, containerName)) {
deletes.add(connection.removeBlob(containerName, md.getKey()));
}
for (Future<Boolean> isdeleted : deletes) {
try {
if (!isdeleted.get(requestTimeoutMilliseconds, TimeUnit.MILLISECONDS)) {
throw new BlobRuntimeException("Failed to delete blob in container: "
+ containerName);
}
} catch (Exception e) {
Utils.<BlobRuntimeException> rethrowIfRuntimeOrSameType(e);
throw new BlobRuntimeException("Error deleting blob in container: " + containerName, e);
}
}
} }
public Set<String> keySet() { public Set<String> keySet() {

View File

@ -39,6 +39,9 @@ import org.jclouds.blobstore.KeyNotFoundException;
import org.jclouds.blobstore.domain.Blob; import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.domain.BlobMetadata; import org.jclouds.blobstore.domain.BlobMetadata;
import org.jclouds.blobstore.domain.ContainerMetadata; 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.GetAllBlobMetadataStrategy;
import org.jclouds.blobstore.strategy.GetAllBlobsStrategy; import org.jclouds.blobstore.strategy.GetAllBlobsStrategy;
import org.jclouds.util.Utils; import org.jclouds.util.Utils;
@ -54,14 +57,18 @@ import com.google.inject.assistedinject.Assisted;
* *
* @author Adrian Cole * @author Adrian Cole
*/ */
public class BlobMapImpl<S extends BlobStore<C, M, B>, C extends ContainerMetadata, M extends BlobMetadata, B extends Blob<M>> public class BlobMapImpl<C extends ContainerMetadata, M extends BlobMetadata, B extends Blob<M>>
extends BaseBlobMap<C, M, B, B> implements BlobMap<M, B> { extends BaseBlobMap<C, M, B, B> implements BlobMap<M, B> {
@Inject @Inject
public BlobMapImpl(S connection, Provider<B> blobFactory, public BlobMapImpl(BlobStore<C, M, B> connection, Provider<B> blobFactory,
GetAllBlobsStrategy<C, M, B> getAllBlobs, GetAllBlobsStrategy<C, M, B> getAllBlobs,
GetAllBlobMetadataStrategy<C, M, B> getAllBlobMetadata, @Assisted String containerName) { GetAllBlobMetadataStrategy<C, M, B> getAllBlobMetadata,
super(connection, blobFactory, getAllBlobs, getAllBlobMetadata, containerName); ContainsValueStrategy<C, M, B> containsValueStrategy,
ClearContainerStrategy<C, M, B> clearContainerStrategy,
ContainerCountStrategy<C, M, B> containerCountStrategy, @Assisted String containerName) {
super(connection, blobFactory, getAllBlobs, getAllBlobMetadata, containsValueStrategy,
clearContainerStrategy, containerCountStrategy, containerName);
} }
/** /**

View File

@ -0,0 +1,36 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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);
}
}

View File

@ -42,6 +42,9 @@ import org.jclouds.blobstore.KeyNotFoundException;
import org.jclouds.blobstore.domain.Blob; import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.domain.BlobMetadata; import org.jclouds.blobstore.domain.BlobMetadata;
import org.jclouds.blobstore.domain.ContainerMetadata; 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.GetAllBlobMetadataStrategy;
import org.jclouds.blobstore.strategy.GetAllBlobsStrategy; import org.jclouds.blobstore.strategy.GetAllBlobsStrategy;
import org.jclouds.util.Utils; import org.jclouds.util.Utils;
@ -59,14 +62,18 @@ import com.google.inject.assistedinject.Assisted;
* @see InputStreamMap * @see InputStreamMap
* @see BaseBlobMap * @see BaseBlobMap
*/ */
public class InputStreamMapImpl<S extends BlobStore<C, M, B>, C extends ContainerMetadata, M extends BlobMetadata, B extends Blob<M>> public class InputStreamMapImpl<C extends ContainerMetadata, M extends BlobMetadata, B extends Blob<M>>
extends BaseBlobMap<C, M, B, InputStream> implements InputStreamMap<M> { extends BaseBlobMap<C, M, B, InputStream> implements InputStreamMap<M> {
@Inject @Inject
public InputStreamMapImpl(S connection, Provider<B> blobFactory, public InputStreamMapImpl(BlobStore<C, M, B> connection, Provider<B> blobFactory,
GetAllBlobsStrategy<C, M, B> getAllBlobs, GetAllBlobsStrategy<C, M, B> getAllBlobs,
GetAllBlobMetadataStrategy<C, M, B> getAllBlobMetadata, @Assisted String containerName) { GetAllBlobMetadataStrategy<C, M, B> getAllBlobMetadata,
super(connection, blobFactory, getAllBlobs, getAllBlobMetadata, containerName); ContainsValueStrategy<C, M, B> containsValueStrategy,
ClearContainerStrategy<C, M, B> clearContainerStrategy,
ContainerCountStrategy<C, M, B> containerCountStrategy, @Assisted String containerName) {
super(connection, blobFactory, getAllBlobs, getAllBlobMetadata, containsValueStrategy,
clearContainerStrategy, containerCountStrategy, containerName);
} }
/** /**

View File

@ -0,0 +1,40 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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<C extends ContainerMetadata, M extends BlobMetadata, B extends Blob<M>> {
void execute(BlobStore<C, M, B> connection, String containerName);
}

View File

@ -0,0 +1,40 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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<C extends ContainerMetadata, M extends BlobMetadata, B extends Blob<M>> {
long execute(BlobStore<C, M, B> connection, String containerName);
}

View File

@ -0,0 +1,40 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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<C extends ContainerMetadata, M extends BlobMetadata, B extends Blob<M>> {
boolean execute(BlobStore<C, M, B> connection, String containerName, Object value);
}

View File

@ -33,7 +33,7 @@ import org.jclouds.blobstore.BlobStore;
import org.jclouds.blobstore.domain.Blob; import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.domain.BlobMetadata; import org.jclouds.blobstore.domain.BlobMetadata;
import org.jclouds.blobstore.domain.ContainerMetadata; 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.reference.BlobStoreConstants;
import org.jclouds.blobstore.strategy.GetAllBlobMetadataStrategy; import org.jclouds.blobstore.strategy.GetAllBlobMetadataStrategy;
import org.jclouds.util.Utils; import org.jclouds.util.Utils;

View File

@ -0,0 +1,74 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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<C extends ContainerMetadata, M extends BlobMetadata, B extends Blob<M>>
implements ContainsValueStrategy<C, M, B> {
protected final ObjectMD5<M, B> objectMD5;
protected final GetAllBlobMetadataStrategy<C, M, B> getAllBlobMetadata;
@Inject
private ContentMD5ContainsValueStrategy(ObjectMD5<M, B> objectMD5,
GetAllBlobMetadataStrategy<C, M, B> getAllBlobMetadata) {
this.objectMD5 = objectMD5;
this.getAllBlobMetadata = getAllBlobMetadata;
}
public boolean execute(BlobStore<C, M, B> 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.<BlobRuntimeException> rethrowIfRuntimeOrSameType(e);
throw new BlobRuntimeException(String.format(
"Error searching for ETAG of value: [%2$s] in container:%1$s", containerName,
value), e);
}
}
}

View File

@ -0,0 +1,80 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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<C extends ContainerMetadata, M extends BlobMetadata, B extends Blob<M>>
implements ClearContainerStrategy<C, M, B> {
/**
* maximum duration of an blob Request
*/
@Inject(optional = true)
@Named(BlobStoreConstants.PROPERTY_BLOBSTORE_TIMEOUT)
protected long requestTimeoutMilliseconds = 30000;
protected final GetAllBlobMetadataStrategy<C, M, B> getAllBlobMetadata;
@Inject
DeleteAllKeysClearContainerStrategy(GetAllBlobMetadataStrategy<C, M, B> getAllBlobMetadata) {
this.getAllBlobMetadata = getAllBlobMetadata;
}
public void execute(BlobStore<C, M, B> connection, final String containerName) {
Set<Future<Void>> deletes = Sets.newHashSet();
for (M md : getAllBlobMetadata.execute(connection, containerName)) {
deletes.add(connection.removeBlob(containerName, md.getKey()));
}
for (Future<Void> isdeleted : deletes) {
try {
isdeleted.get(requestTimeoutMilliseconds, TimeUnit.MILLISECONDS);
} catch (Exception e) {
Utils.<BlobRuntimeException> rethrowIfRuntimeOrSameType(e);
throw new BlobRuntimeException("Error deleting blob in container: " + containerName, e);
}
}
}
}

View File

@ -0,0 +1,53 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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<C extends ContainerMetadata, M extends BlobMetadata, B extends Blob<M>>
implements ContainerCountStrategy<C, M, B> {
protected final GetAllBlobMetadataStrategy<C, M, B> getAllBlobMetadata;
@Inject
KeyCountStrategy(GetAllBlobMetadataStrategy<C, M, B> getAllBlobMetadata) {
this.getAllBlobMetadata = getAllBlobMetadata;
}
public long execute(BlobStore<C, M, B> connection, String container) {
return getAllBlobMetadata.execute(connection, container).size();
}
}

View File

@ -40,7 +40,7 @@ import org.jclouds.blobstore.KeyNotFoundException;
import org.jclouds.blobstore.domain.Blob; import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.domain.BlobMetadata; import org.jclouds.blobstore.domain.BlobMetadata;
import org.jclouds.blobstore.domain.ContainerMetadata; 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.reference.BlobStoreConstants;
import org.jclouds.blobstore.strategy.GetAllBlobMetadataStrategy; import org.jclouds.blobstore.strategy.GetAllBlobMetadataStrategy;
import org.jclouds.blobstore.strategy.GetAllBlobsStrategy; import org.jclouds.blobstore.strategy.GetAllBlobsStrategy;

View File

@ -32,9 +32,15 @@ import org.jclouds.blobstore.domain.ContainerMetadata;
import org.jclouds.blobstore.integration.config.StubBlobStoreConnectionModule; import org.jclouds.blobstore.integration.config.StubBlobStoreConnectionModule;
import org.jclouds.blobstore.internal.BlobMapImpl; import org.jclouds.blobstore.internal.BlobMapImpl;
import org.jclouds.blobstore.internal.InputStreamMapImpl; 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.GetAllBlobMetadataStrategy;
import org.jclouds.blobstore.strategy.GetAllBlobsStrategy; import org.jclouds.blobstore.strategy.GetAllBlobsStrategy;
import org.jclouds.blobstore.strategy.internal.ContainerListGetAllBlobMetadataStrategy; 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.jclouds.blobstore.strategy.internal.RetryOnNotFoundGetAllBlobsStrategy;
import org.testng.annotations.Test; import org.testng.annotations.Test;
@ -48,51 +54,58 @@ public class BlobStoreMapsModuleTest {
public void testBuilderBuild() { public void testBuilderBuild() {
BlobStoreMapsModule module = BlobStoreMapsModule.Builder.newBuilder( BlobStoreMapsModule module = BlobStoreMapsModule.Builder.newBuilder(
new TypeLiteral<BlobStore<ContainerMetadata, BlobMetadata, Blob<BlobMetadata>>>() { new TypeLiteral<ContainerMetadata>() {
}, new TypeLiteral<ContainerMetadata>() {
}, new TypeLiteral<BlobMetadata>() { }, new TypeLiteral<BlobMetadata>() {
}, new TypeLiteral<Blob<BlobMetadata>>() { }, new TypeLiteral<Blob<BlobMetadata>>() {
}).build(); }).build();
assertEquals(module.blobMapFactoryType, assertEquals(module.blobMapFactoryType,
new TypeLiteral<BlobMap.Factory<BlobMetadata, Blob<BlobMetadata>>>() { new TypeLiteral<BlobMap.Factory<BlobMetadata, Blob<BlobMetadata>>>() {
}); });
assertEquals( assertEquals(module.blobMapImplType,
module.blobMapImplType, new TypeLiteral<BlobMapImpl<ContainerMetadata, BlobMetadata, Blob<BlobMetadata>>>() {
new TypeLiteral<BlobMapImpl<BlobStore<ContainerMetadata, BlobMetadata, Blob<BlobMetadata>>, ContainerMetadata, BlobMetadata, Blob<BlobMetadata>>>() {
}); });
assertEquals(module.inputStreamMapFactoryType, assertEquals(module.inputStreamMapFactoryType,
new TypeLiteral<InputStreamMap.Factory<BlobMetadata>>() { new TypeLiteral<InputStreamMap.Factory<BlobMetadata>>() {
}); });
assertEquals( assertEquals(
module.inputStreamMapImplType, module.inputStreamMapImplType,
new TypeLiteral<InputStreamMapImpl<BlobStore<ContainerMetadata, BlobMetadata, Blob<BlobMetadata>>, ContainerMetadata, BlobMetadata, Blob<BlobMetadata>>>() { new TypeLiteral<InputStreamMapImpl<ContainerMetadata, BlobMetadata, Blob<BlobMetadata>>>() {
}); });
assertEquals( assertEquals(
module.getAllBlobsStrategyType, module.strategyImplMap
new TypeLiteral<GetAllBlobsStrategy<ContainerMetadata, BlobMetadata, Blob<BlobMetadata>>>() { .get(new TypeLiteral<GetAllBlobsStrategy<ContainerMetadata, BlobMetadata, Blob<BlobMetadata>>>() {
}); }),
assertEquals(
module.getAllBlobsStrategyImplType,
new TypeLiteral<RetryOnNotFoundGetAllBlobsStrategy<ContainerMetadata, BlobMetadata, Blob<BlobMetadata>>>() { new TypeLiteral<RetryOnNotFoundGetAllBlobsStrategy<ContainerMetadata, BlobMetadata, Blob<BlobMetadata>>>() {
}); });
assertEquals( assertEquals(
module.getAllBlobMetadataStrategyType, module.strategyImplMap
new TypeLiteral<GetAllBlobMetadataStrategy<ContainerMetadata, BlobMetadata, Blob<BlobMetadata>>>() { .get(new TypeLiteral<GetAllBlobMetadataStrategy<ContainerMetadata, BlobMetadata, Blob<BlobMetadata>>>() {
}),
new TypeLiteral<ContainerListGetAllBlobMetadataStrategy<ContainerMetadata, BlobMetadata, Blob<BlobMetadata>>>() {
}); });
assertEquals( assertEquals(
module.getAllBlobMetadataStrategyImplType, module.strategyImplMap
new TypeLiteral<ContainerListGetAllBlobMetadataStrategy<ContainerMetadata, BlobMetadata, Blob<BlobMetadata>>>() { .get(new TypeLiteral<ContainsValueStrategy<ContainerMetadata, BlobMetadata, Blob<BlobMetadata>>>() {
}),
new TypeLiteral<ContentMD5ContainsValueStrategy<ContainerMetadata, BlobMetadata, Blob<BlobMetadata>>>() {
});
assertEquals(
module.strategyImplMap
.get(new TypeLiteral<ClearContainerStrategy<ContainerMetadata, BlobMetadata, Blob<BlobMetadata>>>() {
}),
new TypeLiteral<DeleteAllKeysClearContainerStrategy<ContainerMetadata, BlobMetadata, Blob<BlobMetadata>>>() {
});
assertEquals(
module.strategyImplMap
.get(new TypeLiteral<ContainerCountStrategy<ContainerMetadata, BlobMetadata, Blob<BlobMetadata>>>() {
}),
new TypeLiteral<KeyCountStrategy<ContainerMetadata, BlobMetadata, Blob<BlobMetadata>>>() {
}); });
} }
public void testInject() { public void testInject() {
Injector i = Guice Injector i = Guice.createInjector(new StubBlobStoreConnectionModule(),
.createInjector( BlobStoreMapsModule.Builder.newBuilder(new TypeLiteral<ContainerMetadata>() {
new StubBlobStoreConnectionModule(),
BlobStoreMapsModule.Builder
.newBuilder(
new TypeLiteral<BlobStore<ContainerMetadata, BlobMetadata, Blob<BlobMetadata>>>() {
}, new TypeLiteral<ContainerMetadata>() {
}, new TypeLiteral<BlobMetadata>() { }, new TypeLiteral<BlobMetadata>() {
}, new TypeLiteral<Blob<BlobMetadata>>() { }, new TypeLiteral<Blob<BlobMetadata>>() {
}).build()); }).build());

View File

@ -79,7 +79,7 @@ public class StubBlobStoreContextBuilder
Provider<Blob<BlobMetadata>> blobProvider, Provider<Blob<BlobMetadata>> blobProvider,
BlobStore<ContainerMetadata, BlobMetadata, Blob<BlobMetadata>> api) { BlobStore<ContainerMetadata, BlobMetadata, Blob<BlobMetadata>> api) {
return new BlobStoreContextImpl<BlobStore<ContainerMetadata, BlobMetadata, Blob<BlobMetadata>>, ContainerMetadata, BlobMetadata, Blob<BlobMetadata>>( return new BlobStoreContextImpl<BlobStore<ContainerMetadata, BlobMetadata, Blob<BlobMetadata>>, ContainerMetadata, BlobMetadata, Blob<BlobMetadata>>(
blobMapFactory, inputStreamMapFactory, closer, blobProvider, api, URI blobMapFactory, inputStreamMapFactory, closer, blobProvider, api, api, URI
.create("http://localhost/blobstub"), "foo"); .create("http://localhost/blobstub"), "foo");
} }

View File

@ -44,7 +44,6 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.jclouds.blobstore.BlobStore;
import org.jclouds.blobstore.ContainerNotFoundException; import org.jclouds.blobstore.ContainerNotFoundException;
import org.jclouds.blobstore.domain.Blob; import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.domain.BlobMetadata; import org.jclouds.blobstore.domain.BlobMetadata;
@ -59,7 +58,7 @@ import org.testng.annotations.Test;
/** /**
* @author Adrian Cole * @author Adrian Cole
*/ */
public class BaseBlobIntegrationTest<S extends BlobStore<C, M, B>, C extends ContainerMetadata, M extends BlobMetadata, B extends Blob<M>> public class BaseBlobIntegrationTest<S, C extends ContainerMetadata, M extends BlobMetadata, B extends Blob<M>>
extends BaseBlobStoreIntegrationTest<S, C, M, B> { extends BaseBlobStoreIntegrationTest<S, C, M, B> {
@Test(groups = { "integration", "live" }) @Test(groups = { "integration", "live" })
@ -76,12 +75,12 @@ public class BaseBlobIntegrationTest<S extends BlobStore<C, M, B>, C extends Con
addObjectAndValidateContent(containerName, key); addObjectAndValidateContent(containerName, key);
DateTime after = new DateTime().plusSeconds(1); 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); TimeUnit.SECONDS);
validateContent(containerName, key); validateContent(containerName, key);
try { try {
context.getApi().getBlob(containerName, key, ifModifiedSince(after)).get(10, context.getBlobStore().getBlob(containerName, key, ifModifiedSince(after)).get(10,
TimeUnit.SECONDS); TimeUnit.SECONDS);
validateContent(containerName, key); validateContent(containerName, key);
} catch (ExecutionException e) { } catch (ExecutionException e) {
@ -112,12 +111,12 @@ public class BaseBlobIntegrationTest<S extends BlobStore<C, M, B>, C extends Con
addObjectAndValidateContent(containerName, key); addObjectAndValidateContent(containerName, key);
DateTime after = new DateTime().plusSeconds(1); 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); TimeUnit.SECONDS);
validateContent(containerName, key); validateContent(containerName, key);
try { try {
context.getApi().getBlob(containerName, key, ifUnmodifiedSince(before)).get(10, context.getBlobStore().getBlob(containerName, key, ifUnmodifiedSince(before)).get(10,
TimeUnit.SECONDS); TimeUnit.SECONDS);
validateContent(containerName, key); validateContent(containerName, key);
} catch (ExecutionException e) { } catch (ExecutionException e) {
@ -145,12 +144,12 @@ public class BaseBlobIntegrationTest<S extends BlobStore<C, M, B>, C extends Con
addObjectAndValidateContent(containerName, key); addObjectAndValidateContent(containerName, key);
context.getApi().getBlob(containerName, key, ifETagMatches(goodETag)).get(10, context.getBlobStore().getBlob(containerName, key, ifETagMatches(goodETag)).get(10,
TimeUnit.SECONDS); TimeUnit.SECONDS);
validateContent(containerName, key); validateContent(containerName, key);
try { try {
context.getApi().getBlob(containerName, key, ifETagMatches(badETag)).get(10, context.getBlobStore().getBlob(containerName, key, ifETagMatches(badETag)).get(10,
TimeUnit.SECONDS); TimeUnit.SECONDS);
validateContent(containerName, key); validateContent(containerName, key);
} catch (ExecutionException e) { } catch (ExecutionException e) {
@ -178,12 +177,12 @@ public class BaseBlobIntegrationTest<S extends BlobStore<C, M, B>, C extends Con
addObjectAndValidateContent(containerName, key); addObjectAndValidateContent(containerName, key);
context.getApi().getBlob(containerName, key, ifETagDoesntMatch(badETag)).get(10, context.getBlobStore().getBlob(containerName, key, ifETagDoesntMatch(badETag)).get(10,
TimeUnit.SECONDS); TimeUnit.SECONDS);
validateContent(containerName, key); validateContent(containerName, key);
try { try {
context.getApi().getBlob(containerName, key, ifETagDoesntMatch(goodETag)).get(10, context.getBlobStore().getBlob(containerName, key, ifETagDoesntMatch(goodETag)).get(10,
TimeUnit.SECONDS); TimeUnit.SECONDS);
validateContent(containerName, key); validateContent(containerName, key);
} catch (ExecutionException e) { } catch (ExecutionException e) {
@ -208,13 +207,13 @@ public class BaseBlobIntegrationTest<S extends BlobStore<C, M, B>, C extends Con
String key = "apples"; String key = "apples";
addObjectAndValidateContent(containerName, key); 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); TimeUnit.SECONDS);
assertEquals(BlobStoreUtils.getContentAsStringAndClose(object1), TEST_STRING.substring(0, assertEquals(BlobStoreUtils.getContentAsStringAndClose(object1), TEST_STRING.substring(0,
6)); 6));
B object2 = context.getApi().getBlob(containerName, key, range(6, TEST_STRING.length())) B object2 = context.getBlobStore().getBlob(containerName, key,
.get(10, TimeUnit.SECONDS); range(6, TEST_STRING.length())).get(10, TimeUnit.SECONDS);
assertEquals(BlobStoreUtils.getContentAsStringAndClose(object2), TEST_STRING.substring(6, assertEquals(BlobStoreUtils.getContentAsStringAndClose(object2), TEST_STRING.substring(6,
TEST_STRING.length())); TEST_STRING.length()));
} finally { } finally {
@ -231,7 +230,7 @@ public class BaseBlobIntegrationTest<S extends BlobStore<C, M, B>, C extends Con
String key = "apples"; String key = "apples";
addObjectAndValidateContent(containerName, key); 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); range(0, 5).range(6, TEST_STRING.length())).get(10, TimeUnit.SECONDS);
assertEquals(BlobStoreUtils.getContentAsStringAndClose(object), TEST_STRING); assertEquals(BlobStoreUtils.getContentAsStringAndClose(object), TEST_STRING);
@ -249,7 +248,8 @@ public class BaseBlobIntegrationTest<S extends BlobStore<C, M, B>, C extends Con
String key = "apples"; String key = "apples";
addObjectAndValidateContent(containerName, key); 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 assertEquals(BlobStoreUtils.getContentAsStringAndClose(object), TEST_STRING
.substring(TEST_STRING.length() - 5)); .substring(TEST_STRING.length() - 5));
assertEquals(object.getContentLength(), 5); assertEquals(object.getContentLength(), 5);
@ -267,7 +267,7 @@ public class BaseBlobIntegrationTest<S extends BlobStore<C, M, B>, C extends Con
String key = "apples"; String key = "apples";
addObjectAndValidateContent(containerName, key); 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); TimeUnit.SECONDS);
assertEquals(BlobStoreUtils.getContentAsStringAndClose(object), TEST_STRING.substring(5, assertEquals(BlobStoreUtils.getContentAsStringAndClose(object), TEST_STRING.substring(5,
TEST_STRING.length())); TEST_STRING.length()));
@ -289,7 +289,7 @@ public class BaseBlobIntegrationTest<S extends BlobStore<C, M, B>, C extends Con
String containerName = getContainerName(); String containerName = getContainerName();
String key = "test"; String key = "test";
try { try {
assert context.getApi().removeBlob(containerName, key).get(10, TimeUnit.SECONDS); context.getBlobStore().removeBlob(containerName, key).get(10, TimeUnit.SECONDS);
} finally { } finally {
returnContainer(containerName); returnContainer(containerName);
} }
@ -305,7 +305,7 @@ public class BaseBlobIntegrationTest<S extends BlobStore<C, M, B>, C extends Con
String containerName = getContainerName(); String containerName = getContainerName();
try { try {
addBlobToContainer(containerName, key); 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); assertContainerEmptyDeleting(containerName, key);
} finally { } finally {
returnContainer(containerName); returnContainer(containerName);
@ -314,7 +314,8 @@ public class BaseBlobIntegrationTest<S extends BlobStore<C, M, B>, C extends Con
private void assertContainerEmptyDeleting(String containerName, String key) private void assertContainerEmptyDeleting(String containerName, String key)
throws InterruptedException, ExecutionException, TimeoutException { throws InterruptedException, ExecutionException, TimeoutException {
SortedSet<M> listing = context.getApi().listBlobs(containerName).get(10, TimeUnit.SECONDS); SortedSet<M> listing = context.getBlobStore().listBlobs(containerName).get(10,
TimeUnit.SECONDS);
assertEquals(listing.size(), 0, String.format( assertEquals(listing.size(), 0, String.format(
"deleting %s, we still have %s left in container %s, using encoding %s", key, "deleting %s, we still have %s left in container %s, using encoding %s", key,
listing.size(), containerName, LOCAL_ENCODING)); listing.size(), containerName, LOCAL_ENCODING));
@ -323,7 +324,7 @@ public class BaseBlobIntegrationTest<S extends BlobStore<C, M, B>, C extends Con
@Test(groups = { "integration", "live" }) @Test(groups = { "integration", "live" })
public void deleteObjectNoContainer() throws Exception { public void deleteObjectNoContainer() throws Exception {
try { try {
context.getApi().removeBlob("donb", "test").get(10, TimeUnit.SECONDS); context.getBlobStore().removeBlob("donb", "test").get(10, TimeUnit.SECONDS);
} catch (ExecutionException e) { } catch (ExecutionException e) {
assert (e.getCause() instanceof HttpResponseException || e.getCause() instanceof ContainerNotFoundException); assert (e.getCause() instanceof HttpResponseException || e.getCause() instanceof ContainerNotFoundException);
if (e.getCause() instanceof HttpResponseException) if (e.getCause() instanceof HttpResponseException)
@ -352,12 +353,14 @@ public class BaseBlobIntegrationTest<S extends BlobStore<C, M, B>, C extends Con
} }
String containerName = getContainerName(); String containerName = getContainerName();
try { try {
assertNotNull(context.getApi().putBlob(containerName, object).get(10, TimeUnit.SECONDS)); assertNotNull(context.getBlobStore().putBlob(containerName, object).get(10,
object = context.getApi().getBlob(containerName, object.getKey()) TimeUnit.SECONDS));
.get(10, TimeUnit.SECONDS); object = context.getBlobStore().getBlob(containerName, object.getKey()).get(10,
TimeUnit.SECONDS);
String returnedString = BlobStoreUtils.getContentAsStringAndClose(object); String returnedString = BlobStoreUtils.getContentAsStringAndClose(object);
assertEquals(returnedString, realObject); 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 { } finally {
returnContainer(containerName); returnContainer(containerName);
} }
@ -383,7 +386,7 @@ public class BaseBlobIntegrationTest<S extends BlobStore<C, M, B>, C extends Con
M metadata = newObject.getMetadata(); M metadata = newObject.getMetadata();
validateMetadata(metadata); validateMetadata(metadata);
validateMetadata(context.getApi().blobMetadata(containerName, key)); validateMetadata(context.getBlobStore().blobMetadata(containerName, key));
} finally { } finally {
returnContainer(containerName); returnContainer(containerName);
} }

View File

@ -31,7 +31,6 @@ import java.net.URL;
import java.net.URLConnection; import java.net.URLConnection;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.jclouds.blobstore.BlobStore;
import org.jclouds.blobstore.domain.Blob; import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.domain.BlobMetadata; import org.jclouds.blobstore.domain.BlobMetadata;
import org.jclouds.blobstore.domain.ContainerMetadata; import org.jclouds.blobstore.domain.ContainerMetadata;
@ -48,7 +47,7 @@ import org.testng.annotations.Test;
* @author Adrian Cole * @author Adrian Cole
*/ */
@Test(groups = { "live" }, testName = "blobstore.BlobLiveTest") @Test(groups = { "live" }, testName = "blobstore.BlobLiveTest")
public class BaseBlobLiveTest<S extends BlobStore<C, M, B>, C extends ContainerMetadata, M extends BlobMetadata, B extends Blob<M>> public class BaseBlobLiveTest<S, C extends ContainerMetadata, M extends BlobMetadata, B extends Blob<M>>
extends BaseBlobStoreIntegrationTest<S, C, M, B> { extends BaseBlobStoreIntegrationTest<S, C, M, B> {
private static final String sysHttpStreamUrl = System private static final String sysHttpStreamUrl = System
@ -82,8 +81,8 @@ public class BaseBlobLiveTest<S extends BlobStore<C, M, B>, C extends ContainerM
object.getMetadata().setSize(length); object.getMetadata().setSize(length);
String bucketName = getContainerName(); String bucketName = getContainerName();
try { try {
context.getApi().putBlob(bucketName, object).get(180, TimeUnit.SECONDS); context.getBlobStore().putBlob(bucketName, object).get(180, TimeUnit.SECONDS);
assertEquals(context.getApi().blobMetadata(bucketName, key).getContentMD5(), md5); assertEquals(context.getBlobStore().blobMetadata(bucketName, key).getContentMD5(), md5);
} finally { } finally {
returnContainer(bucketName); returnContainer(bucketName);
} }

View File

@ -37,7 +37,6 @@ import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.jclouds.blobstore.BlobStore;
import org.jclouds.blobstore.BlobStoreContext; import org.jclouds.blobstore.BlobStoreContext;
import org.jclouds.blobstore.domain.Blob; import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.domain.BlobMetadata; import org.jclouds.blobstore.domain.BlobMetadata;
@ -50,7 +49,7 @@ import org.testng.annotations.Test;
* *
* @author Adrian Cole * @author Adrian Cole
*/ */
public class BaseBlobMapIntegrationTest<S extends BlobStore<C, M, B>, C extends ContainerMetadata, M extends BlobMetadata, B extends Blob<M>> public class BaseBlobMapIntegrationTest<S, C extends ContainerMetadata, M extends BlobMetadata, B extends Blob<M>>
extends BaseMapIntegrationTest<S, C, M, B, B> { extends BaseMapIntegrationTest<S, C, M, B, B> {
@Override @Override

View File

@ -27,8 +27,6 @@ import static com.google.common.base.Preconditions.checkNotNull;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.SortedSet; import java.util.SortedSet;
import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ArrayBlockingQueue;
@ -41,13 +39,11 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import org.jclouds.blobstore.BlobStore;
import org.jclouds.blobstore.BlobStoreContext; import org.jclouds.blobstore.BlobStoreContext;
import org.jclouds.blobstore.domain.Blob; import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.domain.BlobMetadata; import org.jclouds.blobstore.domain.BlobMetadata;
import org.jclouds.blobstore.domain.ContainerMetadata; import org.jclouds.blobstore.domain.ContainerMetadata;
import org.jclouds.blobstore.util.BlobStoreUtils; import org.jclouds.blobstore.util.BlobStoreUtils;
import org.jclouds.http.HttpResponseException;
import org.jclouds.http.HttpUtils; import org.jclouds.http.HttpUtils;
import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule; import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule;
import org.jclouds.util.Utils; import org.jclouds.util.Utils;
@ -62,7 +58,7 @@ import com.google.common.collect.Iterables;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import com.google.inject.Module; import com.google.inject.Module;
public class BaseBlobStoreIntegrationTest<S extends BlobStore<C, M, B>, C extends ContainerMetadata, M extends BlobMetadata, B extends Blob<M>> { public class BaseBlobStoreIntegrationTest<S, C extends ContainerMetadata, M extends BlobMetadata, B extends Blob<M>> {
protected static final String LOCAL_ENCODING = System.getProperty("file.encoding"); protected static final String LOCAL_ENCODING = System.getProperty("file.encoding");
protected static final String TEST_STRING = "<apples><apple name=\"fuji\"></apple> </apples>"; protected static final String TEST_STRING = "<apples><apple name=\"fuji\"></apple> </apples>";
@ -110,10 +106,10 @@ public class BaseBlobStoreIntegrationTest<S extends BlobStore<C, M, B>, C extend
protected ExecutorService exec; protected ExecutorService exec;
/** /**
* we are doing this at a class level, as the context.getApi() object is going to be shared for * we are doing this at a class level, as the context.getBlobStore() object is going to be shared
* all methods in the class. We don't want to do this for group, as some test classes may want to * for all methods in the class. We don't want to do this for group, as some test classes may
* have a different implementation of context.getApi(). For example, one class may want * want to have a different implementation of context.getBlobStore(). For example, one class may
* non-blocking i/o and another class google appengine. * want non-blocking i/o and another class google appengine.
*/ */
@BeforeClass(groups = { "integration", "live" }) @BeforeClass(groups = { "integration", "live" })
public void setUpResourcesOnThisThread(ITestContext testContext) throws Exception { public void setUpResourcesOnThisThread(ITestContext testContext) throws Exception {
@ -182,7 +178,7 @@ public class BaseBlobStoreIntegrationTest<S extends BlobStore<C, M, B>, C extend
try { try {
for (int i = 0; i < 2; i++) { for (int i = 0; i < 2; i++) {
Iterable<ContainerMetadata> testContainers = Iterables.filter( Iterable<ContainerMetadata> testContainers = Iterables.filter(
(SortedSet<ContainerMetadata>) context.getApi().listContainers(), (SortedSet<ContainerMetadata>) context.getBlobStore().listContainers(),
new Predicate<ContainerMetadata>() { new Predicate<ContainerMetadata>() {
public boolean apply(ContainerMetadata input) { public boolean apply(ContainerMetadata input) {
return input.getName().startsWith(CONTAINER_PREFIX.toLowerCase()); return input.getName().startsWith(CONTAINER_PREFIX.toLowerCase());
@ -234,42 +230,8 @@ public class BaseBlobStoreIntegrationTest<S extends BlobStore<C, M, B>, C extend
protected static void createContainerAndEnsureEmpty(BlobStoreContext<?, ?, ?, ?> context, protected static void createContainerAndEnsureEmpty(BlobStoreContext<?, ?, ?, ?> context,
final String containerName) throws InterruptedException, ExecutionException, final String containerName) throws InterruptedException, ExecutionException,
TimeoutException { TimeoutException {
attemptToCreateContainerButRetryOn409(context, containerName); context.getBlobStore().createContainer(containerName).get(30, TimeUnit.SECONDS);
emptyContainer(context, containerName); context.createInputStreamMap(containerName).clear();
}
/**
* 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<? extends BlobStore<?, ?, ?>, ?, ?, ?> 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;
} }
protected void createContainerAndEnsureEmpty(String containerName) throws InterruptedException, protected void createContainerAndEnsureEmpty(String containerName) throws InterruptedException,
@ -287,13 +249,13 @@ public class BaseBlobStoreIntegrationTest<S extends BlobStore<C, M, B>, C extend
protected void addBlobToContainer(String sourceContainer, B object) throws InterruptedException, protected void addBlobToContainer(String sourceContainer, B object) throws InterruptedException,
ExecutionException, TimeoutException, IOException { 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, protected B validateContent(String sourceContainer, String key) throws InterruptedException,
ExecutionException, TimeoutException, IOException { ExecutionException, TimeoutException, IOException {
assertEventuallyContainerSize(sourceContainer, 1); 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; assert newObject != null;
assertEquals(BlobStoreUtils.getContentAsStringAndClose(newObject), TEST_STRING); assertEquals(BlobStoreUtils.getContentAsStringAndClose(newObject), TEST_STRING);
return newObject; return newObject;
@ -304,8 +266,8 @@ public class BaseBlobStoreIntegrationTest<S extends BlobStore<C, M, B>, C extend
assertEventually(new Runnable() { assertEventually(new Runnable() {
public void run() { public void run() {
try { try {
assertEquals(context.getApi().listBlobs(containerName).get(10, TimeUnit.SECONDS) assertEquals(context.getBlobStore().listBlobs(containerName).get(10,
.size(), count); TimeUnit.SECONDS).size(), count);
} catch (Exception e) { } catch (Exception e) {
Utils.<RuntimeException> rethrowIfRuntimeOrSameType(e); Utils.<RuntimeException> rethrowIfRuntimeOrSameType(e);
} }
@ -317,8 +279,7 @@ public class BaseBlobStoreIntegrationTest<S extends BlobStore<C, M, B>, C extend
TimeoutException { TimeoutException {
String containerName = containerJsr330.poll(30, TimeUnit.SECONDS); String containerName = containerJsr330.poll(30, TimeUnit.SECONDS);
assert containerName != null : "unable to get a container for the test"; assert containerName != null : "unable to get a container for the test";
if (!emptyContainer(containerName)) createContainerAndEnsureEmpty(containerName);
this.createContainerAndEnsureEmpty(containerName);
return containerName; return containerName;
} }
@ -345,7 +306,7 @@ public class BaseBlobStoreIntegrationTest<S extends BlobStore<C, M, B>, C extend
* *substantially* slow down tests on a real server over a network. * *substantially* slow down tests on a real server over a network.
*/ */
if (SANITY_CHECK_RETURNED_BUCKET_NAME) { if (SANITY_CHECK_RETURNED_BUCKET_NAME) {
if (!Iterables.any(context.getApi().listContainers(), if (!Iterables.any(context.getBlobStore().listContainers(),
new Predicate<ContainerMetadata>() { new Predicate<ContainerMetadata>() {
public boolean apply(ContainerMetadata md) { public boolean apply(ContainerMetadata md) {
return containerName.equals(md.getName()); return containerName.equals(md.getName());
@ -396,60 +357,15 @@ public class BaseBlobStoreIntegrationTest<S extends BlobStore<C, M, B>, C extend
return new JavaUrlHttpCommandExecutorServiceModule(); return new JavaUrlHttpCommandExecutorServiceModule();
} }
protected boolean emptyContainer(String name) throws InterruptedException, ExecutionException, protected static void deleteContainer(final BlobStoreContext<?, ?, ?, ?> context,
TimeoutException {
return emptyContainer(context, name);
}
/**
* Remove any objects in a container, leaving it empty.
*
* @return
*/
protected static boolean emptyContainer(final BlobStoreContext<?, ?, ?, ?> context,
final String name) throws InterruptedException, ExecutionException, TimeoutException { final String name) throws InterruptedException, ExecutionException, TimeoutException {
if (context.getApi().containerExists(name)) { if (context.getBlobStore().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<String, InputStream> map = context.createInputStreamMap(name);
Set<String> 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.<RuntimeException> rethrowIfRuntimeOrSameType(e);
}
}
});
return true;
}
return false;
}
protected static void deleteContainer(
final BlobStoreContext<? extends BlobStore<?, ?, ?>, ?, ?, ?> context, final String name)
throws InterruptedException, ExecutionException, TimeoutException {
if (context.getApi().containerExists(name)) {
System.err.printf("*** deleting container %s...%n", name); System.err.printf("*** deleting container %s...%n", name);
emptyContainer(context, name); context.getBlobStore().deleteContainer(name).get(10, TimeUnit.SECONDS);
context.getApi().deleteContainer(name).get(10, TimeUnit.SECONDS);
assertEventually(new Runnable() { assertEventually(new Runnable() {
public void run() { public void run() {
try { try {
assert !context.getApi().containerExists(name) : "container " + name assert !context.getBlobStore().containerExists(name) : "container " + name
+ " still exists"; + " still exists";
} catch (Exception e) { } catch (Exception e) {
Utils.<RuntimeException> rethrowIfRuntimeOrSameType(e); Utils.<RuntimeException> rethrowIfRuntimeOrSameType(e);

View File

@ -31,7 +31,6 @@ import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import org.jclouds.blobstore.BlobStore;
import org.jclouds.blobstore.domain.Blob; import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.domain.BlobMetadata; import org.jclouds.blobstore.domain.BlobMetadata;
import org.jclouds.blobstore.domain.ContainerMetadata; import org.jclouds.blobstore.domain.ContainerMetadata;
@ -41,20 +40,20 @@ import org.testng.annotations.Test;
/** /**
* @author Adrian Cole * @author Adrian Cole
*/ */
public class BaseContainerIntegrationTest<S extends BlobStore<C, M, B>, C extends ContainerMetadata, M extends BlobMetadata, B extends Blob<M>> public class BaseContainerIntegrationTest<S, C extends ContainerMetadata, M extends BlobMetadata, B extends Blob<M>>
extends BaseBlobStoreIntegrationTest<S, C, M, B> { extends BaseBlobStoreIntegrationTest<S, C, M, B> {
@Test(groups = { "integration", "live" }) @Test(groups = { "integration", "live" })
public void containerDoesntExist() throws Exception { public void containerDoesntExist() throws Exception {
assert !context.getApi().containerExists("forgetaboutit"); assert !context.getBlobStore().containerExists("forgetaboutit");
} }
@Test(groups = { "integration", "live" }) @Test(groups = { "integration", "live" })
public void testPutTwiceIsOk() throws Exception { public void testPutTwiceIsOk() throws Exception {
String containerName = getContainerName(); String containerName = getContainerName();
try { try {
context.getApi().createContainer(containerName).get(10, TimeUnit.SECONDS); context.getBlobStore().createContainer(containerName).get(10, TimeUnit.SECONDS);
context.getApi().createContainer(containerName).get(10, TimeUnit.SECONDS); context.getBlobStore().createContainer(containerName).get(10, TimeUnit.SECONDS);
} finally { } finally {
returnContainer(containerName); returnContainer(containerName);
} }
@ -64,28 +63,21 @@ public class BaseContainerIntegrationTest<S extends BlobStore<C, M, B>, C extend
public void containerExists() throws Exception { public void containerExists() throws Exception {
String containerName = getContainerName(); String containerName = getContainerName();
try { try {
assert context.getApi().containerExists(containerName); assert context.getBlobStore().containerExists(containerName);
} finally { } finally {
returnContainer(containerName); returnContainer(containerName);
} }
} }
/**
* this method overrides containerName to ensure it isn't found
*/
@Test(groups = { "integration", "live" }) @Test(groups = { "integration", "live" })
public void deleteContainerIfEmptyNotFound() throws Exception { public void deleteContainerWithContents() throws Exception {
assert context.getApi().deleteContainer("dbienf").get(10, TimeUnit.SECONDS);
}
@Test(groups = { "integration", "live" })
public void deleteContainerIfEmptyButHasContents() throws Exception {
String containerName = getContainerName(); String containerName = getContainerName();
try { try {
addBlobToContainer(containerName, "test"); addBlobToContainer(containerName, "test");
assert !context.getApi().deleteContainer(containerName).get(10, TimeUnit.SECONDS); context.getBlobStore().deleteContainer(containerName).get(10, TimeUnit.SECONDS);
assertNotExists(containerName);
} finally { } finally {
returnContainer(containerName); recycleContainer(containerName);
} }
} }
@ -93,32 +85,26 @@ public class BaseContainerIntegrationTest<S extends BlobStore<C, M, B>, C extend
public void deleteContainerIfEmpty() throws Exception { public void deleteContainerIfEmpty() throws Exception {
final String containerName = getContainerName(); final String containerName = getContainerName();
try { try {
assert context.getApi().deleteContainer(containerName).get(10, TimeUnit.SECONDS); context.getBlobStore().deleteContainer(containerName).get(10, TimeUnit.SECONDS);
assertNotExists(containerName);
assertEventually(new Runnable() {
public void run() {
try {
assert !context.getApi().containerExists(containerName) : "container "
+ containerName + " still exists";
} catch (Exception e) {
Utils.<RuntimeException> rethrowIfRuntimeOrSameType(e);
}
}
});
} finally { } finally {
// this container is now deleted, so we can't reuse it directly // this container is now deleted, so we can't reuse it directly
recycleContainer(containerName); recycleContainer(containerName);
} }
} }
protected void addAlphabetUnderRoot(String containerName) throws InterruptedException, private void assertNotExists(final String containerName) throws InterruptedException {
ExecutionException, TimeoutException { assertEventually(new Runnable() {
for (char letter = 'a'; letter <= 'z'; letter++) { public void run() {
B blob = context.newBlob(letter + ""); try {
blob.setData(letter + "content"); assert !context.getBlobStore().containerExists(containerName) : "container "
context.getApi().putBlob(containerName, blob).get(10, TimeUnit.SECONDS); + containerName + " still exists";
} catch (Exception e) {
Utils.<RuntimeException> rethrowIfRuntimeOrSameType(e);
} }
} }
});
}
@Test(groups = { "integration", "live" }) @Test(groups = { "integration", "live" })
public void testListContainer() throws InterruptedException, ExecutionException, public void testListContainer() throws InterruptedException, ExecutionException,
@ -126,7 +112,7 @@ public class BaseContainerIntegrationTest<S extends BlobStore<C, M, B>, C extend
String containerName = getContainerName(); String containerName = getContainerName();
try { try {
add15UnderRoot(containerName); add15UnderRoot(containerName);
SortedSet<M> container = context.getApi().listBlobs(containerName).get(10, SortedSet<M> container = context.getBlobStore().listBlobs(containerName).get(10,
TimeUnit.SECONDS); TimeUnit.SECONDS);
assertEquals(container.size(), 15); assertEquals(container.size(), 15);
} finally { } finally {
@ -135,12 +121,21 @@ public class BaseContainerIntegrationTest<S extends BlobStore<C, M, B>, 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, protected void add15UnderRoot(String containerName) throws InterruptedException,
ExecutionException, TimeoutException { ExecutionException, TimeoutException {
for (int i = 0; i < 15; i++) { for (int i = 0; i < 15; i++) {
B blob = context.newBlob(i + ""); B blob = context.newBlob(i + "");
blob.setData(i + "content"); 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<S extends BlobStore<C, M, B>, C extend
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
B blob = context.newBlob(prefix + "/" + i); B blob = context.newBlob(prefix + "/" + i);
blob.setData(i + "content"); blob.setData(i + "content");
context.getApi().putBlob(containerName, blob).get(10, TimeUnit.SECONDS); context.getBlobStore().putBlob(containerName, blob).get(10, TimeUnit.SECONDS);
} }
} }
} }

View File

@ -23,7 +23,6 @@
*/ */
package org.jclouds.blobstore.integration.internal; package org.jclouds.blobstore.integration.internal;
import org.jclouds.blobstore.BlobStore;
import org.jclouds.blobstore.domain.Blob; import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.domain.BlobMetadata; import org.jclouds.blobstore.domain.BlobMetadata;
import org.jclouds.blobstore.domain.ContainerMetadata; import org.jclouds.blobstore.domain.ContainerMetadata;
@ -32,7 +31,7 @@ import org.jclouds.blobstore.domain.ContainerMetadata;
* *
* @author Adrian Cole * @author Adrian Cole
*/ */
public class BaseContainerLiveTest<S extends BlobStore<C, M, B>, C extends ContainerMetadata, M extends BlobMetadata, B extends Blob<M>> public class BaseContainerLiveTest<S, C extends ContainerMetadata, M extends BlobMetadata, B extends Blob<M>>
extends BaseBlobStoreIntegrationTest<S, C, M, B> { extends BaseBlobStoreIntegrationTest<S, C, M, B> {
} }

View File

@ -37,7 +37,6 @@ import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.jclouds.blobstore.BlobStore;
import org.jclouds.blobstore.BlobStoreContext; import org.jclouds.blobstore.BlobStoreContext;
import org.jclouds.blobstore.InputStreamMap; import org.jclouds.blobstore.InputStreamMap;
import org.jclouds.blobstore.domain.Blob; import org.jclouds.blobstore.domain.Blob;
@ -51,7 +50,7 @@ import org.testng.annotations.Test;
* *
* @author Adrian Cole * @author Adrian Cole
*/ */
public class BaseInputStreamMapIntegrationTest<S extends BlobStore<C, M, B>, C extends ContainerMetadata, M extends BlobMetadata, B extends Blob<M>> public class BaseInputStreamMapIntegrationTest<S, C extends ContainerMetadata, M extends BlobMetadata, B extends Blob<M>>
extends BaseMapIntegrationTest<S, C, M, B, InputStream> { extends BaseMapIntegrationTest<S, C, M, B, InputStream> {
@Override @Override

View File

@ -37,7 +37,6 @@ import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.jclouds.blobstore.BlobStore;
import org.jclouds.blobstore.BlobStoreContext; import org.jclouds.blobstore.BlobStoreContext;
import org.jclouds.blobstore.ListableMap; import org.jclouds.blobstore.ListableMap;
import org.jclouds.blobstore.domain.Blob; 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.ImmutableMap;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
public abstract class BaseMapIntegrationTest<S extends BlobStore<C, M, B>, C extends ContainerMetadata, M extends BlobMetadata, B extends Blob<M>, V> public abstract class BaseMapIntegrationTest<S, C extends ContainerMetadata, M extends BlobMetadata, B extends Blob<M>, V>
extends BaseBlobStoreIntegrationTest<S, C, M, B> { extends BaseBlobStoreIntegrationTest<S, C, M, B> {
public abstract void testPutAll() throws InterruptedException, ExecutionException, public abstract void testPutAll() throws InterruptedException, ExecutionException,

View File

@ -25,7 +25,6 @@ package org.jclouds.blobstore.integration.internal;
import java.util.SortedSet; import java.util.SortedSet;
import org.jclouds.blobstore.BlobStore;
import org.jclouds.blobstore.domain.Blob; import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.domain.BlobMetadata; import org.jclouds.blobstore.domain.BlobMetadata;
import org.jclouds.blobstore.domain.ContainerMetadata; import org.jclouds.blobstore.domain.ContainerMetadata;
@ -35,12 +34,12 @@ import org.testng.annotations.Test;
* *
* @author Adrian Cole * @author Adrian Cole
*/ */
public class BaseServiceIntegrationTest<S extends BlobStore<C, M, B>, C extends ContainerMetadata, M extends BlobMetadata, B extends Blob<M>> public class BaseServiceIntegrationTest<S, C extends ContainerMetadata, M extends BlobMetadata, B extends Blob<M>>
extends BaseBlobStoreIntegrationTest<S, C, M, B> { extends BaseBlobStoreIntegrationTest<S, C, M, B> {
@Test(groups = { "integration", "live" }) @Test(groups = { "integration", "live" })
void containerDoesntExist() throws Exception { void containerDoesntExist() throws Exception {
SortedSet<C> list = context.getApi().listContainers(); SortedSet<C> list = context.getBlobStore().listContainers();
assert !list.contains(new ContainerMetadata("shouldntexist")); assert !list.contains(new ContainerMetadata("shouldntexist"));
} }

View File

@ -23,7 +23,6 @@
*/ */
package org.jclouds.blobstore.integration.internal; package org.jclouds.blobstore.integration.internal;
import org.jclouds.blobstore.BlobStore;
import org.jclouds.blobstore.BlobStoreContext; import org.jclouds.blobstore.BlobStoreContext;
import org.jclouds.blobstore.domain.Blob; import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.domain.BlobMetadata; import org.jclouds.blobstore.domain.BlobMetadata;
@ -32,7 +31,7 @@ import org.testng.ITestContext;
import com.google.inject.Module; import com.google.inject.Module;
public abstract class BaseTestInitializer<S extends BlobStore<C, M, B>, C extends ContainerMetadata, M extends BlobMetadata, B extends Blob<M>> { public abstract class BaseTestInitializer<S, C extends ContainerMetadata, M extends BlobMetadata, B extends Blob<M>> {
public BlobStoreContext<S, C, M, B> init(Module configurationModule, ITestContext testContext) public BlobStoreContext<S, C, M, B> init(Module configurationModule, ITestContext testContext)
throws Exception { throws Exception {

View File

@ -199,18 +199,29 @@ public class StubBlobStore<C extends ContainerMetadata, M extends BlobMetadata,
return realContents.get(key).getMetadata(); return realContents.get(key).getMetadata();
} }
public Future<Boolean> removeBlob(final String container, final String key) { public Future<Void> removeBlob(final String container, final String key) {
return new FutureBase<Boolean>() { return new FutureBase<Void>() {
public Boolean get() throws InterruptedException, ExecutionException { public Void get() throws InterruptedException, ExecutionException {
if (getContainerToBlobs().containsKey(container)) { if (getContainerToBlobs().containsKey(container)) {
getContainerToBlobs().get(container).remove(key); getContainerToBlobs().get(container).remove(key);
} }
return true; return null;
} }
}; };
} }
public Future<Boolean> deleteContainer(final String container) { public Future<Void> deleteContainer(final String container) {
return new FutureBase<Void>() {
public Void get() throws InterruptedException, ExecutionException {
if (getContainerToBlobs().containsKey(container)) {
getContainerToBlobs().remove(container);
}
return null;
}
};
}
public Future<Boolean> deleteContainerImpl(final String container) {
return new FutureBase<Boolean>() { return new FutureBase<Boolean>() {
public Boolean get() throws InterruptedException, ExecutionException { public Boolean get() throws InterruptedException, ExecutionException {
if (getContainerToBlobs().containsKey(container)) { if (getContainerToBlobs().containsKey(container)) {
@ -385,6 +396,7 @@ public class StubBlobStore<C extends ContainerMetadata, M extends BlobMetadata,
byte[] data = toByteArray(object.getData()); byte[] data = toByteArray(object.getData());
final byte[] eTag = HttpUtils.md5(data); final byte[] eTag = HttpUtils.md5(data);
newMd.setETag(eTag); newMd.setETag(eTag);
newMd.setContentMD5(eTag);
newMd.setContentType(object.getMetadata().getContentType()); newMd.setContentType(object.getMetadata().getContentType());
B blob = blobProvider.get(); B blob = blobProvider.get();

View File

@ -52,13 +52,13 @@ public class BaseBlobMapTest {
BlobStoreContext<BlobStore<ContainerMetadata, BlobMetadata, Blob<BlobMetadata>>, ContainerMetadata, BlobMetadata, Blob<BlobMetadata>> context; BlobStoreContext<BlobStore<ContainerMetadata, BlobMetadata, Blob<BlobMetadata>>, ContainerMetadata, BlobMetadata, Blob<BlobMetadata>> context;
InputStreamMapImpl<BlobStore<ContainerMetadata, BlobMetadata, Blob<BlobMetadata>>, ContainerMetadata, BlobMetadata, Blob<BlobMetadata>> map; InputStreamMapImpl<ContainerMetadata, BlobMetadata, Blob<BlobMetadata>> map;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@BeforeClass @BeforeClass
void addDefaultObjectsSoThatTestsWillPass() { void addDefaultObjectsSoThatTestsWillPass() {
context = new StubBlobStoreContextBuilder().buildContext(); context = new StubBlobStoreContextBuilder().buildContext();
map = (InputStreamMapImpl<BlobStore<ContainerMetadata, BlobMetadata, Blob<BlobMetadata>>, ContainerMetadata, BlobMetadata, Blob<BlobMetadata>>) context map = (InputStreamMapImpl<ContainerMetadata, BlobMetadata, Blob<BlobMetadata>>) context
.createInputStreamMap("test"); .createInputStreamMap("test");
} }

View File

@ -0,0 +1,91 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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<HttpResponse, byte[]>, 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;
}
}

View File

@ -0,0 +1,56 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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<HttpResponse, URI> {
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);
}
}
}

View File

@ -21,23 +21,22 @@
* under the License. * 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; import com.google.common.base.Function;
/** /**
* Simply returns the InputStream of the response
* *
* @author Adrian Cole * @author Adrian Cole
*/ */
public class ReturnTrueIfContainerNotFound implements Function<Exception, Boolean> { public class ReturnInputStream implements Function<HttpResponse, InputStream> {
public Boolean apply(Exception from) { public InputStream apply(HttpResponse from) {
if (from instanceof ContainerNotFoundException) { return from.getContent();
return true;
} }
return null;
}
} }

View File

@ -0,0 +1,59 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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<HttpResponse, Void> {
static final Void v;
static {
Constructor<Void> 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;
}
}

View File

@ -103,7 +103,12 @@ public class BackoffLimitedRetryHandler implements HttpRetryHandler {
} }
public void imposeBackoffExponentialDelay(int failureCount, String commandDescription) { 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, logger.debug("Retry %1$d/%2$d after server error, delaying for %3$d ms: %4$s", failureCount,
retryCountLimit, delayMs, commandDescription); retryCountLimit, delayMs, commandDescription);
try { try {

Some files were not shown because too many files have changed in this diff Show More