Issue 73: Revised and renamed key value store to BlobStore with common Map implementations; refactored implementations and test drivers to utilize new blobstore

git-svn-id: http://jclouds.googlecode.com/svn/trunk@1910 3d8758e0-26b5-11de-8745-db77d3ebf521
This commit is contained in:
adrian.f.cole 2009-09-24 00:28:01 +00:00
parent ffd34b36c2
commit e3672b6a59
303 changed files with 13115 additions and 7600 deletions

View File

@ -45,13 +45,4 @@
<url>http://jclouds.googlecode.com/svn/trunk</url>
</scm>
<dependencies>
<dependency>
<groupId>xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.2</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -28,7 +28,7 @@ import static org.testng.Assert.assertEquals;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import org.jclouds.aws.util.DateService;
import org.jclouds.util.DateService;
import org.joda.time.DateTime;
import org.testng.annotations.Test;
@ -60,7 +60,7 @@ public class BaseEC2RequestOptionsTest {
* @see MyRequestOptions#withId(String)
*/
public String getId() {
return parameters.get("id");
return queryParameters.get("id");
}
/**

View File

@ -41,8 +41,8 @@
<module>s3</module>
</modules>
<properties>
<jclouds.aws.accesskeyid />
<jclouds.aws.secretaccesskey />
<jclouds.test.user>${jclouds.aws.accesskeyid}</jclouds.test.user>
<jclouds.test.key>${jclouds.aws.secretaccesskey}</jclouds.test.key>
</properties>
<dependencies>
<dependency>
@ -70,53 +70,4 @@
<scope>test</scope>
</dependency>
</dependencies>
<profiles>
<profile>
<id>live</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<executions>
<execution>
<id>integration</id>
<phase>integration-test</phase>
<goals>
<goal>test</goal>
</goals>
<configuration>
<!-- note that the groups/excluded groups don't work due to some problem
in surefire or testng. instead, we have to exclude via file path
<groups>live,integration</groups>
<excludedGroups>unit,performance</excludedGroups> -->
<excludes>
<exclude>none</exclude>
</excludes>
<includes>
<include>**/*IntegrationTest.java</include>
<include>**/*LiveTest.java</include>
</includes>
<systemProperties>
<property>
<name>file.encoding</name>
<value>UTF-8</value>
</property>
<property>
<name>jclouds.aws.accesskeyid</name>
<value>${jclouds.aws.accesskeyid}</value>
</property>
<property>
<name>jclouds.aws.secretaccesskey</name>
<value>${jclouds.aws.secretaccesskey}</value>
</property>
</systemProperties>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@ -40,11 +40,6 @@
<url>http://jclouds.googlecode.com/svn/trunk</url>
</scm>
<properties>
<jclouds.s3.httpstream.url>http://apache.multihomed.net/maven/binaries/apache-maven-2.2.0-bin.zip</jclouds.s3.httpstream.url>
<jclouds.s3.httpstream.md5>132bcde2aeca20acb0b16c1c66b74984</jclouds.s3.httpstream.md5>
</properties>
<repositories>
<repository>
<id>googlecode.java-xmlbuilder</id>
@ -53,22 +48,11 @@
</repositories>
<dependencies>
<dependency>
<groupId>xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.jamesmurty.utils</groupId>
<artifactId>java-xmlbuilder</artifactId>
<version>0.3</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>jclouds-httpnio</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@ -37,19 +37,15 @@ 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.S3Bucket;
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.ParseMetadataFromHeaders;
import org.jclouds.aws.s3.functions.ParseObjectFromHeadersAndHttpContent;
import org.jclouds.aws.s3.functions.ReturnNotFoundIfBucketDoesntExist;
import org.jclouds.aws.s3.functions.ReturnNotFoundIfObjectDoesntExist;
import org.jclouds.aws.s3.functions.ReturnS3BucketNotFoundOn404;
import org.jclouds.aws.s3.functions.ReturnS3ObjectMetadataNotFoundOn404;
import org.jclouds.aws.s3.functions.ReturnS3ObjectNotFoundOn404;
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.functions.S3ObjectKey;
import org.jclouds.aws.s3.options.CopyObjectOptions;
import org.jclouds.aws.s3.options.ListBucketOptions;
import org.jclouds.aws.s3.options.PutBucketOptions;
@ -58,22 +54,29 @@ import org.jclouds.aws.s3.xml.AccessControlListHandler;
import org.jclouds.aws.s3.xml.CopyObjectHandler;
import org.jclouds.aws.s3.xml.ListAllMyBucketsHandler;
import org.jclouds.aws.s3.xml.ListBucketHandler;
import org.jclouds.blobstore.BlobStore;
import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.functions.BlobKey;
import org.jclouds.blobstore.functions.ThrowContainerNotFoundOn404;
import org.jclouds.blobstore.functions.ThrowKeyNotFoundOn404;
import org.jclouds.http.functions.ParseETagHeader;
import org.jclouds.http.functions.ReturnFalseOn404;
import org.jclouds.http.options.GetOptions;
import org.jclouds.rest.EntityParam;
import org.jclouds.rest.ExceptionParser;
import org.jclouds.rest.Header;
import org.jclouds.rest.Headers;
import org.jclouds.rest.HostPrefixParam;
import org.jclouds.rest.HttpRequestOptionsBinder;
import org.jclouds.rest.ParamParser;
import org.jclouds.rest.Query;
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 com.google.inject.internal.Nullable;
/**
* Provides access to S3 via their REST API.
* <p/>
@ -87,34 +90,16 @@ import org.jclouds.rest.XMLResponseParser;
@VirtualHost
@SkipEncoding('/')
@RequestFilters(RequestAuthorizeSignature.class)
public interface S3Connection {
public interface S3BlobStore extends BlobStore<BucketMetadata, ObjectMetadata, S3Object> {
/**
* Retrieves the S3Object associated with the Key or {@link S3Object#NOT_FOUND} if not available;
* 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.
*
* @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.
* @see <a
* href="http://docs.amazonwebservices.com/AmazonS3/2006-03-01/index.html?RESTObjectGET.html"
* />
*/
@GET
@Path("{key}")
@ExceptionParser(ReturnS3ObjectNotFoundOn404.class)
@ResponseParser(ParseObjectFromHeadersAndHttpContent.class)
Future<S3Object> getObject(@HostPrefixParam String bucketName, @PathParam("key") String key);
/**
* Like {@link #getObject(String, String)} except you can use {@link GetObjectOptions} to control
* delivery.
* <p />
* This command allows you to specify {@link GetObjectOptions} to control delivery of content.
*
@ -125,24 +110,35 @@ public interface S3Connection {
* <li>{@link GetObjectOptions#tail}</li>
* </ul>
*
* @return S3Object containing data relevant to the <code>options</options> specified or
* @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 #getBlob(String, String)
* @see GetObjectOptions
*/
@GET
@Path("{key}")
@ExceptionParser(ReturnS3ObjectNotFoundOn404.class)
@ExceptionParser(ThrowKeyNotFoundOn404.class)
@ResponseParser(ParseObjectFromHeadersAndHttpContent.class)
Future<S3Object> getObject(@HostPrefixParam String bucketName, @PathParam("key") String key,
Future<S3Object> getBlob(@HostPrefixParam String bucketName, @PathParam("key") String key);
@GET
@Path("{key}")
@ExceptionParser(ThrowKeyNotFoundOn404.class)
@ResponseParser(ParseObjectFromHeadersAndHttpContent.class)
Future<S3Object> getBlob(@HostPrefixParam String bucketName, @PathParam("key") String key,
GetOptions options);
/**
* Retrieves the {@link org.jclouds.aws.s3.domain.S3Object.Metadata metadata} of the object
* associated with the key or {@link org.jclouds.aws.s3.domain.S3Object.Metadata#NOT_FOUND} if
* not available.
* 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,
@ -155,17 +151,17 @@ public interface S3Connection {
* @param key
* unique key in the s3Bucket identifying the object
* @return metadata associated with the key or
* {@link org.jclouds.aws.s3.domain.S3Object.Metadata#NOT_FOUND} if not present;
* @see #getObject(String, String)
* {@link org.jclouds.aws.s3.domain.ObjectMetadata#NOT_FOUND} if not present;
* @see #getBlob(String, String)
* @see <a
* href="http://docs.amazonwebservices.com/AmazonS3/2006-03-01/index.html?RESTObjectHEAD.html"
* />
*/
@HEAD
@Path("{key}")
@ExceptionParser(ReturnS3ObjectMetadataNotFoundOn404.class)
@ResponseParser(ParseMetadataFromHeaders.class)
S3Object.Metadata headObject(@HostPrefixParam String bucketName, @PathParam("key") String key);
@ExceptionParser(ThrowKeyNotFoundOn404.class)
@ResponseParser(ParseObjectMetadataFromHeaders.class)
ObjectMetadata blobMetadata(@HostPrefixParam String bucketName, @PathParam("key") String key);
/**
* Removes the object and metadata associated with the key.
@ -186,7 +182,7 @@ public interface S3Connection {
*/
@DELETE
@Path("{key}")
Future<Boolean> deleteObject(@HostPrefixParam String bucketName, @PathParam("key") String key);
Future<Boolean> removeBlob(@HostPrefixParam String bucketName, @PathParam("key") String key);
/**
* Store data by creating or overwriting an object.
@ -201,7 +197,11 @@ public interface S3Connection {
* 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"
@ -210,50 +210,23 @@ public interface S3Connection {
@PUT
@Path("{key}")
@ResponseParser(ParseETagHeader.class)
Future<byte[]> putObject(
Future<byte[]> putBlob(
@HostPrefixParam String bucketName,
@PathParam("key") @ParamParser(S3ObjectKey.class) @EntityParam(S3ObjectBinder.class) S3Object object);
@PathParam("key") @ParamParser(BlobKey.class) @EntityParam(S3ObjectBinder.class) S3Object object,
PutObjectOptions options);
/**
* Like {@link #putObject(String, S3Object)} except you can use {@link PutObjectOptions} to
* control delivery of content.
*
*
* @param options
* options for creating the object
* @throws org.jclouds.http.HttpResponseException
* if the conditions requested set are not satisfied by the object on the server.
* @see S3Connection#putObject(String, S3Object)
* @see PutObjectOptions
* @see <a
* href="http://docs.amazonwebservices.com/AmazonS3/2006-03-01/index.html?RESTObjectPUT.html"
* />
*/
@PUT
@Path("{key}")
@ResponseParser(ParseETagHeader.class)
Future<byte[]> putObject(
Future<byte[]> putBlob(
@HostPrefixParam String bucketName,
@PathParam("key") @ParamParser(S3ObjectKey.class) @EntityParam(S3ObjectBinder.class) S3Object object,
PutObjectOptions options);
@PathParam("key") @ParamParser(BlobKey.class) @EntityParam(S3ObjectBinder.class) S3Object object);
/**
* Create and name your own bucket in which to store your objects.
*
* @see <a
* href="http://docs.amazonwebservices.com/AmazonS3/2006-03-01/index.html?RESTBucketPUT.html"
* />
* @return true, if the bucket was created or already exists
* @see org.jclouds.aws.s3.commands.PutBucket
*/
@PUT
@Path("/")
@ExceptionParser(ReturnTrueIfBucketAlreadyOwnedByYou.class)
Future<Boolean> putBucketIfNotExists(@HostPrefixParam String bucketName);
/**
* Like {@link #putBucketIfNotExists(String)} except that you can use {@link PutBucketOptions} to
* create the bucket in EU.
* <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
@ -262,6 +235,8 @@ public interface S3Connection {
*
* @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"
@ -271,8 +246,13 @@ public interface S3Connection {
@PUT
@Path("/")
@ExceptionParser(ReturnTrueIfBucketAlreadyOwnedByYou.class)
Future<Boolean> putBucketIfNotExists(@HostPrefixParam String bucketName,
@EntityParam(HttpRequestOptionsBinder.class) PutBucketOptions options);
Future<Boolean> createContainer(@HostPrefixParam String bucketName,
@Nullable @EntityParam(HttpRequestOptionsBinder.class) PutBucketOptions options);
@PUT
@Path("/")
@ExceptionParser(ReturnTrueIfBucketAlreadyOwnedByYou.class)
Future<Boolean> createContainer(@HostPrefixParam String bucketName);
/**
* Deletes the bucket, if it is empty.
@ -294,7 +274,59 @@ public interface S3Connection {
@DELETE
@Path("/")
@ExceptionParser(ReturnTrueOn404FalseIfNotEmpty.class)
boolean deleteBucketIfEmpty(@HostPrefixParam String bucketName);
Future<Boolean> deleteContainer(@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 containerExists(@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> listBlobs(@HostPrefixParam String bucketName,
@Nullable ListBucketOptions options);
@GET
@Path("/")
@XMLResponseParser(ListBucketHandler.class)
Future<ListBucketResponse> listBlobs(@HostPrefixParam String bucketName);
/**
* 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("/")
List<BucketMetadata> listContainers();
/**
* Copies one object to another bucket, retaining UserMetadata from the source. The destination
@ -313,97 +345,30 @@ public interface S3Connection {
* @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}")
@Header(key = "x-amz-copy-source", value = "/{sourceBucket}/{sourceObject}")
@Headers(keys = "x-amz-copy-source", values = "/{sourceBucket}/{sourceObject}")
@XMLResponseParser(CopyObjectHandler.class)
Future<S3Object.Metadata> copyObject(@PathParam("sourceBucket") String sourceBucket,
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);
/**
* Like {@link #putObject(String, S3Object)} except you can use {@link PutObjectOptions} to
* specify an alternate {@link org.jclouds.aws.s3.domain.CannedAccessPolicy acl}.
*
* @param options
* options for creating the object
* @throws org.jclouds.http.HttpResponseException
* if the conditions requested set are not satisfied by the object on the server.
* @see S3Connection#putObject(String, S3Object)
* @see PutObjectOptions
*/
@PUT
@Path("{destinationObject}")
@Header(key = "x-amz-copy-source", value = "/{sourceBucket}/{sourceObject}")
@XMLResponseParser(CopyObjectHandler.class)
Future<S3Object.Metadata> copyObject(@PathParam("sourceBucket") String sourceBucket,
@PathParam("sourceObject") String sourceObject,
@HostPrefixParam String destinationBucket,
@PathParam("destinationObject") String destinationObject, CopyObjectOptions options);
/**
* Issues a HEAD command to determine if the bucket exists or not.
*/
@HEAD
@Path("/")
@Query(key = "max-keys", value = "0")
@ExceptionParser(ReturnFalseOn404.class)
boolean bucketExists(@HostPrefixParam String bucketName);
/**
* Retrieve a complete <code>S3Bucket</code> listing. A GET request operation using a bucket URI
* lists information about the objects in the bucket.
* <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 S3Bucket#NOT_FOUND} if not present.
* @see <a
* href="http://docs.amazonwebservices.com/AmazonS3/2006-03-01/index.html?RESTBucketGET.html"
* />
*/
@GET
@Path("/")
@XMLResponseParser(ListBucketHandler.class)
@ExceptionParser(ReturnS3BucketNotFoundOn404.class)
Future<S3Bucket> listBucket(@HostPrefixParam String bucketName);
/**
* Like {@link #listBucket(String)} except you can use {@link ListBucketOptions} to control the
* amount of S3Objects to return.
*
* @return S3Bucket containing a subset of {@link org.jclouds.aws.s3.domain.S3Object.Metadata}
* depending on
* <code>options</options> specified or {@link S3Bucket#NOT_FOUND} if not present.
* @see #listBucket(String)
* @see ListBucketOptions
*/
@GET
@Path("/")
@XMLResponseParser(ListBucketHandler.class)
Future<S3Bucket> 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("/")
List<S3Bucket.Metadata> listOwnedBuckets();
/**
*
* A GET request operation directed at an object or bucket URI with the "acl" parameter retrieves
@ -416,11 +381,11 @@ public interface S3Connection {
* @see <a href="http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAccessPolicy.html"/>
*/
@GET
@Query(key = "acl")
@QueryParams(keys = "acl")
@XMLResponseParser(AccessControlListHandler.class)
@ExceptionParser(ReturnNotFoundIfBucketDoesntExist.class)
@ExceptionParser(ThrowContainerNotFoundOn404.class)
@Path("/")
Future<AccessControlList> getBucketACL(@HostPrefixParam String bucketName);
Future<AccessControlList> getContainerACL(@HostPrefixParam String bucketName);
/**
* Update a bucket's Access Control List settings.
@ -441,8 +406,8 @@ public interface S3Connection {
*/
@PUT
@Path("/")
@Query(key = "acl")
Future<Boolean> putBucketACL(@HostPrefixParam String bucketName,
@QueryParams(keys = "acl")
Future<Boolean> putContainerACL(@HostPrefixParam String bucketName,
@EntityParam(AccessControlListBinder.class) AccessControlList acl);
/**
@ -456,11 +421,11 @@ public interface S3Connection {
* @see <a href="http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAccessPolicy.html"/>
*/
@GET
@Query(key = "acl")
@QueryParams(keys = "acl")
@Path("{key}")
@XMLResponseParser(AccessControlListHandler.class)
@ExceptionParser(ReturnNotFoundIfObjectDoesntExist.class)
Future<AccessControlList> getObjectACL(@HostPrefixParam String bucketName,
@ExceptionParser(ThrowKeyNotFoundOn404.class)
Future<AccessControlList> getBlobACL(@HostPrefixParam String bucketName,
@PathParam("key") String key);
/**
@ -483,9 +448,9 @@ public interface S3Connection {
* @see <a href="http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAccessPolicy.html"/>
*/
@PUT
@Query(key = "acl")
@QueryParams(keys = "acl")
@Path("{key}")
Future<Boolean> putObjectACL(@HostPrefixParam String bucketName, @PathParam("key") String key,
Future<Boolean> putBlobACL(@HostPrefixParam String bucketName, @PathParam("key") String key,
@EntityParam(AccessControlListBinder.class) AccessControlList acl);
}

View File

@ -23,8 +23,9 @@
*/
package org.jclouds.aws.s3;
import org.jclouds.cloud.CloudContext;
import org.jclouds.objectstore.ObjectStoreContext;
import org.jclouds.aws.s3.domain.ObjectMetadata;
import org.jclouds.aws.s3.domain.S3Object;
import org.jclouds.blobstore.BlobStoreContext;
/**
* Represents an authenticated context to S3.
@ -33,19 +34,11 @@ import org.jclouds.objectstore.ObjectStoreContext;
* release resources.
*
*
* @see S3Connection
* @see S3InputStreamMap
* @see S3ObjectMap
* @see S3BlobStore
* @see BlobStoreContext
* @author Adrian Cole
*
*/
public interface S3Context extends CloudContext<S3Connection>, ObjectStoreContext<S3InputStreamMap> {
/**
* Creates a <code>Map<String,S3Object></code> view of the specified bucket.
*
* @param bucket
*/
S3ObjectMap createS3ObjectMap(String bucket);
public interface S3Context extends BlobStoreContext<S3BlobStore, ObjectMetadata, S3Object> {
}

View File

@ -26,6 +26,7 @@ package org.jclouds.aws.s3;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.jclouds.aws.reference.AWSConstants.PROPERTY_AWS_ACCESSKEYID;
import static org.jclouds.aws.reference.AWSConstants.PROPERTY_AWS_SECRETACCESSKEY;
import static org.jclouds.blobstore.reference.BlobStoreConstants.PROPERTY_USER_METADATA_PREFIX;
import static org.jclouds.http.HttpConstants.PROPERTY_HTTP_ADDRESS;
import static org.jclouds.http.HttpConstants.PROPERTY_HTTP_MAX_REDIRECTS;
import static org.jclouds.http.HttpConstants.PROPERTY_HTTP_MAX_RETRIES;
@ -63,7 +64,7 @@ import com.google.inject.Module;
* @author Adrian Cole, Andrew Newdigate
* @see S3Context
*/
public class S3ContextBuilder extends CloudContextBuilder<S3Connection, S3Context> {
public class S3ContextBuilder extends CloudContextBuilder<S3Context> {
public S3ContextBuilder(Properties props) {
super(props);
@ -73,6 +74,7 @@ public class S3ContextBuilder extends CloudContextBuilder<S3Connection, S3Contex
Properties properties = new Properties();
properties.setProperty(PROPERTY_HTTP_ADDRESS, "s3.amazonaws.com");
properties.setProperty(PROPERTY_USER_METADATA_PREFIX, "x-amz-meta-");
properties.setProperty(PROPERTY_HTTP_SECURE, "true");
properties.setProperty(PROPERTY_SAX_DEBUG, "false");
properties.setProperty(PROPERTY_HTTP_MAX_RETRIES, "5");

View File

@ -24,50 +24,45 @@
package org.jclouds.aws.s3.binders;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.jclouds.blobstore.reference.BlobStoreConstants.PROPERTY_USER_METADATA_PREFIX;
import javax.ws.rs.core.HttpHeaders;
import org.jclouds.aws.s3.domain.S3Object;
import org.jclouds.blobstore.binders.BlobBinder;
import org.jclouds.blobstore.domain.Blob;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpUtils;
import org.jclouds.rest.EntityBinder;
public class S3ObjectBinder implements EntityBinder {
import com.google.inject.Inject;
import com.google.inject.name.Named;
public class S3ObjectBinder extends BlobBinder {
@Inject
public S3ObjectBinder(@Named(PROPERTY_USER_METADATA_PREFIX) String metadataPrefix) {
super(metadataPrefix);
}
public void addEntityToRequest(Object entity, HttpRequest request) {
S3Object object = (S3Object) entity;
Blob<?> object = (Blob<?>) entity;
checkArgument(object.getMetadata().getSize() >= 0, "size must be set");
request.setEntity(checkNotNull(object.getData(), "object.getContent()"));
request.getHeaders()
.put(
HttpHeaders.CONTENT_TYPE,
checkNotNull(object.getMetadata().getContentType(),
"object.metadata.contentType()"));
request.getHeaders().put(HttpHeaders.CONTENT_LENGTH, object.getMetadata().getSize() + "");
if (object.getMetadata().getCacheControl() != null) {
request.getHeaders()
.put(HttpHeaders.CACHE_CONTROL, object.getMetadata().getCacheControl());
if (object instanceof S3Object) {
S3Object s3Object = (S3Object) object;
if (s3Object.getMetadata().getCacheControl() != null) {
request.getHeaders().put(HttpHeaders.CACHE_CONTROL,
s3Object.getMetadata().getCacheControl());
}
if (object.getMetadata().getContentDisposition() != null) {
if (s3Object.getMetadata().getContentDisposition() != null) {
request.getHeaders().put("Content-Disposition",
object.getMetadata().getContentDisposition());
s3Object.getMetadata().getContentDisposition());
}
if (object.getMetadata().getContentEncoding() != null) {
if (s3Object.getMetadata().getContentEncoding() != null) {
request.getHeaders().put(HttpHeaders.CONTENT_ENCODING,
object.getMetadata().getContentEncoding());
s3Object.getMetadata().getContentEncoding());
}
if (object.getMetadata().getETag() != null) {
request.getHeaders().put("Content-MD5",
HttpUtils.toBase64String(object.getMetadata().getETag()));
}
request.getHeaders().putAll(object.getMetadata().getUserMetadata());
super.addEntityToRequest(entity, request);
}
}

View File

@ -28,7 +28,7 @@ import java.net.URI;
import javax.annotation.Resource;
import org.jclouds.aws.s3.S3Connection;
import org.jclouds.aws.s3.S3BlobStore;
import org.jclouds.aws.s3.filters.RequestAuthorizeSignature;
import org.jclouds.aws.s3.handlers.AWSClientErrorRetryHandler;
import org.jclouds.aws.s3.handlers.AWSRedirectionRetryHandler;
@ -85,8 +85,8 @@ public class RestS3ConnectionModule extends AbstractModule {
@Provides
@Singleton
protected S3Connection provideS3Connection(URI uri, RestClientFactory factory) {
return factory.create(uri, S3Connection.class);
protected S3BlobStore provideS3Connection(URI uri, RestClientFactory factory) {
return factory.create(uri, S3BlobStore.class);
}
protected void bindErrorHandlers() {

View File

@ -23,31 +23,47 @@
*/
package org.jclouds.aws.s3.config;
import org.jclouds.aws.s3.S3Connection;
import org.jclouds.aws.s3.S3BlobStore;
import org.jclouds.aws.s3.S3Context;
import org.jclouds.aws.s3.domain.ObjectMetadata;
import org.jclouds.aws.s3.domain.S3Object;
import org.jclouds.aws.s3.internal.GuiceS3Context;
import org.jclouds.aws.s3.internal.LiveS3InputStreamMap;
import org.jclouds.aws.s3.internal.LiveS3ObjectMap;
import org.jclouds.blobstore.functions.ParseBlobFromHeadersAndHttpContent.BlobFactory;
import org.jclouds.blobstore.functions.ParseBlobMetadataFromHeaders.BlobMetadataFactory;
import com.google.inject.AbstractModule;
import com.google.inject.TypeLiteral;
import com.google.inject.assistedinject.FactoryProvider;
/**
* Configures the {@link S3Context}; requires {@link S3Connection} bound.
* Configures the {@link S3Context}; requires {@link S3BlobStore} bound.
*
* @author Adrian Cole
*/
public class S3ContextModule extends AbstractModule {
protected final TypeLiteral<BlobMetadataFactory<ObjectMetadata>> objectMetadataFactoryLiteral = new TypeLiteral<BlobMetadataFactory<ObjectMetadata>>() {
};
protected final TypeLiteral<BlobFactory<ObjectMetadata, S3Object>> objectFactoryLiteral = new TypeLiteral<BlobFactory<ObjectMetadata, S3Object>>() {
};
@Override
protected void configure() {
this.requireBinding(S3Connection.class);
this.requireBinding(S3BlobStore.class);
bind(GuiceS3Context.S3ObjectMapFactory.class).toProvider(
FactoryProvider.newFactory(GuiceS3Context.S3ObjectMapFactory.class,
LiveS3ObjectMap.class));
bind(GuiceS3Context.S3InputStreamMapFactory.class).toProvider(
FactoryProvider.newFactory(GuiceS3Context.S3InputStreamMapFactory.class,
LiveS3InputStreamMap.class));
bind(objectMetadataFactoryLiteral).toProvider(
FactoryProvider.newFactory(objectMetadataFactoryLiteral,
new TypeLiteral<ObjectMetadata>() {
}));
bind(objectFactoryLiteral).toProvider(
FactoryProvider.newFactory(objectFactoryLiteral, new TypeLiteral<S3Object>() {
}));
bind(S3Context.class).to(GuiceS3Context.class);
}

View File

@ -31,7 +31,6 @@ import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
@ -50,7 +49,6 @@ import com.google.common.collect.Collections2;
* @see <a href="http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAccessPolicy.html"/>
*/
public class AccessControlList {
public static final AccessControlList NOT_FOUND = new AccessControlList();
private CanonicalUser owner;
private final List<Grant> grants = new ArrayList<Grant>();

View File

@ -0,0 +1,72 @@
/**
*
* 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.domain;
import java.util.List;
import java.util.SortedSet;
/**
*
* @author Adrian Cole
*
*/
public class ArrayListBucketResponse extends org.jclouds.rest.ArrayBoundedList<ObjectMetadata>
implements ListBucketResponse {
/** The serialVersionUID */
private static final long serialVersionUID = -4475709781001190244L;
private final String bucketName;
private final String delimiter;
private final SortedSet<String> commonPrefixes;
private final boolean truncated;
public ArrayListBucketResponse(String bucketName, List<ObjectMetadata> contents, String prefix,
String marker, int maxResults, String delimiter, boolean isTruncated,
SortedSet<String> commonPrefixes) {
super(contents, prefix, marker, maxResults);
this.delimiter = delimiter;
this.bucketName = bucketName;
this.commonPrefixes = commonPrefixes;
this.truncated = isTruncated;
}
/**
* {@inheritDoc}
*/
public SortedSet<String> getCommonPrefixes() {
return commonPrefixes;
}
public String getBucketName() {
return bucketName;
}
public String getDelimiter() {
return delimiter;
}
public boolean isTruncated() {
return truncated;
}
}

View File

@ -0,0 +1,132 @@
/**
*
* 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.domain;
import org.jclouds.blobstore.domain.ContainerMetadata;
import org.joda.time.DateTime;
/**
* System metadata of the S3Bucket
*
* @author Adrian Cole
*/
public class BucketMetadata extends ContainerMetadata {
protected DateTime creationDate;
public DateTime getCreationDate() {
return creationDate;
}
public void setCreationDate(DateTime creationDate) {
this.creationDate = creationDate;
}
/**
* Location constraint of the bucket.
*
* @author Adrian Cole
* @see <a href=
* "http://docs.amazonwebservices.com/AmazonS3/latest/RESTBucketLocationGET.html" />
*/
public static enum LocationConstraint {
EU
}
private CanonicalUser canonicalUser;
public BucketMetadata(String name) {
super(name);
}
public BucketMetadata() {
super();
}
/**
* To comply with Amazon S3 requirements, bucket names must:
* <p/>
* Contain lowercase letters, numbers, periods (.), underscores (_), and dashes (-)
* <p/>
* Start with a number or letter
* <p/>
* Be between 3 and 255 characters long
* <p/>
* Not be in an IP address style (e.g., "192.168.5.4")
*/
@Override
public void setName(String name) {
// note that we cannot enforce this, as invalid buckets may already exist
super.setName(name);
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("Metadata [canonicalUser=").append(canonicalUser).append("]");
return builder.toString();
}
public String getName() {
return name;
}
/**
* Every bucket and object in Amazon S3 has an owner, the user that created the bucket or
* object. The owner of a bucket or object cannot be changed. However, if the object is
* overwritten by another user (deleted and rewritten), the new object will have a new owner.
*/
public CanonicalUser getOwner() {
return canonicalUser;
}
public void setOwner(CanonicalUser canonicalUser) {
this.canonicalUser = canonicalUser;
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((canonicalUser == null) ? 0 : canonicalUser.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!super.equals(obj))
return false;
if (getClass() != obj.getClass())
return false;
BucketMetadata other = (BucketMetadata) obj;
if (canonicalUser == null) {
if (other.canonicalUser != null)
return false;
} else if (!canonicalUser.equals(other.canonicalUser))
return false;
return true;
}
}

View File

@ -54,8 +54,8 @@ public class CanonicalUser {
/**
* To locate the CanonicalUser ID for a user, the user must perform the
* {@link org.jclouds.aws.s3.S3Connection#listBucket(String)} and retrieve
* {@link S3Bucket.Metadata#getOwner()}
* {@link org.jclouds.aws.s3.S3BlobStore#listBlobs(String)} and retrieve
* {@link BucketMetadata#getOwner()}
*/
public String getId() {
return id;

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.aws.s3.domain;
import java.util.SortedSet;
/**
* A container that provides namespace, access control and aggregation of {@link S3Object}s
* <p/>
* <p/>
* Every object stored in Amazon S3 is contained in a bucket. Buckets partition the namespace of
* objects stored in Amazon S3 at the top level. Within a bucket, you can use any names for your
* objects, but bucket names must be unique across all of Amazon S3.
* <p/>
* Buckets are similar to Internet domain names. Just as Amazon is the only owner of the domain name
* Amazon.com, only one person or organization can own a bucket within Amazon S3. Once you create a
* uniquely named bucket in Amazon S3, you can organize and name the objects within the bucket in
* any way you like and the bucket will remain yours for as long as you like and as long as you have
* the Amazon S3 account.
* <p/>
* The similarities between buckets and domain names is not a coincidenceÑthere is a direct mapping
* between Amazon S3 buckets and subdomains of s3.amazonaws.com. Objects stored in Amazon S3 are
* addressable using the REST API under the domain bucketname.s3.amazonaws.com. For example, if the
* object homepage.html?is stored in the Amazon S3 bucket mybucket its address would be
* http://mybucket.s3.amazonaws.com/homepage.html?
*
* @author Adrian Cole
* @see <a href="http://docs.amazonwebservices.com/AmazonS3/2006-03-01/index.html" />
*/
public interface ListBucketResponse extends org.jclouds.rest.BoundedList<ObjectMetadata> {
/**
* Example:
* <p/>
* if the following keys are in the bucket
* <p/>
* a/1/a<br/>
* a/1/b<br/>
* a/2/a<br/>
* a/2/b<br/>
* <p/>
* and prefix is set to <code>a/</code> and delimiter is set to <code>/</code> then
* commonprefixes would return 1,2
*
* @see org.jclouds.aws.s3.options.ListBucketOptions#getPrefix()
*/
public SortedSet<String> getCommonPrefixes();
public String getBucketName();
public String getDelimiter();
public boolean isTruncated();
}

View File

@ -0,0 +1,208 @@
/**
*
* 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.domain;
import java.io.Serializable;
import java.util.Arrays;
import org.jclouds.blobstore.domain.BlobMetadata;
/**
* /** Amazon S3 is designed to store objects. Objects are stored in {@link S3BucketListing buckets}
* and consist of a {@link org.jclouds.aws.s3.domain.S3Object#getData() value}, a
* {@link S3Object#getKey key}, {@link ObjectMetadata#getUserMetadata() metadata}, and an access
* control policy.
*
* @author Adrian Cole
* @see <a href="http://docs.amazonwebservices.com/AmazonS3/2006-03-01/index.html?UsingObjects.html"
*
* @see <a href= "http://docs.amazonwebservices.com/AmazonS3/2006-03-01/UsingMetadata.html" />
*/
public class ObjectMetadata extends BlobMetadata implements Serializable {
/** The serialVersionUID */
private static final long serialVersionUID = -4415449798024051115L;
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("Metadata [accessControlList=").append(accessControlList).append(
", cacheControl=").append(cacheControl).append(", dataDisposition=").append(
dataDisposition).append(", owner=").append(owner).append(", storageClass=").append(
storageClass).append(", allHeaders=").append(allHeaders).append(", dataEncoding=")
.append(dataEncoding).append(", dataType=").append(dataType).append(", eTag=")
.append(Arrays.toString(eTag)).append(", key=").append(key)
.append(", lastModified=").append(lastModified).append(", size=").append(size)
.append(", userMetadata=").append(userMetadata).append("]");
return builder.toString();
}
private String cacheControl;
private String dataDisposition;
private AccessControlList accessControlList;
// only parsed on list
private CanonicalUser owner = null;
private String storageClass = null;
protected String dataEncoding;
public ObjectMetadata() {
super();
}
/**
* @param key
* @see #getKey()
*/
public ObjectMetadata(String key) {
super(key);
}
public void setOwner(CanonicalUser owner) {
this.owner = owner;
}
/**
* Every bucket and object in Amazon S3 has an owner, the user that created the bucket or object.
* The owner of a bucket or object cannot be changed. However, if the object is overwritten by
* another user (deleted and rewritten), the new object will have a new owner.
*/
public CanonicalUser getOwner() {
return owner;
}
public void setStorageClass(String storageClass) {
this.storageClass = storageClass;
}
/**
* Currently defaults to 'STANDARD' and not used.
*/
public String getStorageClass() {
return storageClass;
}
public void setCacheControl(String cacheControl) {
this.cacheControl = cacheControl;
}
/**
* Can be used to specify caching behavior along the request/reply chain.
*
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html?sec14.9.
*/
public String getCacheControl() {
return cacheControl;
}
public void setContentDisposition(String dataDisposition) {
this.dataDisposition = dataDisposition;
}
/**
* Specifies presentational information for the object.
*
* @see <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html?sec19.5.1."/>
*/
public String getContentDisposition() {
return dataDisposition;
}
public void setAccessControlList(AccessControlList acl) {
this.accessControlList = acl;
}
public AccessControlList getAccessControlList() {
return this.accessControlList;
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((accessControlList == null) ? 0 : accessControlList.hashCode());
result = prime * result + ((cacheControl == null) ? 0 : cacheControl.hashCode());
result = prime * result + ((dataDisposition == null) ? 0 : dataDisposition.hashCode());
result = prime * result + ((owner == null) ? 0 : owner.hashCode());
result = prime * result + ((storageClass == null) ? 0 : storageClass.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!super.equals(obj))
return false;
if (getClass() != obj.getClass())
return false;
ObjectMetadata other = (ObjectMetadata) obj;
if (accessControlList == null) {
if (other.accessControlList != null)
return false;
} else if (!accessControlList.equals(other.accessControlList))
return false;
if (cacheControl == null) {
if (other.cacheControl != null)
return false;
} else if (!cacheControl.equals(other.cacheControl))
return false;
if (dataDisposition == null) {
if (other.dataDisposition != null)
return false;
} else if (!dataDisposition.equals(other.dataDisposition))
return false;
if (owner == null) {
if (other.owner != null)
return false;
} else if (!owner.equals(other.owner))
return false;
if (storageClass == null) {
if (other.storageClass != null)
return false;
} else if (!storageClass.equals(other.storageClass))
return false;
return true;
}
public int compareTo(ObjectMetadata o) {
return (this == o) ? 0 : getKey().compareTo(o.getKey());
}
public void setContentEncoding(String dataEncoding) {
this.dataEncoding = dataEncoding;
}
/**
* Specifies what content encodings have been applied to the object and thus what decoding
* mechanisms must be applied in order to obtain the media-type referenced by the Content-Type
* header field.
*
* @see <a href= "http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html?sec14.11" />
*/
public String getContentEncoding() {
return dataEncoding;
}
}

View File

@ -1,341 +0,0 @@
/**
*
* 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.domain;
import static com.google.common.base.Preconditions.checkNotNull;
import org.joda.time.DateTime;
import java.util.SortedSet;
import java.util.TreeSet;
/**
* A container that provides namespace, access control and aggregation of {@link S3Object}s
* <p/>
* <p/>
* Every object stored in Amazon S3 is contained in a bucket. Buckets partition the namespace of
* objects stored in Amazon S3 at the top level. Within a bucket, you can use any names for your
* objects, but bucket names must be unique across all of Amazon S3.
* <p/>
* Buckets are similar to Internet domain names. Just as Amazon is the only owner of the domain name
* Amazon.com, only one person or organization can own a bucket within Amazon S3. Once you create a
* uniquely named bucket in Amazon S3, you can organize and name the objects within the bucket in
* any way you like and the bucket will remain yours for as long as you like and as long as you have
* the Amazon S3 account.
* <p/>
* The similarities between buckets and domain names is not a coincidenceÑthere is a direct mapping
* between Amazon S3 buckets and subdomains of s3.amazonaws.com. Objects stored in Amazon S3 are
* addressable using the REST API under the domain bucketname.s3.amazonaws.com. For example, if the
* object homepage.html?is stored in the Amazon S3 bucket mybucket its address would be
* http://mybucket.s3.amazonaws.com/homepage.html?
*
* @author Adrian Cole
* @see <a href="http://docs.amazonwebservices.com/AmazonS3/2006-03-01/index.html" />
*/
public class S3Bucket {
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append("S3Bucket");
sb.append("{metadata=").append(metadata);
sb.append(", isTruncated=").append(isTruncated);
sb.append('}');
return sb.toString();
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (!(o instanceof S3Bucket))
return false;
S3Bucket s3Bucket = (S3Bucket) o;
if (isTruncated != s3Bucket.isTruncated)
return false;
if (!metadata.equals(s3Bucket.metadata))
return false;
if (objects != null ? !objects.equals(s3Bucket.objects) : s3Bucket.objects != null)
return false;
return true;
}
@Override
public int hashCode() {
int result = objects != null ? objects.hashCode() : 0;
result = 31 * result + metadata.hashCode();
result = 31 * result + (isTruncated ? 1 : 0);
return result;
}
/**
* System metadata of the S3Bucket
*
* @author Adrian Cole
*/
public static class Metadata {
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append("Metadata");
sb.append("{name='").append(name).append('\'');
sb.append(", creationDate=").append(creationDate);
sb.append(", canonicalUser=").append(canonicalUser);
sb.append('}');
return sb.toString();
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (!(o instanceof Metadata))
return false;
Metadata metadata = (Metadata) o;
if (canonicalUser != null ? !canonicalUser.equals(metadata.canonicalUser)
: metadata.canonicalUser != null)
return false;
if (!name.equals(metadata.name))
return false;
return true;
}
@Override
public int hashCode() {
int result = name.hashCode();
result = 31 * result + (canonicalUser != null ? canonicalUser.hashCode() : 0);
return result;
}
/**
* Location constraint of the bucket.
*
* @author Adrian Cole
* @see <a href=
* "http://docs.amazonwebservices.com/AmazonS3/latest/RESTBucketLocationGET.html" />
*/
public static enum LocationConstraint {
EU
}
private String name;
private DateTime creationDate;
private CanonicalUser canonicalUser;
/**
* @see #getName()
*/
public Metadata(String name) {
this.name = checkNotNull(name, "name");
}
public Metadata() {
super();
}
/**
* To comply with Amazon S3 requirements, bucket names must:
* <p/>
* Contain lowercase letters, numbers, periods (.), underscores (_), and dashes (-)
* <p/>
* Start with a number or letter
* <p/>
* Be between 3 and 255 characters long
* <p/>
* Not be in an IP address style (e.g., "192.168.5.4")
*/
public void setName(String name) {
this.name = checkNotNull(name, "name");
}
public String getName() {
return name;
}
public DateTime getCreationDate() {
return creationDate;
}
public void setCreationDate(DateTime creationDate) {
this.creationDate = creationDate;
}
/**
* Every bucket and object in Amazon S3 has an owner, the user that created the bucket or
* object. The owner of a bucket or object cannot be changed. However, if the object is
* overwritten by another user (deleted and rewritten), the new object will have a new owner.
*/
public CanonicalUser getOwner() {
return canonicalUser;
}
public void setOwner(CanonicalUser canonicalUser) {
this.canonicalUser = canonicalUser;
}
}
public static final S3Bucket NOT_FOUND = new S3Bucket("NOT_FOUND");
private SortedSet<S3Object.Metadata> objects = new TreeSet<S3Object.Metadata>();
private SortedSet<String> commonPrefixes = new TreeSet<String>();
private String prefix;
private String marker;
private String delimiter;
private long maxKeys;
private final Metadata metadata;
private boolean isTruncated;
public S3Bucket() {
this.metadata = new Metadata();
}
public S3Bucket(String name) {
this.metadata = new Metadata(name);
}
public String getName() {
return this.metadata.getName();
}
public S3Bucket(Metadata metadata) {
this.metadata = checkNotNull(metadata, "metadata");
}
public void setName(String name) {
this.metadata.setName(name);
}
/**
* @see org.jclouds.aws.s3.S3Connection#listBucket(String)
*/
public SortedSet<S3Object.Metadata> getContents() {
return objects;
}
public long getSize() {
return objects.size();
}
public void setContents(SortedSet<S3Object.Metadata> objects) {
this.objects = objects;
}
/**
* @return true, if the list contains all objects.
*/
public boolean isTruncated() {
return isTruncated;
}
public void setTruncated(boolean truncated) {
isTruncated = truncated;
}
public Metadata getMetadata() {
return metadata;
}
public void setCommonPrefixes(SortedSet<String> commonPrefixes) {
this.commonPrefixes = commonPrefixes;
}
/**
* Example:
* <p/>
* if the following keys are in the bucket
* <p/>
* a/1/a<br/>
* a/1/b<br/>
* a/2/a<br/>
* a/2/b<br/>
* <p/>
* and prefix is set to <code>a/</code> and delimiter is set to <code>/</code> then
* commonprefixes would return 1,2
*
* @see org.jclouds.aws.s3.options.ListBucketOptions#getPrefix()
*/
public SortedSet<String> getCommonPrefixes() {
return commonPrefixes;
}
public void setPrefix(String prefix) {
this.prefix = prefix;
}
/**
* return keys that start with this.
*
* @see org.jclouds.aws.s3.options.ListBucketOptions#getPrefix()
*/
public String getPrefix() {
return prefix;
}
public void setMaxKeys(long maxKeys) {
this.maxKeys = maxKeys;
}
/**
* @return maximum results of the bucket.
* @see org.jclouds.aws.s3.options.ListBucketOptions#getMaxKeys()
*/
public long getMaxKeys() {
return maxKeys;
}
public void setMarker(String marker) {
this.marker = marker;
}
/**
* when set, bucket contains results whose keys are lexigraphically after marker.
*
* @see org.jclouds.aws.s3.options.ListBucketOptions#getMarker()
*/
public String getMarker() {
return marker;
}
public void setDelimiter(String delimiter) {
this.delimiter = delimiter;
}
/**
* when set, bucket results will not contain keys that have text following this delimiter.
* <p/>
* note that delimiter has no effect on prefix. prefix can contain the delimiter many times, or
* not at all. delimiter only restricts after the prefix.
*
* @see org.jclouds.aws.s3.options.ListBucketOptions#getMarker()
*/
public String getDelimiter() {
return delimiter;
}
}

View File

@ -23,22 +23,10 @@
*/
package org.jclouds.aws.s3.domain;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import org.jclouds.blobstore.domain.Blob;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import javax.ws.rs.core.MediaType;
import org.jclouds.http.HttpUtils;
import org.jclouds.http.HttpUtils.ETagInputStreamResult;
import org.joda.time.DateTime;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
/**
* Amazon S3 is designed to store objects. Objects are stored in {@link S3Bucket buckets} and
@ -50,413 +38,23 @@ import com.google.common.collect.Multimap;
* @see <a href="http://docs.amazonwebservices.com/AmazonS3/2006-03-01/index.html?UsingObjects.html"
* />
*/
public class S3Object {
public static final S3Object NOT_FOUND = new S3Object(Metadata.NOT_FOUND);
public class S3Object extends Blob<ObjectMetadata> {
private Object data;
private final Metadata metadata;
private long contentLength = -1;
private String contentRange;
public S3Object(String key) {
this(new Metadata(key));
public S3Object(ObjectMetadata metadata, Object data) {
super(metadata, data);
}
public S3Object(Metadata metadata) {
this.metadata = metadata;
}
public S3Object(Metadata metadata, Object data) {
this(metadata);
setData(data);
@Inject
public S3Object(@Assisted ObjectMetadata metadata) {
super(metadata);
}
public S3Object(String key, Object data) {
this(key);
setData(data);
this(new ObjectMetadata(key), data);
}
/**
* System and user Metadata for the {@link S3Object}.
*
* @author Adrian Cole
* @see <a href= "http://docs.amazonwebservices.com/AmazonS3/2006-03-01/UsingMetadata.html" />
*/
public static class Metadata implements Comparable<Metadata> {
public static final Metadata NOT_FOUND = new Metadata("NOT_FOUND");
// parsed during list, head, or get
private String key;
private byte[] eTag;
private volatile long size = -1;
// only parsed during head or get
private Multimap<String, String> allHeaders = HashMultimap.create();
private Multimap<String, String> userMetadata = HashMultimap.create();
private DateTime lastModified;
private String dataType = MediaType.APPLICATION_OCTET_STREAM;
private String cacheControl;
private String dataDisposition;
private String dataEncoding;
private AccessControlList accessControlList;
// only parsed on list
private CanonicalUser owner = null;
private String storageClass = null;
public Metadata() {
super();
}
/**
* @param key
* @see #getKey()
*/
public Metadata(String key) {
setKey(key);
}
public void setKey(String key) {
checkNotNull(key, "key");
checkArgument(!key.startsWith("/"), "keys cannot start with /");
this.key = key;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append("Metadata");
sb.append("{key='").append(key).append('\'');
sb.append(", lastModified=").append(lastModified);
sb.append(", eTag=").append(
getETag() == null ? "null" : Arrays.asList(getETag()).toString());
sb.append(", size=").append(size);
sb.append(", dataType='").append(dataType).append('\'');
sb.append('}');
return sb.toString();
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (!(o instanceof Metadata))
return false;
Metadata metadata = (Metadata) o;
if (size != metadata.size)
return false;
if (dataType != null ? !dataType.equals(metadata.dataType) : metadata.dataType != null)
return false;
if (!key.equals(metadata.key))
return false;
if (lastModified != null ? !lastModified.equals(metadata.lastModified)
: metadata.lastModified != null)
return false;
if (!Arrays.equals(getETag(), metadata.getETag()))
return false;
return true;
}
@Override
public int hashCode() {
int result = key.hashCode();
result = 31 * result + (lastModified != null ? lastModified.hashCode() : 0);
result = 31 * result + (getETag() != null ? Arrays.hashCode(getETag()) : 0);
result = 31 * result + (int) (size ^ (size >>> 32));
result = 31 * result + (dataType != null ? dataType.hashCode() : 0);
return result;
}
/**
* The key is the handle that you assign to an object that allows you retrieve it later. A key
* is a sequence of Unicode characters whose UTF-8 encoding is at most 1024 bytes long. Each
* object in a bucket must have a unique key.
*
* @see <a href= "http://docs.amazonwebservices.com/AmazonS3/2006-03-01/UsingKeys.html" />
*/
public String getKey() {
return key;
}
public DateTime getLastModified() {
return lastModified;
}
public void setLastModified(DateTime lastModified) {
this.lastModified = lastModified;
}
/**
* The size of the object, in bytes.
*
* @see <a href= "http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html?sec14.13." />
*/
public long getSize() {
return size;
}
public void setSize(long size) {
this.size = size;
}
/**
* A standard MIME type describing the format of the contents. If none is provided, the
* default is binary/octet-stream.
*
* @see <a href= "http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html?sec14.17." />
*/
public String getContentType() {
return dataType;
}
public void setContentType(String dataType) {
this.dataType = dataType;
}
public void setETag(byte[] eTag) {
this.eTag = new byte[eTag.length];
System.arraycopy(eTag, 0, this.eTag, 0, eTag.length);
}
/**
* @return the eTag value stored in the Etag header returned by S3.
*/
public byte[] getETag() {
if (eTag != null) {
byte[] retval = new byte[eTag.length];
System.arraycopy(this.eTag, 0, retval, 0, eTag.length);
return retval;
} else {
return null;
}
}
public void setUserMetadata(Multimap<String, String> userMetadata) {
this.userMetadata = userMetadata;
}
/**
* Any header starting with <code>x-amz-meta-</code> is considered user metadata. It will be
* stored with the object and returned when you retrieve the object. The total size of the
* HTTP request, not including the body, must be less than 8 KB.
*/
public Multimap<String, String> getUserMetadata() {
return userMetadata;
}
public void setOwner(CanonicalUser owner) {
this.owner = owner;
}
/**
* Every bucket and object in Amazon S3 has an owner, the user that created the bucket or
* object. The owner of a bucket or object cannot be changed. However, if the object is
* overwritten by another user (deleted and rewritten), the new object will have a new owner.
*/
public CanonicalUser getOwner() {
return owner;
}
public void setStorageClass(String storageClass) {
this.storageClass = storageClass;
}
/**
* Currently defaults to 'STANDARD' and not used.
*/
public String getStorageClass() {
return storageClass;
}
public void setCacheControl(String cacheControl) {
this.cacheControl = cacheControl;
}
/**
* Can be used to specify caching behavior along the request/reply chain.
*
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html?sec14.9.
*/
public String getCacheControl() {
return cacheControl;
}
public void setContentDisposition(String dataDisposition) {
this.dataDisposition = dataDisposition;
}
/**
* Specifies presentational information for the object.
*
* @see <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html?sec19.5.1."/>
*/
public String getContentDisposition() {
return dataDisposition;
}
public void setContentEncoding(String dataEncoding) {
this.dataEncoding = dataEncoding;
}
/**
* Specifies what content encodings have been applied to the object and thus what decoding
* mechanisms must be applied in order to obtain the media-type referenced by the Content-Type
* header field.
*
* @see <a href= "http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html?sec14.11" />
*/
public String getContentEncoding() {
return dataEncoding;
}
public void setAllHeaders(Multimap<String, String> allHeaders) {
this.allHeaders = allHeaders;
}
public void setAccessControlList(AccessControlList acl) {
this.accessControlList = acl;
}
public AccessControlList getAccessControlList() {
return this.accessControlList;
}
/**
* @return all http response headers associated with this S3Object
*/
public Multimap<String, String> getAllHeaders() {
return allHeaders;
}
public int compareTo(Metadata o) {
return (this == o) ? 0 : getKey().compareTo(o.getKey());
}
}
/**
* @see Metadata#getKey()
*/
public String getKey() {
return metadata.getKey();
}
/**
* Sets entity for the request or the content from the response. If size isn't set, this will
* attempt to discover it.
*
* @param data
* typically InputStream for downloads, or File, byte [], String, or InputStream for
* uploads.
*/
public void setData(Object data) {
this.data = checkNotNull(data, "data");
if (getMetadata().getSize() == -1)
this.getMetadata().setSize(HttpUtils.calculateSize(data));
}
/**
* generate an MD5 Hash for the current data.
* <p/>
* <h2>Note</h2>
* <p/>
* If this is an InputStream, it will be converted to a byte array first.
*
* @throws IOException
* if there is a problem generating the hash.
*/
public void generateETag() throws IOException {
checkState(data != null, "data");
if (data instanceof InputStream) {
ETagInputStreamResult result = HttpUtils.generateETagResult((InputStream) data);
getMetadata().setSize(result.length);
getMetadata().setETag(result.eTag);
setData(result.data);
} else {
getMetadata().setETag(HttpUtils.eTag(data));
}
}
/**
* @return InputStream, if downloading, or whatever was set during {@link #setData(Object)}
*/
public Object getData() {
return data;
}
/**
* @return System and User metadata relevant to this object.
*/
public Metadata getMetadata() {
return metadata;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append("S3Object");
sb.append("{metadata=").append(metadata);
sb.append('}');
return sb.toString();
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (!(o instanceof S3Object))
return false;
S3Object s3Object = (S3Object) o;
if (data != null ? !data.equals(s3Object.data) : s3Object.data != null)
return false;
if (!metadata.equals(s3Object.metadata))
return false;
return true;
}
@Override
public int hashCode() {
int result = data != null ? data.hashCode() : 0;
result = 31 * result + metadata.hashCode();
return result;
}
public void setContentLength(long contentLength) {
this.contentLength = contentLength;
}
/**
* Returns the total size of the downloaded object, or the chunk that's available.
* <p/>
* Chunking is only used when
* {@link org.jclouds.aws.s3.S3Connection#getObject(String, String, org.jclouds.aws.s3.commands.options.GetObjectOptions) }
* is called with options like tail, range, or startAt.
*
* @return the length in bytes that can be be obtained from {@link #getData()}
* @see org.jclouds.http.HttpHeaders#CONTENT_LENGTH
* @see GetObjectOptions
*/
public long getContentLength() {
return contentLength;
}
public void setContentRange(String contentRange) {
this.contentRange = contentRange;
}
/**
* If this is not-null, {@link #getContentLength() } will the size of chunk of the S3Object
* available via {@link #getData()}
*
* @see org.jclouds.http.HttpHeaders#CONTENT_RANGE
* @see GetObjectOptions
*/
public String getContentRange() {
return contentRange;
public S3Object(String key) {
this(new ObjectMetadata(key));
}
}

View File

@ -150,7 +150,7 @@ public class RequestAuthorizeSignature implements HttpRequestFilter {
Set<String> headers = new TreeSet<String>(request.getHeaders().keySet());
for (String header : headers) {
if (header.startsWith("x-amz-")) {
toSign.append(header).append(":");
toSign.append(header.toLowerCase()).append(":");
for (String value : request.getHeaders().get(header))
toSign.append(value.replaceAll("\r?\n", "")).append(",");
toSign.deleteCharAt(toSign.lastIndexOf(","));

View File

@ -23,14 +23,10 @@
*/
package org.jclouds.aws.s3.functions;
import javax.ws.rs.core.HttpHeaders;
import org.jclouds.aws.s3.domain.ObjectMetadata;
import org.jclouds.aws.s3.domain.S3Object;
import org.jclouds.http.HttpException;
import org.jclouds.http.HttpResponse;
import org.jclouds.blobstore.functions.ParseBlobFromHeadersAndHttpContent;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.inject.Inject;
/**
@ -39,45 +35,13 @@ import com.google.inject.Inject;
* @see ParseMetadataFromHeaders
* @author Adrian Cole
*/
public class ParseObjectFromHeadersAndHttpContent implements Function<HttpResponse, S3Object> {
private final ParseMetadataFromHeaders metadataParser;
public class ParseObjectFromHeadersAndHttpContent extends ParseBlobFromHeadersAndHttpContent<ObjectMetadata, S3Object> {
@Inject
public ParseObjectFromHeadersAndHttpContent(ParseMetadataFromHeaders metadataParser) {
this.metadataParser = metadataParser;
}
/**
* First, calls {@link ParseMetadataFromHeaders}.
*
* Then, sets the object size based on the Content-Length header and adds the content to the
* {@link S3Object} result.
*
* @throws org.jclouds.http.HttpException
*/
public S3Object apply(HttpResponse from) {
S3Object.Metadata metadata = metadataParser.apply(from);
S3Object object = new S3Object(metadata, from.getContent());
parseContentLengthOrThrowException(from, object);
return object;
}
@VisibleForTesting
void parseContentLengthOrThrowException(HttpResponse from, S3Object object) throws HttpException {
String contentLength = from.getFirstHeaderOrNull(HttpHeaders.CONTENT_LENGTH);
String contentRange = from.getFirstHeaderOrNull("Content-Range");
if (contentLength == null)
throw new HttpException(HttpHeaders.CONTENT_LENGTH + " header not present in headers: "
+ from.getHeaders());
object.setContentLength(Long.parseLong(contentLength));
if (contentRange == null) {
object.getMetadata().setSize(object.getContentLength());
} else {
object.setContentRange(contentRange);
object.getMetadata().setSize(
Long.parseLong(contentRange.substring(contentRange.lastIndexOf('/') + 1)));
}
public ParseObjectFromHeadersAndHttpContent(
ParseObjectMetadataFromHeaders metadataParser,
BlobFactory<ObjectMetadata, S3Object> blobFactory) {
super(metadataParser, blobFactory);
}
}

View File

@ -0,0 +1,84 @@
/**
*
* 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 static org.jclouds.blobstore.reference.BlobStoreConstants.PROPERTY_USER_METADATA_PREFIX;
import javax.ws.rs.core.HttpHeaders;
import org.jclouds.aws.s3.domain.ObjectMetadata;
import org.jclouds.aws.s3.reference.S3Headers;
import org.jclouds.blobstore.functions.ParseBlobMetadataFromHeaders;
import org.jclouds.http.HttpResponse;
import org.jclouds.http.HttpUtils;
import org.jclouds.util.DateService;
import com.google.common.annotations.VisibleForTesting;
import com.google.inject.Inject;
import com.google.inject.name.Named;
/**
* This parses @{link {@link org.jclouds.aws.s3.domain.ObjectMetadata} from HTTP headers.
*
* @see <a href="http://docs.amazonwebservices.com/AmazonS3/latest/RESTObjectGET.html" />
* @author Adrian Cole
*/
public class ParseObjectMetadataFromHeaders extends ParseBlobMetadataFromHeaders<ObjectMetadata> {
@Inject
public ParseObjectMetadataFromHeaders(DateService dateParser,
@Named(PROPERTY_USER_METADATA_PREFIX) String metadataPrefix,
BlobMetadataFactory<ObjectMetadata> metadataFactory) {
super(dateParser, metadataPrefix, metadataFactory);
}
/**
* parses the http response headers to create a new
* {@link org.jclouds.aws.s3.domain.ObjectMetadata} object.
*/
@Override
public ObjectMetadata apply(HttpResponse from) {
ObjectMetadata to = super.apply(from);
to.setCacheControl(from.getFirstHeaderOrNull(HttpHeaders.CACHE_CONTROL));
to.setContentDisposition(from.getFirstHeaderOrNull("Content-Disposition"));
to.setContentEncoding(from.getFirstHeaderOrNull(HttpHeaders.CONTENT_ENCODING));
return to;
}
/**
* ETag == Content-MD5
*/
@VisibleForTesting
protected void addETagTo(HttpResponse from, ObjectMetadata metadata) {
super.addETagTo(from, metadata);
if (metadata.getETag() == null) {
String eTagHeader = from.getFirstHeaderOrNull(S3Headers.AMZ_MD5);
if (eTagHeader != null) {
metadata.setETag(HttpUtils.fromHexString(eTagHeader));
}
}
metadata.setContentMD5(metadata.getETag());
}
}

View File

@ -1,42 +0,0 @@
/**
*
* 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 org.jclouds.aws.AWSResponseException;
import org.jclouds.aws.s3.domain.AccessControlList;
import com.google.common.base.Function;
public class ReturnNotFoundIfBucketDoesntExist implements Function<Exception, AccessControlList> {
public AccessControlList apply(Exception from) {
if (from != null && from instanceof AWSResponseException) {
AWSResponseException responseException = (AWSResponseException) from;
if ("NoSuchBucket".equals(responseException.getError().getCode())) {
return AccessControlList.NOT_FOUND;
}
}
return null;
}
}

View File

@ -1,43 +0,0 @@
/**
*
* 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 org.jclouds.aws.AWSResponseException;
import org.jclouds.aws.s3.domain.AccessControlList;
import com.google.common.base.Function;
public class ReturnNotFoundIfObjectDoesntExist implements Function<Exception, AccessControlList> {
public AccessControlList apply(Exception from) {
if (from != null && from instanceof AWSResponseException) {
AWSResponseException responseException = (AWSResponseException) from;
if ("NoSuchKey".equals(responseException.getError().getCode())) {
return AccessControlList.NOT_FOUND;
}
}
return null;
}
}

View File

@ -1,43 +0,0 @@
/**
*
* 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 org.jclouds.aws.s3.domain.S3Object.Metadata;
import org.jclouds.http.HttpResponseException;
import com.google.common.base.Function;
public class ReturnS3ObjectMetadataNotFoundOn404 implements Function<Exception, Metadata> {
public Metadata apply(Exception from) {
if (from instanceof HttpResponseException) {
HttpResponseException responseException = (HttpResponseException) from;
if (responseException.getResponse().getStatusCode() == 404) {
return Metadata.NOT_FOUND;
}
}
return null;
}
}

View File

@ -1,255 +0,0 @@
/**
*
* 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.internal;
import static com.google.common.base.Preconditions.checkNotNull;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.jclouds.aws.s3.S3Connection;
import org.jclouds.aws.s3.S3Map;
import org.jclouds.aws.s3.domain.S3Bucket;
import org.jclouds.aws.s3.domain.S3Object;
import org.jclouds.aws.s3.reference.S3Constants;
import org.jclouds.util.Utils;
import com.google.common.annotations.VisibleForTesting;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.name.Named;
/**
* Implements core Map functionality with an {@link S3Connection}
* <p/>
* All commands will wait a maximum of ${jclouds.s3.map.timeout} milliseconds to complete before
* throwing an exception.
*
* @author Adrian Cole
* @param <V>
* value of the map
*/
public abstract class BaseS3Map<V> implements S3Map<String, V> {
protected final S3Connection connection;
protected final String bucket;
/**
* maximum duration of an S3 Request
*/
@Inject(optional = true)
@Named(S3Constants.PROPERTY_OBJECTMAP_TIMEOUT)
protected long requestTimeoutMilliseconds = 10000;
/**
* time to pause before retrying a transient failure
*/
@Inject(optional = true)
@Named(S3Constants.PROPERTY_OBJECTMAP_RETRY)
protected long requestRetryMilliseconds = 10;
@Inject
public BaseS3Map(S3Connection connection, @Assisted String bucket) {
this.connection = checkNotNull(connection, "connection");
this.bucket = checkNotNull(bucket, "bucketName");
}
/**
* {@inheritDoc}
* <p/>
* This returns the number of keys in the {@link S3Bucket}
*
* @see S3Bucket#getContents()
*/
public int size() {
try {
S3Bucket bucket = refreshBucket();
Set<S3Object.Metadata> contents = bucket.getContents();
return contents.size();
} catch (Exception e) {
Utils.<S3RuntimeException> rethrowIfRuntimeOrSameType(e);
throw new S3RuntimeException("Error getting size of bucketName" + bucket, e);
}
}
protected boolean containsETag(byte[] eTag) throws InterruptedException, ExecutionException,
TimeoutException {
for (S3Object.Metadata metadata : refreshBucket().getContents()) {
if (Arrays.equals(eTag, metadata.getETag()))
return true;
}
return false;
}
protected byte[] getETag(Object value) throws IOException, FileNotFoundException,
InterruptedException, ExecutionException, TimeoutException {
S3Object object = null;
if (value instanceof S3Object) {
object = (S3Object) value;
} else {
object = new S3Object("dummy", value);
}
if (object.getMetadata().getETag() == null)
object.generateETag();
return object.getMetadata().getETag();
}
/**
* attempts asynchronous gets on all objects.
*
* @see S3Connection#getObject(String, String)
*/
protected Set<S3Object> getAllObjects() {
Set<S3Object> objects = new HashSet<S3Object>();
Set<Future<S3Object>> futureObjects = new HashSet<Future<S3Object>>();
for (String key : keySet()) {
futureObjects.add(connection.getObject(bucket, key));
}
for (Future<S3Object> futureObject : futureObjects) {
try {
ifNotFoundRetryOtherwiseAddToSet(futureObject, objects);
} catch (Exception e) {
Utils.<S3RuntimeException> rethrowIfRuntimeOrSameType(e);
throw new S3RuntimeException(String.format("Error getting value from bucket %1$s",
bucket), e);
}
}
return objects;
}
@VisibleForTesting
void ifNotFoundRetryOtherwiseAddToSet(Future<S3Object> futureObject, Set<S3Object> objects)
throws InterruptedException, ExecutionException, TimeoutException {
for (int i = 0; i < 3; i++) {
S3Object object = futureObject.get(requestTimeoutMilliseconds, TimeUnit.MILLISECONDS);
if (object != S3Object.NOT_FOUND) {
objects.add(object);
break;
} else {
Thread.sleep(requestRetryMilliseconds);
}
}
}
/**
* {@inheritDoc}
* <p/>
* Note that if value is an instance of InputStream, it will be read and closed following this
* method. To reuse data from InputStreams, pass {@link java.io.InputStream}s inside
* {@link S3Object}s
*/
public boolean containsValue(Object value) {
try {
byte[] eTag = getETag(value);
return containsETag(eTag);
} catch (Exception e) {
Utils.<S3RuntimeException> rethrowIfRuntimeOrSameType(e);
throw new S3RuntimeException(String.format(
"Error searching for ETAG of value: [%2$s] in bucketName:%1$s", bucket, value), e);
}
}
public static class S3RuntimeException extends RuntimeException {
private static final long serialVersionUID = 1L;
S3RuntimeException(String s) {
super(s);
}
public S3RuntimeException(String s, Throwable throwable) {
super(s, throwable);
}
}
public void clear() {
try {
List<Future<Boolean>> deletes = new ArrayList<Future<Boolean>>();
for (String key : keySet()) {
deletes.add(connection.deleteObject(bucket, key));
}
for (Future<Boolean> isdeleted : deletes)
if (!isdeleted.get(requestTimeoutMilliseconds, TimeUnit.MILLISECONDS)) {
throw new S3RuntimeException("failed to delete entry");
}
} catch (Exception e) {
Utils.<S3RuntimeException> rethrowIfRuntimeOrSameType(e);
throw new S3RuntimeException("Error clearing bucketName" + bucket, e);
}
}
protected S3Bucket refreshBucket() throws InterruptedException, ExecutionException,
TimeoutException {
S3Bucket currentBucket = connection.listBucket(bucket).get(requestTimeoutMilliseconds,
TimeUnit.MILLISECONDS);
if (currentBucket == S3Bucket.NOT_FOUND)
throw new S3RuntimeException("bucketName not found: " + bucket);
else
return currentBucket;
}
public Set<String> keySet() {
try {
Set<String> keys = new HashSet<String>();
for (S3Object.Metadata object : refreshBucket().getContents())
keys.add(object.getKey());
return keys;
} catch (Exception e) {
Utils.<S3RuntimeException> rethrowIfRuntimeOrSameType(e);
throw new S3RuntimeException("Error getting keys in bucketName: " + bucket, e);
}
}
public boolean containsKey(Object key) {
try {
return connection.headObject(bucket, key.toString()) != S3Object.Metadata.NOT_FOUND;
} catch (Exception e) {
Utils.<S3RuntimeException> rethrowIfRuntimeOrSameType(e);
throw new S3RuntimeException(String.format("Error searching for %1$s:%2$s", bucket, key),
e);
}
}
public boolean isEmpty() {
return keySet().size() == 0;
}
public S3Bucket getBucket() {
try {
return refreshBucket();
} catch (Exception e) {
Utils.<S3RuntimeException> rethrowIfRuntimeOrSameType(e);
throw new S3RuntimeException("Error getting bucketName" + bucket, e);
}
}
}

View File

@ -24,18 +24,23 @@
package org.jclouds.aws.s3.internal;
import java.io.IOException;
import java.net.URI;
import javax.annotation.Resource;
import org.jclouds.aws.s3.S3Connection;
import org.jclouds.aws.s3.S3BlobStore;
import org.jclouds.aws.s3.S3Context;
import org.jclouds.aws.s3.S3InputStreamMap;
import org.jclouds.aws.s3.S3ObjectMap;
import org.jclouds.aws.s3.domain.ObjectMetadata;
import org.jclouds.aws.s3.domain.S3Object;
import org.jclouds.aws.s3.reference.S3Constants;
import org.jclouds.blobstore.BlobMap;
import org.jclouds.blobstore.InputStreamMap;
import org.jclouds.lifecycle.Closer;
import org.jclouds.logging.Logger;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.name.Named;
/**
* Uses a Guice Injector to configure the objects served by S3Context methods.
@ -45,11 +50,11 @@ import com.google.inject.Injector;
*/
public class GuiceS3Context implements S3Context {
public interface S3ObjectMapFactory {
S3ObjectMap createMapView(String bucket);
BlobMap<ObjectMetadata, S3Object> createMapView(String bucket);
}
public interface S3InputStreamMapFactory {
S3InputStreamMap createMapView(String bucket);
InputStreamMap<ObjectMetadata> createMapView(String bucket);
}
@Resource
@ -58,36 +63,41 @@ public class GuiceS3Context implements S3Context {
private final S3InputStreamMapFactory s3InputStreamMapFactory;
private final S3ObjectMapFactory s3ObjectMapFactory;
private final Closer closer;
private final String account;
private final URI endPoint;
@Inject
private GuiceS3Context(Injector injector, Closer closer, S3ObjectMapFactory s3ObjectMapFactory,
S3InputStreamMapFactory s3InputStreamMapFactory) {
S3InputStreamMapFactory s3InputStreamMapFactory,
@Named(S3Constants.PROPERTY_AWS_ACCESSKEYID) String accessKey, URI endPoint) {
this.injector = injector;
this.s3InputStreamMapFactory = s3InputStreamMapFactory;
this.s3ObjectMapFactory = s3ObjectMapFactory;
this.closer = closer;
this.account = accessKey;
this.endPoint = endPoint;
}
/**
* {@inheritDoc}
*/
public S3Connection getConnection() {
return injector.getInstance(S3Connection.class);
public S3BlobStore getApi() {
return injector.getInstance(S3BlobStore.class);
}
/**
* {@inheritDoc}
*/
public S3InputStreamMap createInputStreamMap(String bucket) {
getConnection().putBucketIfNotExists(bucket);
public InputStreamMap<ObjectMetadata> createInputStreamMap(String bucket) {
getApi().createContainer(bucket);
return s3InputStreamMapFactory.createMapView(bucket);
}
/**
* {@inheritDoc}
*/
public S3ObjectMap createS3ObjectMap(String bucket) {
getConnection().putBucketIfNotExists(bucket);
public BlobMap<ObjectMetadata, S3Object> createBlobMap(String bucket) {
getApi().createContainer(bucket);
return s3ObjectMapFactory.createMapView(bucket);
}
@ -104,4 +114,48 @@ public class GuiceS3Context implements S3Context {
}
}
@Override
public String toString() {
return "GuiceS3Context [account=" + account + ", endPoint=" + endPoint + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((account == null) ? 0 : account.hashCode());
result = prime * result + ((endPoint == null) ? 0 : endPoint.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
GuiceS3Context other = (GuiceS3Context) obj;
if (account == null) {
if (other.account != null)
return false;
} else if (!account.equals(other.account))
return false;
if (endPoint == null) {
if (other.endPoint != null)
return false;
} else if (!endPoint.equals(other.endPoint))
return false;
return true;
}
public String getAccount() {
return account;
}
public URI getEndPoint() {
return endPoint;
}
}

View File

@ -23,259 +23,33 @@
*/
package org.jclouds.aws.s3.internal;
import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.jclouds.aws.s3.S3Connection;
import org.jclouds.aws.s3.S3InputStreamMap;
import org.jclouds.aws.s3.S3BlobStore;
import org.jclouds.aws.s3.domain.BucketMetadata;
import org.jclouds.aws.s3.domain.ObjectMetadata;
import org.jclouds.aws.s3.domain.S3Object;
import org.jclouds.util.Utils;
import org.jclouds.blobstore.LiveInputStreamMap;
import com.google.common.annotations.VisibleForTesting;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
/**
* Map representation of a live connection to S3. All put operations will result
* in ETag calculation. If this is not desired, use {@link LiveS3ObjectMap}
* instead.
* Map representation of a live connection to S3. All put operations will result in ETag
* calculation. If this is not desired, use {@link LiveS3ObjectMap} instead.
*
* @author Adrian Cole
* @see S3Connection
* @see BaseS3Map
*/
public class LiveS3InputStreamMap extends BaseS3Map<InputStream> implements
S3InputStreamMap {
public class LiveS3InputStreamMap extends
LiveInputStreamMap<BucketMetadata, ObjectMetadata, S3Object> {
@Inject
public LiveS3InputStreamMap(S3Connection connection, @Assisted String bucket) {
super(connection, bucket);
public LiveS3InputStreamMap(S3BlobStore connection, @Assisted String container) {
super(connection, container);
}
/**
* {@inheritDoc}
*
* @see S3Connection#getObject(String, String)
*/
public InputStream get(Object o) {
try {
return (InputStream) (connection.getObject(bucket, o.toString())
.get(requestTimeoutMilliseconds, TimeUnit.MILLISECONDS))
.getData();
} catch (Exception e) {
Utils.<S3RuntimeException>rethrowIfRuntimeOrSameType(e);
throw new S3RuntimeException(String.format(
"Error geting object %1$s:%2$s", bucket, o), e);
@Override
protected S3Object createBlob(String s) {
return new S3Object(s);
}
}
/**
* {@inheritDoc}
*
* @see S3Connection#deleteObject(String, String)
*/
public InputStream remove(Object o) {
InputStream old = get(o);
try {
connection.deleteObject(bucket, o.toString()).get(
requestTimeoutMilliseconds, TimeUnit.MILLISECONDS);
} catch (Exception e) {
Utils.<S3RuntimeException>rethrowIfRuntimeOrSameType(e);
throw new S3RuntimeException(String.format(
"Error removing object %1$s:%2$s", bucket, o), e);
}
return old;
}
/**
* {@inheritDoc}
*
* @see #getAllObjects()
*/
public Collection<InputStream> values() {
Collection<InputStream> values = new LinkedList<InputStream>();
Set<S3Object> objects = getAllObjects();
for (S3Object object : objects) {
values.add((InputStream) object.getData());
}
return values;
}
/**
* {@inheritDoc}
*
* @see #getAllObjects()
*/
public Set<Map.Entry<String, InputStream>> entrySet() {
Set<Map.Entry<String, InputStream>> entrySet = new HashSet<Map.Entry<String, InputStream>>();
for (S3Object object : getAllObjects()) {
entrySet.add(new Entry(object.getKey(), (InputStream) object
.getData()));
}
return entrySet;
}
public class Entry implements java.util.Map.Entry<String, InputStream> {
private InputStream value;
private String key;
Entry(String key, InputStream value) {
this.key = key;
this.value = value;
}
public String getKey() {
return key;
}
public InputStream getValue() {
return value;
}
/**
* {@inheritDoc}
*
* @see LiveS3InputStreamMap#put(String, InputStream)
*/
public InputStream setValue(InputStream value) {
return put(key, value);
}
}
/**
* {@inheritDoc}
*
* @see #putAllInternal(Map)
*/
public void putAll(Map<? extends String, ? extends InputStream> map) {
putAllInternal(map);
}
/**
* {@inheritDoc}
*
* @see #putAllInternal(Map)
*/
public void putAllBytes(Map<? extends String, ? extends byte[]> map) {
putAllInternal(map);
}
/**
* {@inheritDoc}
*
* @see #putAllInternal(Map)
*/
public void putAllFiles(Map<? extends String, ? extends File> map) {
putAllInternal(map);
}
/**
* {@inheritDoc}
*
* @see #putAllInternal(Map)
*/
public void putAllStrings(Map<? extends String, ? extends String> map) {
putAllInternal(map);
}
/**
* submits requests to add all objects and collects the results later. All
* values will have eTag calculated first. As a side-effect of this, the
* content will be copied into a byte [].
*
* @see S3Connection#putObject(String, S3Object)
*/
@VisibleForTesting
void putAllInternal(Map<? extends String, ? extends Object> map) {
try {
List<Future<byte[]>> puts = new ArrayList<Future<byte[]>>();
for (Map.Entry<? extends String, ? extends Object> entry : map.entrySet()) {
S3Object object = new S3Object(entry.getKey());
object.setData(entry.getValue());
object.generateETag();
puts.add(connection.putObject(bucket, object));
/// ParamExtractor Funcion<?,String>
/// response transformer set key on the way out.
/// ExceptionHandler convert 404 to NOT_FOUND
}
for (Future<byte[]> put : puts)
// this will throw an exception if there was a problem
put.get(requestTimeoutMilliseconds, TimeUnit.MILLISECONDS);
} catch (Exception e) {
Utils.<S3RuntimeException>rethrowIfRuntimeOrSameType(e);
throw new S3RuntimeException("Error putting into bucketName" + bucket,
e);
}
}
/**
* {@inheritDoc}
*
* @see #putInternal(String, Object)
*/
public InputStream putString(String key, String value) {
return putInternal(key, value);
}
/**
* {@inheritDoc}
*
* @see #putInternal(String, Object)
*/
public InputStream putFile(String key, File value) {
return putInternal(key, value);
}
/**
* {@inheritDoc}
*
* @see #putInternal(String, Object)
*/
public InputStream putBytes(String key, byte[] value) {
return putInternal(key, value);
}
/**
* {@inheritDoc}
*
* @see #putInternal(String, Object)
*/
public InputStream put(String key, InputStream value) {
return putInternal(key, value);
}
/**
* calculates eTag before adding the object to s3. As a side-effect of this,
* the content will be copied into a byte []. *
*
* @see S3Connection#putObject(String, S3Object)
*/
@VisibleForTesting
InputStream putInternal(String s, Object o) {
S3Object object = new S3Object(s);
try {
InputStream returnVal = containsKey(s) ? get(s) : null;
object.setData(o);
object.generateETag();
connection.putObject(bucket, object).get(
requestTimeoutMilliseconds, TimeUnit.MILLISECONDS);
return returnVal;
} catch (Exception e) {
Utils.<S3RuntimeException>rethrowIfRuntimeOrSameType(e);
throw new S3RuntimeException(String.format(
"Error adding object %1$s:%2$s", bucket, object), e);
}
}
}

View File

@ -23,19 +23,11 @@
*/
package org.jclouds.aws.s3.internal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.jclouds.aws.s3.S3Connection;
import org.jclouds.aws.s3.S3ObjectMap;
import org.jclouds.aws.s3.S3BlobStore;
import org.jclouds.aws.s3.domain.BucketMetadata;
import org.jclouds.aws.s3.domain.ObjectMetadata;
import org.jclouds.aws.s3.domain.S3Object;
import org.jclouds.util.Utils;
import org.jclouds.blobstore.LiveBlobMap;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
@ -48,137 +40,11 @@ import com.google.inject.assistedinject.Assisted;
*
* @author Adrian Cole
*/
public class LiveS3ObjectMap extends BaseS3Map<S3Object> implements S3ObjectMap {
public class LiveS3ObjectMap extends LiveBlobMap<BucketMetadata, ObjectMetadata, S3Object> {
@Inject
public LiveS3ObjectMap(S3Connection connection, @Assisted String bucket) {
super(connection, bucket);
}
/**
* {@inheritDoc}
*
* @see #values()
*/
public Set<java.util.Map.Entry<String, S3Object>> entrySet() {
Set<Map.Entry<String, S3Object>> entrySet = new HashSet<Map.Entry<String, S3Object>>();
for (S3Object value : values()) {
Map.Entry<String, S3Object> entry = new Entry(value.getKey(), value);
entrySet.add(entry);
}
return entrySet;
}
public class Entry implements java.util.Map.Entry<String, S3Object> {
private S3Object value;
private String key;
Entry(String key, S3Object value) {
this.key = key;
this.value = value;
}
public String getKey() {
return key;
}
public S3Object getValue() {
return value;
}
/**
* {@inheritDoc}
*
* @see LiveS3ObjectMap#put(String, S3Object)
*/
public S3Object setValue(S3Object value) {
return put(key, value);
}
}
/**
* {@inheritDoc}
*
* @see S3Connection#getObject(String, String)
*/
public S3Object get(Object key) {
try {
return connection.getObject(bucket, key.toString()).get(
requestTimeoutMilliseconds, TimeUnit.MILLISECONDS);
} catch (Exception e) {
Utils.<S3RuntimeException> rethrowIfRuntimeOrSameType(e);
throw new S3RuntimeException(String.format(
"Error geting object %1$s:%2$s", bucket, key), e);
}
}
/**
* {@inheritDoc}
*
* @see S3Connection#putObject(String, S3Object)
*/
public S3Object put(String key, S3Object value) {
S3Object returnVal = get(key);
try {
connection.putObject(bucket, value).get(requestTimeoutMilliseconds,
TimeUnit.MILLISECONDS);
} catch (Exception e) {
Utils.<S3RuntimeException> rethrowIfRuntimeOrSameType(e);
throw new S3RuntimeException(
String.format("Error putting object %1$s:%2$s%n%3$s",
bucket, key, value), e);
}
return returnVal;
}
/**
* {@inheritDoc} attempts to put all objects asynchronously.
*
* @see S3Connection#putObject(String, S3Object)
*/
public void putAll(Map<? extends String, ? extends S3Object> map) {
try {
List<Future<byte[]>> puts = new ArrayList<Future<byte[]>>();
for (S3Object object : map.values()) {
puts.add(connection.putObject(bucket, object));
}
for (Future<byte[]> put : puts)
// this will throw an exception if there was a problem
put.get(requestTimeoutMilliseconds, TimeUnit.MILLISECONDS);
} catch (Exception e) {
Utils.<S3RuntimeException> rethrowIfRuntimeOrSameType(e);
throw new S3RuntimeException("Error putting into bucketName" + bucket,
e);
}
}
/**
* {@inheritDoc}
*
* @see S3Connection#deleteObject(String, String)
*/
public S3Object remove(Object key) {
S3Object old = get(key);
try {
connection.deleteObject(bucket, key.toString()).get(
requestTimeoutMilliseconds, TimeUnit.MILLISECONDS);
} catch (Exception e) {
Utils.<S3RuntimeException> rethrowIfRuntimeOrSameType(e);
throw new S3RuntimeException(String.format(
"Error removing object %1$s:%2$s", bucket, key), e);
}
return old;
}
/**
* {@inheritDoc}
*
* @see #getAllObjects()
*/
public Collection<S3Object> values() {
return getAllObjects();
public LiveS3ObjectMap(S3BlobStore connection, @Assisted String container) {
super(connection, container);
}
}

View File

@ -23,9 +23,9 @@
*/
package org.jclouds.aws.s3.options;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static org.jclouds.blobstore.reference.BlobStoreConstants.PROPERTY_USER_METADATA_PREFIX;
import java.io.UnsupportedEncodingException;
@ -39,6 +39,8 @@ import org.joda.time.DateTime;
import com.google.common.base.Preconditions;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.google.inject.Inject;
import com.google.inject.name.Named;
/**
* Contains options supported in the REST API for the COPY object operation.
@ -78,6 +80,13 @@ public class CopyObjectOptions extends BaseHttpRequestOptions {
private CannedAccessPolicy acl = CannedAccessPolicy.PRIVATE;
private String metadataPrefix;
@Inject
public void setMetadataPrefix(@Named(PROPERTY_USER_METADATA_PREFIX) String metadataPrefix) {
this.metadataPrefix = metadataPrefix;
}
/**
* Override the default ACL (private) with the specified one.
*
@ -245,10 +254,14 @@ public class CopyObjectOptions extends BaseHttpRequestOptions {
@Override
public Multimap<String, String> buildRequestHeaders() {
checkState(metadataPrefix != null, "metadataPrefix should have been injected!");
Multimap<String, String> returnVal = HashMultimap.create();
returnVal.putAll(headers);
if (metadata != null) {
returnVal.putAll(metadata);
for (String key : metadata.keySet()) {
returnVal.putAll(key.startsWith(metadataPrefix) ? key : metadataPrefix + key, metadata
.get(key));
}
returnVal.put("x-amz-metadata-directive", "REPLACE");
}
return returnVal;
@ -259,10 +272,6 @@ public class CopyObjectOptions extends BaseHttpRequestOptions {
*/
public CopyObjectOptions overrideMetadataWith(Multimap<String, String> metadata) {
checkNotNull(metadata, "metadata");
for (String header : metadata.keySet()) {
checkArgument(header.startsWith("x-amz-meta-"),
"Metadata keys must start with x-amz-meta-");
}
this.metadata = metadata;
return this;
}

View File

@ -26,7 +26,7 @@ package org.jclouds.aws.s3.options;
import static com.google.common.base.Preconditions.checkNotNull;
import org.jclouds.aws.s3.domain.CannedAccessPolicy;
import org.jclouds.aws.s3.domain.S3Bucket.Metadata.LocationConstraint;
import org.jclouds.aws.s3.domain.BucketMetadata.LocationConstraint;
import org.jclouds.aws.s3.reference.S3Headers;
import org.jclouds.http.options.BaseHttpRequestOptions;
@ -88,7 +88,7 @@ public class PutBucketOptions extends BaseHttpRequestOptions {
}
/**
* @see PutBucketOptions#createIn(org.jclouds.aws.s3.domain.S3Bucket.Metadata.LocationConstraint)
* @see PutBucketOptions#createIn(org.jclouds.aws.s3.domain.BucketMetadata.LocationConstraint)
*/
public LocationConstraint getLocationConstraint() {
return constraint;
@ -96,7 +96,7 @@ public class PutBucketOptions extends BaseHttpRequestOptions {
public static class Builder {
/**
* @see PutBucketOptions#createIn(org.jclouds.aws.s3.domain.S3Bucket.Metadata.LocationConstraint)
* @see PutBucketOptions#createIn(org.jclouds.aws.s3.domain.BucketMetadata.LocationConstraint)
*/
public static PutBucketOptions createIn(LocationConstraint constraint) {
PutBucketOptions options = new PutBucketOptions();

View File

@ -24,14 +24,14 @@
package org.jclouds.aws.s3.reference;
import org.jclouds.aws.reference.AWSConstants;
import org.jclouds.keyvaluestore.reference.ObjectStoreConstants;
import org.jclouds.blobstore.reference.BlobStoreConstants;
/**
* Configuration properties and constants used in S3 connections.
*
* @author Adrian Cole
*/
public interface S3Constants extends AWSConstants, S3Headers, ObjectStoreConstants {
public interface S3Constants extends AWSConstants, S3Headers, BlobStoreConstants {
/**
* S3 service's XML Namespace, as used in XML request and response documents.

View File

@ -40,12 +40,6 @@ public interface S3Headers {
* Policy.
*/
public static final String CANNED_ACL = "x-amz-acl";
/**
* Any header starting with this prefix is considered user metadata. It will be stored with the
* object and returned when you retrieve the object. The total size of the HTTP request, not
* including the body, must be less than 8 KB.
*/
public static final String USER_METADATA_PREFIX = "x-amz-meta-";
public static final String AMZ_MD5 = "x-amz-meta-object-eTag";
public static final String REQUEST_ID = "x-amz-request-id";
public static final String REQUEST_TOKEN = "x-amz-id-2";

View File

@ -27,19 +27,17 @@ import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.jclouds.aws.domain.AWSError;
import org.jclouds.aws.s3.domain.S3Object;
import org.jclouds.aws.s3.filters.RequestAuthorizeSignature;
import org.jclouds.aws.s3.reference.S3Headers;
import org.jclouds.aws.s3.xml.S3ParserFactory;
import org.jclouds.blobstore.util.BlobStoreUtils;
import org.jclouds.http.HttpCommand;
import org.jclouds.http.HttpException;
import org.jclouds.http.HttpResponse;
import org.jclouds.http.HttpUtils;
import org.jclouds.util.Utils;
import com.google.inject.Inject;
@ -48,7 +46,7 @@ import com.google.inject.Inject;
*
* @author Adrian Cole
*/
public class S3Utils {
public class S3Utils extends BlobStoreUtils {
@Inject
RequestAuthorizeSignature signer;
@ -86,19 +84,4 @@ public class S3Utils {
return bucketName;
}
public static String getContentAsStringAndClose(S3Object object) throws IOException {
checkNotNull(object, "s3Object");
checkNotNull(object.getData(), "s3Object.content");
Object o = object.getData();
if (o instanceof InputStream) {
String returnVal = Utils.toStringAndClose((InputStream) o);
if (object.getMetadata().getContentType().indexOf("xml") >= 0) {
}
return returnVal;
} else {
throw new IllegalArgumentException("Object type not supported: " + o.getClass().getName());
}
}
}

View File

@ -23,7 +23,7 @@
*/
package org.jclouds.aws.s3.xml;
import org.jclouds.aws.s3.domain.S3Object;
import org.jclouds.aws.s3.domain.ObjectMetadata;
import org.jclouds.http.HttpUtils;
import org.jclouds.http.functions.ParseSax;
import org.jclouds.util.DateService;
@ -38,14 +38,14 @@ import com.google.inject.Inject;
* @see <a href= "http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTObjectCOPY.html" />
* @author Adrian Cole
*/
public class CopyObjectHandler extends ParseSax.HandlerWithResult<S3Object.Metadata> {
public class CopyObjectHandler extends ParseSax.HandlerWithResult<ObjectMetadata> {
private S3Object.Metadata metadata = new S3Object.Metadata();
private ObjectMetadata metadata = new ObjectMetadata();
private StringBuilder currentText = new StringBuilder();
@Inject
private DateService dateParser;
public S3Object.Metadata getResult() {
public ObjectMetadata getResult() {
return metadata;
}

View File

@ -27,7 +27,7 @@ import java.util.ArrayList;
import java.util.List;
import org.jclouds.aws.s3.domain.CanonicalUser;
import org.jclouds.aws.s3.domain.S3Bucket;
import org.jclouds.aws.s3.domain.BucketMetadata;
import org.jclouds.http.functions.ParseSax;
import org.jclouds.util.DateService;
@ -43,10 +43,10 @@ import com.google.inject.Inject;
* />
* @author Adrian Cole
*/
public class ListAllMyBucketsHandler extends ParseSax.HandlerWithResult<List<S3Bucket.Metadata>> {
public class ListAllMyBucketsHandler extends ParseSax.HandlerWithResult<List<BucketMetadata>> {
private List<S3Bucket.Metadata> buckets = new ArrayList<S3Bucket.Metadata>();
private S3Bucket.Metadata currentS3Bucket;
private List<BucketMetadata> buckets = new ArrayList<BucketMetadata>();
private BucketMetadata currentS3Bucket;
private CanonicalUser currentOwner;
private StringBuilder currentText = new StringBuilder();
@ -57,7 +57,7 @@ public class ListAllMyBucketsHandler extends ParseSax.HandlerWithResult<List<S3B
this.dateParser = dateParser;
}
public List<S3Bucket.Metadata> getResult() {
public List<BucketMetadata> getResult() {
return buckets;
}
@ -70,7 +70,7 @@ public class ListAllMyBucketsHandler extends ParseSax.HandlerWithResult<List<S3B
currentS3Bucket.setOwner(currentOwner);
buckets.add(currentS3Bucket);
} else if (qName.equals("Name")) {
currentS3Bucket = new S3Bucket.Metadata(currentText.toString());
currentS3Bucket = new BucketMetadata(currentText.toString());
} else if (qName.equals("CreationDate")) {
currentS3Bucket.setCreationDate(dateParser.iso8601DateParse(currentText.toString()));
}

View File

@ -23,14 +23,20 @@
*/
package org.jclouds.aws.s3.xml;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
import org.jclouds.aws.s3.domain.ArrayListBucketResponse;
import org.jclouds.aws.s3.domain.CanonicalUser;
import org.jclouds.aws.s3.domain.S3Bucket;
import org.jclouds.aws.s3.domain.S3Object;
import org.jclouds.aws.s3.domain.ListBucketResponse;
import org.jclouds.aws.s3.domain.ObjectMetadata;
import org.jclouds.http.HttpUtils;
import org.jclouds.http.functions.ParseSax;
import org.jclouds.util.DateService;
import org.xml.sax.Attributes;
import com.google.common.collect.Lists;
import com.google.inject.Inject;
/**
@ -43,22 +49,31 @@ import com.google.inject.Inject;
* href="http://docs.amazonwebservices.com/AmazonS3/2006-03-01/index.html?RESTBucketGET.html"
* />
*/
public class ListBucketHandler extends ParseSax.HandlerWithResult<S3Bucket> {
private S3Bucket s3Bucket;
private S3Object.Metadata currentObjectMetadata;
public class ListBucketHandler extends ParseSax.HandlerWithResult<ListBucketResponse> {
private List<ObjectMetadata> contents;
private SortedSet<String> commonPrefixes;
private ObjectMetadata currentObjectMetadata;
private CanonicalUser currentOwner;
private StringBuilder currentText = new StringBuilder();
private final DateService dateParser;
private String bucketName;
private String prefix;
private String marker;
private int maxResults;
private String delimiter;
private boolean isTruncated;
@Inject
public ListBucketHandler(DateService dateParser) {
this.dateParser = dateParser;
this.s3Bucket = new S3Bucket();
this.contents = Lists.newArrayList();
this.commonPrefixes = new TreeSet<String>();
}
public S3Bucket getResult() {
return s3Bucket;
public ListBucketResponse getResult() {
return new ArrayListBucketResponse(bucketName, contents, prefix, marker, maxResults,
delimiter, isTruncated, commonPrefixes);
}
private boolean inCommonPrefixes;
@ -75,7 +90,7 @@ public class ListBucketHandler extends ParseSax.HandlerWithResult<S3Bucket> {
} else if (qName.equals("DisplayName")) {
currentOwner.setDisplayName(currentText.toString());
} else if (qName.equals("Key")) { // content stuff
currentObjectMetadata = new S3Object.Metadata(currentText.toString());
currentObjectMetadata = new ObjectMetadata(currentText.toString());
} else if (qName.equals("LastModified")) {
currentObjectMetadata.setLastModified(dateParser.iso8601DateParse(currentText.toString()));
} else if (qName.equals("ETag")) {
@ -88,26 +103,25 @@ public class ListBucketHandler extends ParseSax.HandlerWithResult<S3Bucket> {
} else if (qName.equals("StorageClass")) {
currentObjectMetadata.setStorageClass(currentText.toString());
} else if (qName.equals("Contents")) {
s3Bucket.getContents().add(currentObjectMetadata);
contents.add(currentObjectMetadata);
} else if (qName.equals("Name")) {
s3Bucket.setName(currentText.toString());
this.bucketName = currentText.toString();
} else if (qName.equals("Prefix")) {
String prefix = currentText.toString().trim();
if (inCommonPrefixes)
s3Bucket.getCommonPrefixes().add(prefix);
commonPrefixes.add(prefix);
else
s3Bucket.setPrefix(prefix);
this.prefix = prefix;
} else if (qName.equals("Delimiter")) {
if (!currentText.toString().equals(""))
s3Bucket.setDelimiter(currentText.toString().trim());
this.delimiter = currentText.toString().trim();
} else if (qName.equals("Marker")) {
if (!currentText.toString().equals(""))
s3Bucket.setMarker(currentText.toString());
this.marker = currentText.toString();
} else if (qName.equals("MaxKeys")) {
s3Bucket.setMaxKeys(Long.parseLong(currentText.toString()));
this.maxResults = Integer.parseInt(currentText.toString());
} else if (qName.equals("IsTruncated")) {
boolean isTruncated = Boolean.parseBoolean(currentText.toString());
s3Bucket.setTruncated(isTruncated);
this.isTruncated = Boolean.parseBoolean(currentText.toString());
}
currentText = new StringBuilder();
}

View File

@ -27,8 +27,9 @@ import java.util.List;
import org.jclouds.aws.domain.AWSError;
import org.jclouds.aws.s3.domain.AccessControlList;
import org.jclouds.aws.s3.domain.S3Bucket;
import org.jclouds.aws.s3.domain.S3Object;
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.xml.ErrorHandler;
import org.jclouds.http.functions.ParseSax;
@ -46,7 +47,7 @@ import com.google.inject.Provider;
public class S3ParserFactory {
@Inject
private GenericParseFactory<List<S3Bucket.Metadata>> parseListAllMyBucketsFactory;
private GenericParseFactory<List<BucketMetadata>> parseListAllMyBucketsFactory;
@VisibleForTesting
public static interface GenericParseFactory<T> {
@ -59,12 +60,12 @@ public class S3ParserFactory {
/**
* @return a parser used to handle {@link org.jclouds.aws.s3.commands.ListOwnedBuckets} responses
*/
public ParseSax<List<S3Bucket.Metadata>> createListBucketsParser() {
public ParseSax<List<BucketMetadata>> createListBucketsParser() {
return parseListAllMyBucketsFactory.create(ListAllMyBucketsHandlerprovider.get());
}
@Inject
private GenericParseFactory<S3Bucket> parseListBucketFactory;
private GenericParseFactory<ListBucketResponse> parseListBucketFactory;
@Inject
Provider<ListBucketHandler> ListBucketHandlerprovider;
@ -72,12 +73,12 @@ public class S3ParserFactory {
/**
* @return a parser used to handle {@link org.jclouds.aws.s3.commands.ListBucket} responses
*/
public ParseSax<S3Bucket> createListBucketParser() {
public ParseSax<ListBucketResponse> createListBucketParser() {
return parseListBucketFactory.create(ListBucketHandlerprovider.get());
}
@Inject
private GenericParseFactory<S3Object.Metadata> parseCopyObjectFactory;
private GenericParseFactory<ObjectMetadata> parseCopyObjectFactory;
@Inject
Provider<CopyObjectHandler> copyObjectHandlerProvider;
@ -85,7 +86,7 @@ public class S3ParserFactory {
/**
* @return a parser used to handle {@link org.jclouds.aws.s3.commands.CopyObject} responses
*/
public ParseSax<S3Object.Metadata> createCopyObjectParser() {
public ParseSax<ObjectMetadata> createCopyObjectParser() {
return parseCopyObjectFactory.create(copyObjectHandlerProvider.get());
}

View File

@ -27,8 +27,9 @@ import java.util.List;
import org.jclouds.aws.domain.AWSError;
import org.jclouds.aws.s3.domain.AccessControlList;
import org.jclouds.aws.s3.domain.S3Bucket;
import org.jclouds.aws.s3.domain.S3Object;
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.xml.AccessControlListHandler;
import org.jclouds.aws.s3.xml.CopyObjectHandler;
import org.jclouds.aws.s3.xml.ListAllMyBucketsHandler;
@ -51,11 +52,11 @@ import com.google.inject.assistedinject.FactoryProvider;
public class S3ParserModule extends AbstractModule {
protected final TypeLiteral<S3ParserFactory.GenericParseFactory<AWSError>> errorTypeLiteral = new TypeLiteral<S3ParserFactory.GenericParseFactory<AWSError>>() {
};
protected final TypeLiteral<S3ParserFactory.GenericParseFactory<List<S3Bucket.Metadata>>> listBucketsTypeLiteral = new TypeLiteral<S3ParserFactory.GenericParseFactory<List<S3Bucket.Metadata>>>() {
protected final TypeLiteral<S3ParserFactory.GenericParseFactory<List<BucketMetadata>>> listBucketsTypeLiteral = new TypeLiteral<S3ParserFactory.GenericParseFactory<List<BucketMetadata>>>() {
};
protected final TypeLiteral<S3ParserFactory.GenericParseFactory<S3Bucket>> bucketTypeLiteral = new TypeLiteral<S3ParserFactory.GenericParseFactory<S3Bucket>>() {
protected final TypeLiteral<S3ParserFactory.GenericParseFactory<ListBucketResponse>> bucketTypeLiteral = new TypeLiteral<S3ParserFactory.GenericParseFactory<ListBucketResponse>>() {
};
protected final TypeLiteral<S3ParserFactory.GenericParseFactory<S3Object.Metadata>> objectMetadataTypeLiteral = new TypeLiteral<S3ParserFactory.GenericParseFactory<S3Object.Metadata>>() {
protected final TypeLiteral<S3ParserFactory.GenericParseFactory<ObjectMetadata>> objectMetadataTypeLiteral = new TypeLiteral<S3ParserFactory.GenericParseFactory<ObjectMetadata>>() {
};
protected final TypeLiteral<S3ParserFactory.GenericParseFactory<AccessControlList>> accessControlListTypeLiteral = new TypeLiteral<S3ParserFactory.GenericParseFactory<AccessControlList>>() {
};
@ -73,11 +74,11 @@ public class S3ParserModule extends AbstractModule {
}
private void bindParserImplementationsToReturnTypes() {
bind(new TypeLiteral<ParseSax.HandlerWithResult<List<S3Bucket.Metadata>>>() {
bind(new TypeLiteral<ParseSax.HandlerWithResult<List<BucketMetadata>>>() {
}).to(ListAllMyBucketsHandler.class);
bind(new TypeLiteral<ParseSax.HandlerWithResult<S3Bucket>>() {
bind(new TypeLiteral<ParseSax.HandlerWithResult<ListBucketResponse>>() {
}).to(ListBucketHandler.class);
bind(new TypeLiteral<ParseSax.HandlerWithResult<S3Object.Metadata>>() {
bind(new TypeLiteral<ParseSax.HandlerWithResult<ObjectMetadata>>() {
}).to(CopyObjectHandler.class);
bind(new TypeLiteral<ParseSax.HandlerWithResult<AccessControlList>>() {
}).to(AccessControlListHandler.class);
@ -86,14 +87,15 @@ public class S3ParserModule extends AbstractModule {
private void bindCallablesThatReturnParseResults() {
bind(listBucketsTypeLiteral).toProvider(
FactoryProvider.newFactory(listBucketsTypeLiteral,
new TypeLiteral<ParseSax<List<S3Bucket.Metadata>>>() {
new TypeLiteral<ParseSax<List<BucketMetadata>>>() {
}));
bind(bucketTypeLiteral).toProvider(
FactoryProvider.newFactory(bucketTypeLiteral, new TypeLiteral<ParseSax<S3Bucket>>() {
FactoryProvider.newFactory(bucketTypeLiteral,
new TypeLiteral<ParseSax<ListBucketResponse>>() {
}));
bind(objectMetadataTypeLiteral).toProvider(
FactoryProvider.newFactory(objectMetadataTypeLiteral,
new TypeLiteral<ParseSax<S3Object.Metadata>>() {
new TypeLiteral<ParseSax<ObjectMetadata>>() {
}));
bind(accessControlListTypeLiteral).toProvider(
FactoryProvider.newFactory(accessControlListTypeLiteral,

View File

@ -25,21 +25,29 @@ package org.jclouds.aws.s3;
import static org.jclouds.aws.reference.AWSConstants.PROPERTY_AWS_ACCESSKEYID;
import static org.jclouds.aws.reference.AWSConstants.PROPERTY_AWS_SECRETACCESSKEY;
import static org.jclouds.blobstore.reference.BlobStoreConstants.PROPERTY_USER_METADATA_PREFIX;
import static org.jclouds.http.HttpConstants.PROPERTY_HTTP_ADDRESS;
import static org.testng.Assert.assertEquals;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import org.jclouds.aws.s3.config.RestS3ConnectionModule;
import org.jclouds.aws.s3.config.S3ContextModule;
import org.jclouds.aws.s3.domain.ObjectMetadata;
import org.jclouds.aws.s3.domain.S3Object;
import org.jclouds.aws.s3.internal.GuiceS3Context;
import org.jclouds.aws.s3.xml.config.S3ParserModule;
import org.jclouds.blobstore.functions.ParseBlobFromHeadersAndHttpContent.BlobFactory;
import org.jclouds.blobstore.functions.ParseBlobMetadataFromHeaders.BlobMetadataFactory;
import org.jclouds.cloud.CloudContext;
import org.testng.annotations.Test;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Module;
import com.google.inject.TypeLiteral;
/**
* Tests behavior of modules configured in S3ContextBuilder
@ -51,20 +59,31 @@ public class S3ContextBuilderTest {
public void testNewBuilder() {
S3ContextBuilder builder = S3ContextBuilder.newBuilder("id", "secret");
assertEquals(builder.getProperties().getProperty(PROPERTY_USER_METADATA_PREFIX),
"x-amz-meta-");
assertEquals(builder.getProperties().getProperty(PROPERTY_HTTP_ADDRESS), "s3.amazonaws.com");
assertEquals(builder.getProperties().getProperty(PROPERTY_AWS_ACCESSKEYID), "id");
assertEquals(builder.getProperties().getProperty(PROPERTY_AWS_SECRETACCESSKEY), "secret");
}
public void testBuildContext() {
CloudContext<S3Connection> context = S3ContextBuilder.newBuilder("id", "secret")
CloudContext<S3BlobStore> context = S3ContextBuilder.newBuilder("id", "secret")
.buildContext();
assertEquals(context.getClass(), GuiceS3Context.class);
assertEquals(context.getAccount(), "id");
assertEquals(context.getEndPoint(), URI.create("https://s3.amazonaws.com:443"));
}
public void testBuildInjector() {
Injector i = S3ContextBuilder.newBuilder("id", "secret").buildInjector();
assert i.getInstance(S3Context.class) != null;
assert i.getInstance(GuiceS3Context.S3ObjectMapFactory.class) != null;
assert i.getInstance(GuiceS3Context.S3InputStreamMapFactory.class) != null;
assert i.getInstance(Key.get(new TypeLiteral<BlobMetadataFactory<ObjectMetadata>>() {
})) != null;
assert i.getInstance(Key.get(new TypeLiteral<BlobFactory<ObjectMetadata, S3Object>>() {
})) != null;
}
protected void testAddParserModule() {

View File

@ -1,355 +0,0 @@
/**
*
* 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.options.PutBucketOptions.Builder.createIn;
import static org.testng.Assert.assertEquals;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.jclouds.aws.s3.config.StubS3ConnectionModule;
import org.jclouds.aws.s3.domain.S3Bucket;
import org.jclouds.aws.s3.domain.S3Object;
import org.jclouds.aws.s3.domain.S3Bucket.Metadata;
import org.jclouds.aws.s3.domain.S3Bucket.Metadata.LocationConstraint;
import org.jclouds.aws.s3.reference.S3Constants;
import org.jclouds.aws.s3.util.S3Utils;
import org.jclouds.http.HttpUtils;
import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule;
import org.jclouds.logging.log4j.config.Log4JLoggingModule;
import org.jclouds.util.Utils;
import org.testng.ITestContext;
import org.testng.annotations.AfterGroups;
import org.testng.annotations.BeforeGroups;
import org.testng.annotations.Optional;
import org.testng.annotations.Parameters;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.inject.Module;
public class S3IntegrationTest {
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 sysAWSAccessKeyId = System
.getProperty(S3Constants.PROPERTY_AWS_ACCESSKEYID);
protected static final String sysAWSSecretAccessKey = System
.getProperty(S3Constants.PROPERTY_AWS_SECRETACCESSKEY);
public static long INCONSISTENCY_WINDOW = 1000;
protected static int bucketCount = 20;
protected static volatile int bucketIndex = 0;
protected byte[] goodETag;
protected byte[] badETag;
protected S3Connection client;
protected S3Context context = null;
protected boolean SANITY_CHECK_RETURNED_BUCKET_NAME = false;
private String bucketPrefix = System.getProperty("user.name") + ".s3int";
/**
* two test groups integration and live.
*/
private static final BlockingQueue<String> bucketNames = new ArrayBlockingQueue<String>(
bucketCount);
/**
* Due to eventual consistency, bucket commands may not return correctly immediately. Hence, we
* will try up to the inconsistency window to see if the assertion completes.
*/
protected void assertEventually(Runnable assertion) throws InterruptedException {
AssertionError error = null;
for (int i = 0; i < 5; i++) {
try {
assertion.run();
return;
} catch (AssertionError e) {
error = e;
}
Thread.sleep(INCONSISTENCY_WINDOW / 5);
}
if (error != null)
throw error;
}
protected void createBucketAndEnsureEmpty(String bucketName) throws InterruptedException,
ExecutionException, TimeoutException {
client.putBucketIfNotExists(bucketName).get(10, TimeUnit.SECONDS);
emptyBucket(bucketName);
}
protected void addObjectToBucket(String sourceBucket, String key) throws InterruptedException,
ExecutionException, TimeoutException, IOException {
S3Object sourceObject = new S3Object(key);
sourceObject.getMetadata().setContentType("text/xml");
sourceObject.setData(TEST_STRING);
addObjectToBucket(sourceBucket, sourceObject);
}
protected void addObjectToBucket(String sourceBucket, S3Object object)
throws InterruptedException, ExecutionException, TimeoutException, IOException {
client.putObject(sourceBucket, object).get(10, TimeUnit.SECONDS);
}
protected S3Object validateContent(String sourceBucket, String key) throws InterruptedException,
ExecutionException, TimeoutException, IOException {
assertEventuallyBucketSize(sourceBucket, 1);
S3Object newObject = client.getObject(sourceBucket, key).get(10, TimeUnit.SECONDS);
assert newObject != S3Object.NOT_FOUND;
assertEquals(S3Utils.getContentAsStringAndClose(newObject), TEST_STRING);
return newObject;
}
protected void assertEventuallyBucketSize(final String bucketName, final int count)
throws InterruptedException {
assertEventually(new Runnable() {
public void run() {
try {
assertEquals(client.listBucket(bucketName).get(10, TimeUnit.SECONDS).getContents()
.size(), count);
} catch (Exception e) {
Utils.<RuntimeException> rethrowIfRuntimeOrSameType(e);
}
}
});
}
@BeforeGroups(groups = { "integration", "live" })
@Parameters( { S3Constants.PROPERTY_AWS_ACCESSKEYID, S3Constants.PROPERTY_AWS_SECRETACCESSKEY })
public void setUpCredentials(@Optional String AWSAccessKeyId,
@Optional String AWSSecretAccessKey, ITestContext testContext) throws Exception {
AWSAccessKeyId = AWSAccessKeyId != null ? AWSAccessKeyId : sysAWSAccessKeyId;
AWSSecretAccessKey = AWSSecretAccessKey != null ? AWSSecretAccessKey : sysAWSSecretAccessKey;
if (AWSAccessKeyId != null)
testContext.setAttribute(S3Constants.PROPERTY_AWS_ACCESSKEYID, AWSAccessKeyId);
if (AWSSecretAccessKey != null)
testContext.setAttribute(S3Constants.PROPERTY_AWS_SECRETACCESSKEY, AWSSecretAccessKey);
}
@BeforeGroups(dependsOnMethods = { "setUpCredentials" }, groups = { "integration", "live" })
public void setUpClient(ITestContext testContext) throws Exception {
if (testContext.getAttribute(S3Constants.PROPERTY_AWS_ACCESSKEYID) != null) {
String AWSAccessKeyId = (String) testContext
.getAttribute(S3Constants.PROPERTY_AWS_ACCESSKEYID);
String AWSSecretAccessKey = (String) testContext
.getAttribute(S3Constants.PROPERTY_AWS_SECRETACCESSKEY);
createLiveS3Context(AWSAccessKeyId, AWSSecretAccessKey);
} else {
createStubS3Context();
}
client = context.getConnection();
assert client != null;
goodETag = HttpUtils.eTag(TEST_STRING);
badETag = HttpUtils.eTag("alf");
}
protected void createStubS3Context() {
context = S3ContextFactory.createS3Context("stub", "stub", new StubS3ConnectionModule());
SANITY_CHECK_RETURNED_BUCKET_NAME = true;
}
protected void createLiveS3Context(String AWSAccessKeyId, String AWSSecretAccessKey) {
context = buildS3ContextFactory(AWSAccessKeyId, AWSSecretAccessKey).buildContext();
}
public String getBucketName() throws InterruptedException, ExecutionException, TimeoutException {
String bucketName = bucketNames.poll(30, TimeUnit.SECONDS);
assert bucketName != null : "unable to get a bucket for the test";
emptyBucket(bucketName);
return bucketName;
}
/**
* a bucket that should be deleted and recreated after the test is complete. This is due to
* having an ACL or otherwise that makes it not compatible with normal buckets
*/
public String getScratchBucketName() throws InterruptedException, ExecutionException,
TimeoutException {
return getBucketName();
}
public void returnBucket(final String bucketName) throws InterruptedException,
ExecutionException, TimeoutException {
if (bucketName != null) {
bucketNames.add(bucketName);
/*
* Ensure that any returned bucket name actually exists on the server. Return of a
* non-existent bucket introduces subtle testing bugs, where later unrelated tests will
* fail.
*
* NOTE: This sanity check should only be run for Stub-based Integration testing -- it will
* *substantially* slow down tests on a real server over a network.
*/
if (SANITY_CHECK_RETURNED_BUCKET_NAME) {
if (!Iterables.any(client.listOwnedBuckets(), new Predicate<Metadata>() {
public boolean apply(Metadata md) {
return bucketName.equals(md.getName());
}
})) {
throw new IllegalStateException("Test returned the name of a non-existent bucket: "
+ bucketName);
}
}
}
}
/**
* abandon old bucket name instead of waiting for the bucket to be created.
*/
public void returnScratchBucket(String scratchBucket) throws InterruptedException,
ExecutionException, TimeoutException {
if (scratchBucket != null) {
deleteBucket(scratchBucket);
String newScratchBucket = bucketPrefix + (++bucketIndex);
createBucketAndEnsureEmpty(newScratchBucket);
returnBucket(newScratchBucket);
}
}
/**
* There are a lot of retries here mainly from experience running inside amazon EC2.
*/
@BeforeGroups(dependsOnMethods = { "setUpClient" }, groups = { "integration", "live" })
public void setUpBuckets(ITestContext context) throws Exception {
synchronized (bucketNames) {
if (bucketNames.peek() == null) {
deleteEverything();
for (; bucketIndex < bucketCount; bucketIndex++) {
String bucketName = bucketPrefix + bucketIndex;
try {
createBucketAndEnsureEmpty(bucketName);
bucketNames.put(bucketName);
} catch (AssertionError e) {
// throw away the bucket and try again with the next index
deleteBucket(bucketName);
bucketCount++;
}
}
}
}
}
protected S3ContextBuilder buildS3ContextFactory(String AWSAccessKeyId, String AWSSecretAccessKey) {
return (S3ContextBuilder) S3ContextBuilder.newBuilder(AWSAccessKeyId, AWSSecretAccessKey)
.withSaxDebug().relaxSSLHostname().withModule(new Log4JLoggingModule());
}
protected Module createHttpModule() {
return new JavaUrlHttpCommandExecutorServiceModule();
}
protected void deleteEverything() throws Exception {
try {
List<S3Bucket.Metadata> metadata = client.listOwnedBuckets();
for (S3Bucket.Metadata metaDatum : metadata) {
if (metaDatum.getName().startsWith(bucketPrefix.toLowerCase())) {
deleteBucket(metaDatum.getName());
}
}
} catch (CancellationException e) {
throw e;
}
}
/**
* Remove any objects in a bucket, leaving it empty.
*/
protected void emptyBucket(final String name) throws InterruptedException, ExecutionException,
TimeoutException {
if (client.bucketExists(name)) {
// This can fail to be zero length because of stale bucket lists. Ex. client.listBucket()
// 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 bucket deletion
// operation multiple times to ensure we can acheive a zero length bucket.
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 bucket %s, using encoding %s",
keys, map.keySet(), name, LOCAL_ENCODING));
}
} catch (Exception e) {
Utils.<RuntimeException> rethrowIfRuntimeOrSameType(e);
}
}
});
}
}
protected String createScratchBucketInEU() throws InterruptedException, ExecutionException,
TimeoutException {
String bucketName = getScratchBucketName();
deleteBucket(bucketName);
client.putBucketIfNotExists(bucketName, createIn(LocationConstraint.EU)).get(10,
TimeUnit.SECONDS);
return bucketName;
}
/**
* Empty and delete a bucket.
*
* @param name
* @throws InterruptedException
* @throws ExecutionException
* @throws TimeoutException
*/
protected void deleteBucket(final String name) throws InterruptedException, ExecutionException,
TimeoutException {
if (client.bucketExists(name)) {
emptyBucket(name);
client.deleteBucketIfEmpty(name);
assertEventually(new Runnable() {
public void run() {
try {
assert !client.bucketExists(name) : "bucket " + name + " still exists";
} catch (Exception e) {
Utils.<RuntimeException> rethrowIfRuntimeOrSameType(e);
}
}
});
}
}
@AfterGroups(groups = { "integration", "live" })
protected void tearDownClient() throws Exception {
context.close();
context = null;
}
}

View File

@ -1,219 +0,0 @@
/**
*
* 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.commands;
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 java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.jclouds.aws.s3.S3IntegrationTest;
import org.jclouds.aws.s3.domain.S3Object;
import org.jclouds.aws.s3.reference.S3Headers;
import org.jclouds.http.HttpResponseException;
import org.joda.time.DateTime;
import org.testng.annotations.Test;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
/**
* Tests integrated functionality of all copyObject commands.
* <p/>
* Each test uses a different bucket name, so it should be perfectly fine to run in parallel.
*
* @author Adrian Cole
*/
@Test(testName = "s3.CopyObjectIntegrationTest")
public class CopyObjectIntegrationTest extends S3IntegrationTest {
String sourceKey = "apples";
String destinationKey = "pears";
@Test(groups = { "integration", "live" })
void testCopyObject() throws Exception {
String bucketName = getBucketName();
String destinationBucket = getScratchBucketName();
try {
addToBucketAndValidate(bucketName, sourceKey);
client.copyObject(bucketName, sourceKey, destinationBucket, destinationKey).get(10,
TimeUnit.SECONDS);
validateContent(destinationBucket, destinationKey);
} finally {
returnBucket(bucketName);
returnScratchBucket(destinationBucket);
}
}
private void addToBucketAndValidate(String bucketName, String sourceKey)
throws InterruptedException, ExecutionException, TimeoutException, IOException {
addObjectToBucket(bucketName, sourceKey);
validateContent(bucketName, sourceKey);
}
@Test(enabled = false, groups = { "integration", "live" })
// TODO: fails on linux and windows
void testCopyIfModifiedSince() throws InterruptedException, ExecutionException,
TimeoutException, IOException {
String bucketName = getBucketName();
String destinationBucket = getScratchBucketName();
try {
DateTime before = new DateTime();
addToBucketAndValidate(bucketName, sourceKey);
DateTime after = new DateTime().plusSeconds(1);
client.copyObject(bucketName, sourceKey, destinationBucket, destinationKey,
ifSourceModifiedSince(before)).get(10, TimeUnit.SECONDS);
validateContent(destinationBucket, destinationKey);
try {
client.copyObject(bucketName, sourceKey, destinationBucket, destinationKey,
ifSourceModifiedSince(after)).get(10, TimeUnit.SECONDS);
} catch (ExecutionException e) {
HttpResponseException ex = (HttpResponseException) e.getCause();
assertEquals(ex.getResponse().getStatusCode(), 412);
}
} finally {
returnBucket(bucketName);
returnScratchBucket(destinationBucket);
}
}
@Test(enabled = false, groups = { "integration", "live" })
// TODO: fails on linux and windows
void testCopyIfUnmodifiedSince() throws InterruptedException, ExecutionException,
TimeoutException, IOException {
String bucketName = getBucketName();
String destinationBucket = getScratchBucketName();
try {
DateTime before = new DateTime();
addToBucketAndValidate(bucketName, sourceKey);
DateTime after = new DateTime().plusSeconds(1);
client.copyObject(bucketName, sourceKey, destinationBucket, destinationKey,
ifSourceUnmodifiedSince(after)).get(10, TimeUnit.SECONDS);
validateContent(destinationBucket, destinationKey);
try {
client.copyObject(bucketName, sourceKey, destinationBucket, destinationKey,
ifSourceModifiedSince(before)).get(10, TimeUnit.SECONDS);
} catch (ExecutionException e) {
HttpResponseException ex = (HttpResponseException) e.getCause();
assertEquals(ex.getResponse().getStatusCode(), 412);
}
} finally {
returnBucket(bucketName);
returnScratchBucket(destinationBucket);
}
}
@Test(groups = { "integration", "live" })
void testCopyIfMatch() throws InterruptedException, ExecutionException, TimeoutException,
IOException {
String bucketName = getBucketName();
String destinationBucket = getScratchBucketName();
try {
addToBucketAndValidate(bucketName, sourceKey);
client.copyObject(bucketName, sourceKey, destinationBucket, destinationKey,
ifSourceETagMatches(goodETag)).get(10, TimeUnit.SECONDS);
validateContent(destinationBucket, destinationKey);
try {
client.copyObject(bucketName, sourceKey, destinationBucket, destinationKey,
ifSourceETagMatches(badETag)).get(10, TimeUnit.SECONDS);
} catch (ExecutionException e) {
HttpResponseException ex = (HttpResponseException) e.getCause();
assertEquals(ex.getResponse().getStatusCode(), 412);
}
} finally {
returnBucket(bucketName);
returnScratchBucket(destinationBucket);
}
}
@Test(groups = { "integration", "live" })
void testCopyIfNoneMatch() throws IOException, InterruptedException, ExecutionException,
TimeoutException {
String bucketName = getBucketName();
String destinationBucket = getScratchBucketName();
try {
addToBucketAndValidate(bucketName, sourceKey);
client.copyObject(bucketName, sourceKey, destinationBucket, destinationKey,
ifSourceETagDoesntMatch(badETag)).get(10, TimeUnit.SECONDS);
validateContent(destinationBucket, destinationKey);
try {
client.copyObject(bucketName, sourceKey, destinationBucket, destinationKey,
ifSourceETagDoesntMatch(goodETag)).get(10, TimeUnit.SECONDS);
} catch (ExecutionException e) {
HttpResponseException ex = (HttpResponseException) e.getCause();
assertEquals(ex.getResponse().getStatusCode(), 412);
}
} finally {
returnBucket(bucketName);
returnScratchBucket(destinationBucket);
}
}
@Test(groups = { "integration", "live" })
void testCopyWithMetadata() throws InterruptedException, ExecutionException, TimeoutException,
IOException {
String bucketName = getBucketName();
String destinationBucket = getScratchBucketName();
try {
addToBucketAndValidate(bucketName, sourceKey);
Multimap<String, String> metadata = HashMultimap.create();
metadata.put(S3Headers.USER_METADATA_PREFIX + "adrian", "cole");
client.copyObject(bucketName, sourceKey, destinationBucket, destinationKey,
overrideMetadataWith(metadata)).get(10, TimeUnit.SECONDS);
validateContent(destinationBucket, destinationKey);
S3Object.Metadata objectMeta = client.headObject(destinationBucket, destinationKey);
assertEquals(objectMeta.getUserMetadata(), metadata);
} finally {
returnBucket(bucketName);
returnScratchBucket(destinationBucket);
}
}
}

View File

@ -1,79 +0,0 @@
/**
*
* 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.commands;
import org.jclouds.aws.s3.S3IntegrationTest;
import org.jclouds.util.Utils;
import org.testng.annotations.Test;
/**
* Tests integrated functionality of all deleteBucket commands.
* <p/>
* Each test uses a different bucket name, so it should be perfectly fine to run in parallel.
*
* @author Adrian Cole
*/
@Test(groups = { "integration", "live" }, testName = "s3.DeleteBucketIntegrationTest")
public class DeleteBucketIntegrationTest extends S3IntegrationTest {
/**
* this method overrides bucketName to ensure it isn't found
*/
@Test
void deleteBucketIfEmptyNotFound() throws Exception {
assert client.deleteBucketIfEmpty("dbienf");
}
@Test
void deleteBucketIfEmptyButHasContents() throws Exception {
String bucketName = getBucketName();
try {
addObjectToBucket(bucketName, "test");
assert !client.deleteBucketIfEmpty(bucketName);
} finally {
returnBucket(bucketName);
}
}
@Test
void deleteBucketIfEmpty() throws Exception {
final String bucketName = getScratchBucketName();
try {
assert client.deleteBucketIfEmpty(bucketName);
assertEventually(new Runnable() {
public void run() {
try {
assert !client.bucketExists(bucketName) : "bucket " + bucketName
+ " still exists";
} catch (Exception e) {
Utils.<RuntimeException> rethrowIfRuntimeOrSameType(e);
}
}
});
} finally {
returnScratchBucket(bucketName);
}
}
}

View File

@ -1,94 +0,0 @@
/**
*
* 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.commands;
import static org.testng.Assert.assertEquals;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.jclouds.aws.AWSResponseException;
import org.jclouds.aws.s3.S3IntegrationTest;
import org.jclouds.aws.s3.domain.S3Bucket;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
/**
* Tests integrated functionality of all deleteObject commands.
* <p/>
* Each test uses a different bucket name, so it should be perfectly fine to run in parallel.
*
* @author Adrian Cole
*/
@Test(groups = { "integration", "live" }, testName = "s3.DeleteObjectIntegrationTest")
public class DeleteObjectIntegrationTest extends S3IntegrationTest {
@Test
void deleteObjectNotFound() throws Exception {
String bucketName = getBucketName();
String key = "test";
try {
assert client.deleteObject(bucketName, key).get(10, TimeUnit.SECONDS);
} finally {
returnBucket(bucketName);
}
}
@DataProvider(name = "delete")
public Object[][] createData() {
return new Object[][] { { "sp ace" }, { "unic¿de" }, { "qu?stion" } };
}
@Test(dataProvider = "delete")
void deleteObject(String key) throws Exception {
String bucketName = getBucketName();
try {
addObjectToBucket(bucketName, key);
assert client.deleteObject(bucketName, key).get(10, TimeUnit.SECONDS);
assertBucketEmptyDeleting(bucketName, key);
} finally {
returnBucket(bucketName);
}
}
private void assertBucketEmptyDeleting(String bucketName, String key)
throws InterruptedException, ExecutionException, TimeoutException {
S3Bucket listing = client.listBucket(bucketName).get(10, TimeUnit.SECONDS);
assertEquals(listing.getContents().size(), 0, String.format(
"deleting %s, we still have %s left in bucket %s, using encoding %s", key, listing
.getContents().size(), bucketName, LOCAL_ENCODING));
}
@Test
void deleteObjectNoBucket() throws Exception {
try {
client.deleteObject("donb", "test").get(10, TimeUnit.SECONDS);
} catch (ExecutionException e) {
assert e.getCause() instanceof AWSResponseException;
assertEquals(((AWSResponseException) e.getCause()).getResponse().getStatusCode(), 404);
}
}
}

View File

@ -1,159 +0,0 @@
/**
*
* 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.commands;
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.S3IntegrationTest;
import org.jclouds.aws.s3.domain.AccessControlList;
import org.jclouds.aws.s3.domain.CannedAccessPolicy;
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.options.PutObjectOptions;
import org.jclouds.util.Utils;
import org.testng.annotations.Test;
/**
* Tests integrated functionality of all commands that retrieve Access Control Lists (ACLs).
*
* @author James Murty
* @author Adrian Cole
*/
@Test(groups = { "integration", "live" }, testName = "s3.GetAccessControlListIntegrationTest")
public class GetAccessControlListIntegrationTest extends S3IntegrationTest {
@Test
void testPrivateAclIsDefaultForBucket() throws InterruptedException, ExecutionException,
TimeoutException, IOException {
String bucketName = getBucketName();
try {
AccessControlList acl = client.getBucketACL(bucketName).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 {
returnBucket(bucketName);
}
}
@Test
void testPrivateAclIsDefaultForObject() throws InterruptedException, ExecutionException,
TimeoutException, IOException {
String privateObjectKey = "private-acl";
String bucketName = getBucketName();
try {
// Private object
addObjectToBucket(bucketName, privateObjectKey);
AccessControlList acl = client.getObjectACL(bucketName, 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 {
returnBucket(bucketName);
}
}
@Test
void testPublicReadOnObject() throws InterruptedException, ExecutionException, TimeoutException,
IOException {
final String publicReadObjectKey = "public-read-acl";
final String bucketName = getBucketName();
try {
client.putObject(bucketName, new S3Object(publicReadObjectKey, ""), new PutObjectOptions()
.withAcl(CannedAccessPolicy.PUBLIC_READ));
assertEventually(new Runnable() {
public void run() {
try {
AccessControlList acl = client.getObjectACL(bucketName, 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 {
returnBucket(bucketName);
}
}
@Test
void testPublicWriteOnObject() throws InterruptedException, ExecutionException,
TimeoutException, IOException {
final String publicReadWriteObjectKey = "public-read-write-acl";
final String bucketName = getBucketName();
try {
// Public Read-Write object
client.putObject(bucketName, new S3Object(publicReadWriteObjectKey, ""),
new PutObjectOptions().withAcl(CannedAccessPolicy.PUBLIC_READ_WRITE));
assertEventually(new Runnable() {
public void run() {
try {
AccessControlList acl = client.getObjectACL(bucketName, 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 {
returnBucket(bucketName);
}
}
}

View File

@ -1,265 +0,0 @@
/**
*
* 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.commands;
import static org.jclouds.http.options.GetOptions.Builder.ifETagDoesntMatch;
import static org.jclouds.http.options.GetOptions.Builder.ifETagMatches;
import static org.jclouds.http.options.GetOptions.Builder.ifModifiedSince;
import static org.jclouds.http.options.GetOptions.Builder.ifUnmodifiedSince;
import static org.jclouds.http.options.GetOptions.Builder.range;
import static org.jclouds.http.options.GetOptions.Builder.startAt;
import static org.jclouds.http.options.GetOptions.Builder.tail;
import static org.testng.Assert.assertEquals;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.jclouds.aws.s3.S3IntegrationTest;
import org.jclouds.aws.s3.domain.S3Object;
import org.jclouds.aws.s3.util.S3Utils;
import org.jclouds.http.HttpResponseException;
import org.joda.time.DateTime;
import org.testng.annotations.Test;
/**
* Tests integrated functionality of all GetObject commands.
* <p/>
* Each test uses a different bucket name, so it should be perfectly fine to run in parallel.
*
* @author Adrian Cole
*/
@Test(groups = { "integration", "live" }, testName = "s3.GetObjectIntegrationTest")
public class GetObjectIntegrationTest extends S3IntegrationTest {
@Test
void testGetIfModifiedSince() throws InterruptedException, ExecutionException, TimeoutException,
IOException {
String bucketName = getBucketName();
try {
String key = "apples";
DateTime before = new DateTime();
addObjectAndValidateContent(bucketName, key);
DateTime after = new DateTime().plusSeconds(1);
client.getObject(bucketName, key, ifModifiedSince(before)).get(10, TimeUnit.SECONDS);
validateContent(bucketName, key);
try {
client.getObject(bucketName, key, ifModifiedSince(after)).get(10, TimeUnit.SECONDS);
validateContent(bucketName, key);
} catch (ExecutionException e) {
if (e.getCause() instanceof HttpResponseException) {
HttpResponseException ex = (HttpResponseException) e.getCause();
assertEquals(ex.getResponse().getStatusCode(), 304);
} else if (e.getCause() instanceof RuntimeException) {
// TODO enhance stub connection so that it throws the correct error
} else {
throw e;
}
}
} finally {
returnBucket(bucketName);
}
}
@Test
void testGetIfUnmodifiedSince() throws InterruptedException, ExecutionException,
TimeoutException, IOException {
String bucketName = getBucketName();
try {
String key = "apples";
DateTime before = new DateTime();
addObjectAndValidateContent(bucketName, key);
DateTime after = new DateTime().plusSeconds(1);
client.getObject(bucketName, key, ifUnmodifiedSince(after)).get(10, TimeUnit.SECONDS);
validateContent(bucketName, key);
try {
client.getObject(bucketName, key, ifUnmodifiedSince(before)).get(10, TimeUnit.SECONDS);
validateContent(bucketName, key);
} catch (ExecutionException e) {
if (e.getCause() instanceof HttpResponseException) {
HttpResponseException ex = (HttpResponseException) e.getCause();
assertEquals(ex.getResponse().getStatusCode(), 412);
} else if (e.getCause() instanceof RuntimeException) {
// TODO enhance stub connection so that it throws the correct error
} else {
throw e;
}
}
} finally {
returnBucket(bucketName);
}
}
@Test
void testGetIfMatch() throws InterruptedException, ExecutionException, TimeoutException,
IOException {
String bucketName = getBucketName();
try {
String key = "apples";
addObjectAndValidateContent(bucketName, key);
client.getObject(bucketName, key, ifETagMatches(goodETag)).get(10, TimeUnit.SECONDS);
validateContent(bucketName, key);
try {
client.getObject(bucketName, key, ifETagMatches(badETag)).get(10, TimeUnit.SECONDS);
validateContent(bucketName, key);
} catch (ExecutionException e) {
if (e.getCause() instanceof HttpResponseException) {
HttpResponseException ex = (HttpResponseException) e.getCause();
assertEquals(ex.getResponse().getStatusCode(), 412);
} else if (e.getCause() instanceof RuntimeException) {
// TODO enhance stub connection so that it throws the correct error
} else {
throw e;
}
}
} finally {
returnBucket(bucketName);
}
}
@Test
void testGetIfNoneMatch() throws InterruptedException, ExecutionException, TimeoutException,
IOException {
String bucketName = getBucketName();
try {
String key = "apples";
addObjectAndValidateContent(bucketName, key);
client.getObject(bucketName, key, ifETagDoesntMatch(badETag)).get(10, TimeUnit.SECONDS);
validateContent(bucketName, key);
try {
client.getObject(bucketName, key, ifETagDoesntMatch(goodETag)).get(10, TimeUnit.SECONDS);
validateContent(bucketName, key);
} catch (ExecutionException e) {
if (e.getCause() instanceof HttpResponseException) {
HttpResponseException ex = (HttpResponseException) e.getCause();
assertEquals(ex.getResponse().getStatusCode(), 304);
} else {
throw e;
}
}
} finally {
returnBucket(bucketName);
}
}
@Test
void testGetRange() throws InterruptedException, ExecutionException, TimeoutException,
IOException {
String bucketName = getBucketName();
try {
String key = "apples";
addObjectAndValidateContent(bucketName, key);
S3Object object1 = client.getObject(bucketName, key, range(0, 5))
.get(10, TimeUnit.SECONDS);
assertEquals(S3Utils.getContentAsStringAndClose(object1), TEST_STRING.substring(0, 6));
S3Object object2 = client.getObject(bucketName, key, range(6, TEST_STRING.length())).get(
10, TimeUnit.SECONDS);
assertEquals(S3Utils.getContentAsStringAndClose(object2), TEST_STRING.substring(6,
TEST_STRING.length()));
} finally {
returnBucket(bucketName);
}
}
@Test
void testGetTwoRanges() throws InterruptedException, ExecutionException, TimeoutException,
IOException {
String bucketName = getBucketName();
try {
String key = "apples";
addObjectAndValidateContent(bucketName, key);
S3Object object = client.getObject(bucketName, key,
range(0, 5).range(6, TEST_STRING.length())).get(10, TimeUnit.SECONDS);
assertEquals(S3Utils.getContentAsStringAndClose(object), TEST_STRING);
} finally {
returnBucket(bucketName);
}
}
@Test
void testGetTail() throws InterruptedException, ExecutionException, TimeoutException,
IOException {
String bucketName = getBucketName();
try {
String key = "apples";
addObjectAndValidateContent(bucketName, key);
S3Object object = client.getObject(bucketName, key, tail(5)).get(10, TimeUnit.SECONDS);
assertEquals(S3Utils.getContentAsStringAndClose(object), TEST_STRING.substring(TEST_STRING
.length() - 5));
assertEquals(object.getContentLength(), 5);
assertEquals(object.getMetadata().getSize(), TEST_STRING.length());
} finally {
returnBucket(bucketName);
}
}
@Test
void testGetStartAt() throws InterruptedException, ExecutionException, TimeoutException,
IOException {
String bucketName = getBucketName();
try {
String key = "apples";
addObjectAndValidateContent(bucketName, key);
S3Object object = client.getObject(bucketName, key, startAt(5)).get(10, TimeUnit.SECONDS);
assertEquals(S3Utils.getContentAsStringAndClose(object), TEST_STRING.substring(5,
TEST_STRING.length()));
assertEquals(object.getContentLength(), TEST_STRING.length() - 5);
assertEquals(object.getMetadata().getSize(), TEST_STRING.length());
} finally {
returnBucket(bucketName);
}
}
private void addObjectAndValidateContent(String sourcebucketName, String sourceKey)
throws InterruptedException, ExecutionException, TimeoutException, IOException {
addObjectToBucket(sourcebucketName, sourceKey);
validateContent(sourcebucketName, sourceKey);
}
}

View File

@ -1,157 +0,0 @@
/**
*
* 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.commands;
import org.jclouds.aws.s3.S3IntegrationTest;
import static org.jclouds.aws.s3.options.ListBucketOptions.Builder.*;
import org.jclouds.aws.s3.domain.S3Bucket;
import org.jclouds.aws.s3.domain.S3Object;
import static org.testng.Assert.assertEquals;
import org.testng.annotations.Test;
import java.io.UnsupportedEncodingException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* Tests integrated functionality of all getBucket commands.
* <p/>
* Each test uses a different bucket name, so it should be perfectly fine to run in parallel.
*
* @author Adrian Cole
*/
@Test(groups = { "integration", "live" }, testName = "s3.ListBucketIntegrationTest")
public class ListBucketIntegrationTest extends S3IntegrationTest {
@Test()
void testListBucketDelimiter() throws InterruptedException, ExecutionException,
TimeoutException, UnsupportedEncodingException {
String bucketName = getBucketName();
try {
String prefix = "apps";
addTenObjectsUnderPrefix(bucketName, prefix);
add15UnderRoot(bucketName);
S3Bucket bucket = client.listBucket(bucketName, delimiter("/")).get(10, TimeUnit.SECONDS);
assertEquals(bucket.getDelimiter(), "/");
assert !bucket.isTruncated();
assertEquals(bucket.getContents().size(), 15);
assertEquals(bucket.getCommonPrefixes().size(), 1);
} finally {
returnBucket(bucketName);
}
}
private void addAlphabetUnderRoot(String bucketName) throws InterruptedException,
ExecutionException, TimeoutException {
for (char letter = 'a'; letter <= 'z'; letter++) {
client.putObject(bucketName, new S3Object(letter + "", letter + "content")).get(10,
TimeUnit.SECONDS);
}
}
@Test
void testListBucketMarker() throws InterruptedException, ExecutionException, TimeoutException,
UnsupportedEncodingException {
String bucketName = getBucketName();
try {
addAlphabetUnderRoot(bucketName);
S3Bucket bucket = client.listBucket(bucketName, afterMarker("y"))
.get(10, TimeUnit.SECONDS);
assertEquals(bucket.getMarker(), "y");
assert !bucket.isTruncated();
assertEquals(bucket.getContents().size(), 1);
} finally {
returnBucket(bucketName);
}
}
@Test
void testListBucketMaxResults() throws InterruptedException, ExecutionException,
TimeoutException, UnsupportedEncodingException {
String bucketName = getBucketName();
try {
addAlphabetUnderRoot(bucketName);
S3Bucket bucket = client.listBucket(bucketName, maxResults(5)).get(10, TimeUnit.SECONDS);
assertEquals(bucket.getMaxKeys(), 5);
assert bucket.isTruncated();
assertEquals(bucket.getContents().size(), 5);
} finally {
returnBucket(bucketName);
}
}
@Test()
void testListBucketPrefix() throws InterruptedException, ExecutionException, TimeoutException,
UnsupportedEncodingException {
String bucketName = getBucketName();
try {
String prefix = "apps";
addTenObjectsUnderPrefix(bucketName, prefix);
add15UnderRoot(bucketName);
S3Bucket bucket = client.listBucket(bucketName, withPrefix("apps/")).get(10,
TimeUnit.SECONDS);
assert !bucket.isTruncated();
assertEquals(bucket.getContents().size(), 10);
assertEquals(bucket.getPrefix(), "apps/");
} finally {
returnBucket(bucketName);
}
}
@Test()
void testListBucket() throws InterruptedException, ExecutionException, TimeoutException,
UnsupportedEncodingException {
String bucketName = getBucketName();
try {
String prefix = "apps";
addTenObjectsUnderPrefix(bucketName, prefix);
S3Bucket bucket = client.listBucket(bucketName).get(10, TimeUnit.SECONDS);
assertEquals(bucket.getContents().size(), 10);
} finally {
returnBucket(bucketName);
}
}
private void add15UnderRoot(String bucketName) throws InterruptedException, ExecutionException,
TimeoutException {
for (int i = 0; i < 15; i++)
client.putObject(bucketName, new S3Object(i + "", i + "content"))
.get(10, TimeUnit.SECONDS);
}
private void addTenObjectsUnderPrefix(String bucketName, String prefix)
throws InterruptedException, ExecutionException, TimeoutException {
for (int i = 0; i < 10; i++)
client.putObject(bucketName, new S3Object(prefix + "/" + i, i + "content")).get(10,
TimeUnit.SECONDS);
}
}

View File

@ -1,143 +0,0 @@
/**
*
* 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.commands;
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.testng.Assert.assertEquals;
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.S3IntegrationTest;
import org.jclouds.aws.s3.domain.AccessControlList;
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.testng.annotations.Test;
/**
* Tests integrated functionality of all commands that retrieve Access Control Lists (ACLs).
*
* @author James Murty
*/
@Test(groups = { "integration", "live" }, testName = "s3.PutAccessControlListIntegrationTest")
public class PutAccessControlListIntegrationTest extends S3IntegrationTest {
@Test
void testUpdateBucketACL() throws InterruptedException, ExecutionException, TimeoutException,
IOException, Exception {
String bucketName = getScratchBucketName();
try {
// Confirm the bucket is private
AccessControlList acl = client.getBucketACL(bucketName).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(client.putBucketACL(bucketName, acl).get(10, TimeUnit.SECONDS));
// Confirm that the updated ACL has stuck.
acl = client.getBucketACL(bucketName).get(10, TimeUnit.SECONDS);
checkGrants(acl);
} finally {
returnScratchBucket(bucketName);
}
}
@Test
void testUpdateObjectACL() throws InterruptedException, ExecutionException, TimeoutException,
IOException {
String bucketName = getBucketName();
try {
String objectKey = "private-acl";
// Private object
addObjectToBucket(bucketName, objectKey);
AccessControlList acl = client.getObjectACL(bucketName, 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(client.putObjectACL(bucketName, objectKey, acl).get(10, TimeUnit.SECONDS));
// Confirm that the updated ACL has stuck.
acl = client.getObjectACL(bucketName, 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(client.putObjectACL(bucketName, objectKey, acl).get(10, TimeUnit.SECONDS));
// Confirm that the updated ACL has stuck
acl = client.getObjectACL(bucketName, 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 {
returnBucket(bucketName);
}
}
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);
}
}

View File

@ -1,116 +0,0 @@
/**
*
* 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.commands;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.IOUtils;
import org.jclouds.aws.s3.S3IntegrationTest;
import org.jclouds.aws.s3.domain.S3Object;
import org.jclouds.aws.s3.reference.S3Headers;
import org.jclouds.aws.s3.util.S3Utils;
import org.jclouds.http.HttpUtils;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
/**
* Tests integrated functionality of all PutObject commands.
* <p/>
* Each test uses a different bucket name, so it should be perfectly fine to run in parallel.
*
* @author Adrian Cole
*/
@Test(testName = "s3.PutObjectIntegrationTest")
public class PutObjectIntegrationTest extends S3IntegrationTest {
@DataProvider(name = "putTests")
public Object[][] createData1() throws IOException {
String realObject = IOUtils.toString(new FileInputStream("pom.xml"));
return new Object[][] { { "file", "text/xml", new File("pom.xml"), realObject },
{ "string", "text/xml", realObject, realObject },
{ "bytes", "application/octet-stream", realObject.getBytes(), realObject } };
}
@Test(dataProvider = "putTests", groups = { "integration", "live" })
void testPutObject(String key, String type, Object content, Object realObject) throws Exception {
S3Object object = new S3Object(key);
object.getMetadata().setContentType(type);
object.setData(content);
if (content instanceof InputStream) {
object.generateETag();
}
String bucketName = getBucketName();
try {
assertNotNull(client.putObject(bucketName, object).get(10, TimeUnit.SECONDS));
object = client.getObject(bucketName, object.getKey()).get(10, TimeUnit.SECONDS);
String returnedString = S3Utils.getContentAsStringAndClose(object);
assertEquals(returnedString, realObject);
assertEquals(client.listBucket(bucketName).get(10, TimeUnit.SECONDS).getContents().size(),
1);
} finally {
returnBucket(bucketName);
}
}
@Test(groups = { "integration", "live" })
void testMetadata() throws Exception {
String key = "hello";
S3Object object = new S3Object(key, TEST_STRING);
object.getMetadata().setCacheControl("no-cache");
object.getMetadata().setContentType("text/plain");
object.getMetadata().setContentEncoding("x-compress");
object.getMetadata().setSize(TEST_STRING.length());
object.getMetadata().setContentDisposition("attachment; filename=hello.txt");
object.getMetadata().getUserMetadata().put(S3Headers.USER_METADATA_PREFIX + "adrian",
"powderpuff");
object.getMetadata().setETag(HttpUtils.eTag(TEST_STRING.getBytes()));
String bucketName = getBucketName();
try {
addObjectToBucket(bucketName, object);
S3Object newObject = validateContent(bucketName, key);
assertEquals(newObject.getMetadata().getContentType(), "text/plain");
assertEquals(newObject.getMetadata().getContentEncoding(), "x-compress");
assertEquals(newObject.getMetadata().getContentDisposition(),
"attachment; filename=hello.txt");
assertEquals(newObject.getMetadata().getCacheControl(), "no-cache");
assertEquals(newObject.getMetadata().getSize(), TEST_STRING.length());
assertEquals(newObject.getMetadata().getUserMetadata().values().iterator().next(),
"powderpuff");
assertEquals(newObject.getMetadata().getETag(), HttpUtils.eTag(TEST_STRING.getBytes()));
} finally {
returnBucket(bucketName);
}
}
}

View File

@ -1,65 +0,0 @@
/**
*
* 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.commands;
import static org.jclouds.aws.s3.options.PutObjectOptions.Builder.withAcl;
import java.net.URL;
import java.util.concurrent.TimeUnit;
import org.jclouds.aws.s3.S3IntegrationTest;
import org.jclouds.aws.s3.domain.CannedAccessPolicy;
import org.jclouds.aws.s3.domain.S3Object;
import org.jclouds.util.Utils;
import org.testng.annotations.Test;
/**
* Tests integrated functionality of all PutObject commands.
* <p/>
* Each test uses a different bucket name, so it should be perfectly fine to run in parallel.
*
* @author Adrian Cole
*/
@Test(testName = "s3.PutObjectLiveTest")
public class PutObjectLiveTest extends S3IntegrationTest {
@Test(groups = { "live" })
void testCannedAccessPolicyPublic() throws Exception {
String bucketName = getBucketName();
try {
String key = "hello";
client.putObject(bucketName, 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", bucketName, key));
Utils.toStringAndClose(url.openStream());
} finally {
returnBucket(bucketName);
}
}
}

View File

@ -25,8 +25,8 @@ package org.jclouds.aws.s3.config;
import java.net.URI;
import org.jclouds.aws.s3.S3Connection;
import org.jclouds.aws.s3.internal.StubS3Connection;
import org.jclouds.aws.s3.S3BlobStore;
import org.jclouds.aws.s3.internal.StubS3BlobStore;
import org.jclouds.cloud.ConfiguresCloudConnection;
import org.jclouds.http.functions.config.ParserModule;
@ -38,10 +38,10 @@ import com.google.inject.AbstractModule;
* @author Adrian Cole
*/
@ConfiguresCloudConnection
public class StubS3ConnectionModule extends AbstractModule {
public class StubS3BlobStoreModule extends AbstractModule {
protected void configure() {
install(new ParserModule());
bind(S3Connection.class).to(StubS3Connection.class);
bind(S3BlobStore.class).to(StubS3BlobStore.class);
bind(URI.class).toInstance(URI.create("http://localhost:8080"));
}
}

View File

@ -111,6 +111,16 @@ public class RequestAuthorizeSignatureTest {
assertEquals(builder.toString(), "");
}
@Test
void testHeadersGoLowercase() {
URI host = URI.create("http://s3.amazonaws.com:80");
HttpRequest request = new HttpRequest(HttpMethod.GET, host);
request.getHeaders().put("x-amz-adrian", "s3.amazonaws.com");
StringBuilder builder = new StringBuilder();
filter.appendBucketName(request, builder);
assertEquals(builder.toString(), "");
}
@Test
void testAppendBucketNameURIHost() {
URI host = URI.create("http://adriancole.s3int5.s3-external-3.amazonaws.com:80");

View File

@ -26,7 +26,7 @@ package org.jclouds.aws.s3.functions;
import static org.testng.Assert.assertEquals;
import org.apache.commons.io.IOUtils;
import org.jclouds.aws.s3.domain.S3Bucket;
import org.jclouds.aws.s3.domain.ListBucketResponse;
import org.jclouds.http.HttpException;
import org.jclouds.http.functions.ParseSax;
import org.testng.annotations.BeforeMethod;
@ -38,15 +38,15 @@ public class ListBucketHandlerTest extends BaseHandlerTest {
public static final String listBucketWithSlashDelimiterAndCommonPrefixApps = "<ListBucketResult xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\"> <Delimiter>/</Delimiter> <CommonPrefixes><Prefix>apps/</Prefix></CommonPrefixes></ListBucketResult>";
@BeforeMethod
ParseSax<S3Bucket> createParser() {
ParseSax<S3Bucket> parser = parserFactory.createListBucketParser();
ParseSax<ListBucketResponse> createParser() {
ParseSax<ListBucketResponse> parser = parserFactory.createListBucketParser();
return parser;
}
@Test
public void testListMyBucketsWithDelimiterSlashAndCommonPrefixesAppsSlash() throws HttpException {
S3Bucket bucket = createParser().parse(
ListBucketResponse bucket = createParser().parse(
IOUtils.toInputStream(listBucketWithSlashDelimiterAndCommonPrefixApps));
assertEquals(bucket.getCommonPrefixes().iterator().next(), "apps/");
assertEquals(bucket.getDelimiter(), "/");
@ -56,9 +56,9 @@ public class ListBucketHandlerTest extends BaseHandlerTest {
@Test
public void testListMyBucketsWithPrefixAppsSlash() throws HttpException {
S3Bucket bucket = createParser().parse(IOUtils.toInputStream(listBucketWithPrefixAppsSlash));
ListBucketResponse bucket = createParser().parse(IOUtils.toInputStream(listBucketWithPrefixAppsSlash));
assertEquals(bucket.getPrefix(), "apps/");
assertEquals(bucket.getMaxKeys(), 1000);
assertEquals(bucket.getMaxResults(), 1000);
assert bucket.getMarker() == null;
}

View File

@ -21,7 +21,7 @@
* under the License.
* ====================================================================
*/
package org.jclouds.aws.s3.commands;
package org.jclouds.aws.s3.functions;
import static org.easymock.EasyMock.expect;
import static org.easymock.classextension.EasyMock.createMock;

View File

@ -0,0 +1,424 @@
/**
*
* 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.integration;
import static org.jclouds.aws.s3.internal.StubS3BlobStore.TEST_ACL_EMAIL;
import static org.jclouds.aws.s3.internal.StubS3BlobStore.TEST_ACL_ID;
import static org.jclouds.aws.s3.options.CopyObjectOptions.Builder.ifSourceETagDoesntMatch;
import static org.jclouds.aws.s3.options.CopyObjectOptions.Builder.ifSourceETagMatches;
import static org.jclouds.aws.s3.options.CopyObjectOptions.Builder.ifSourceModifiedSince;
import static org.jclouds.aws.s3.options.CopyObjectOptions.Builder.ifSourceUnmodifiedSince;
import static org.jclouds.aws.s3.options.CopyObjectOptions.Builder.overrideMetadataWith;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.jclouds.aws.s3.S3BlobStore;
import org.jclouds.aws.s3.domain.AccessControlList;
import org.jclouds.aws.s3.domain.BucketMetadata;
import org.jclouds.aws.s3.domain.CannedAccessPolicy;
import org.jclouds.aws.s3.domain.ObjectMetadata;
import org.jclouds.aws.s3.domain.S3Object;
import org.jclouds.aws.s3.domain.AccessControlList.CanonicalUserGrantee;
import org.jclouds.aws.s3.domain.AccessControlList.EmailAddressGrantee;
import org.jclouds.aws.s3.domain.AccessControlList.GroupGranteeURI;
import org.jclouds.aws.s3.domain.AccessControlList.Permission;
import org.jclouds.aws.s3.options.PutObjectOptions;
import org.jclouds.blobstore.integration.internal.BaseBlobIntegrationTest;
import org.jclouds.http.HttpResponseException;
import org.jclouds.util.Utils;
import org.joda.time.DateTime;
import org.testng.annotations.Test;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
/**
*
* @author James Murty
* @author Adrian Cole
*/
@Test(groups = { "integration", "live" }, testName = "s3.S3BlobIntegrationTest")
public class S3BlobIntegrationTest extends
BaseBlobIntegrationTest<S3BlobStore, 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
client.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 = client
.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 = client.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(client.putBlobACL(containerName, objectKey, acl).get(10, TimeUnit.SECONDS));
// Confirm that the updated ACL has stuck.
acl = client.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(client.putBlobACL(containerName, objectKey, acl).get(10, TimeUnit.SECONDS));
// Confirm that the updated ACL has stuck
acl = client.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 = client.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 {
client.putBlob(containerName, new S3Object(publicReadObjectKey, ""),
new PutObjectOptions().withAcl(CannedAccessPolicy.PUBLIC_READ)).get(10,
TimeUnit.SECONDS);
assertEventually(new Runnable() {
public void run() {
try {
AccessControlList acl = client.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 = objectFactory.createBlob(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 = objectFactory.createBlob(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);
client.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);
client.copyBlob(containerName, sourceKey + "mod", destinationContainer, destinationKey,
ifSourceModifiedSince(before)).get(10, TimeUnit.SECONDS);
validateContent(destinationContainer, destinationKey);
try {
client.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);
client.copyBlob(containerName, sourceKey + "un", destinationContainer, destinationKey,
ifSourceUnmodifiedSince(after)).get(10, TimeUnit.SECONDS);
validateContent(destinationContainer, destinationKey);
try {
client.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);
client.copyBlob(containerName, sourceKey, destinationContainer, destinationKey,
ifSourceETagMatches(goodETag)).get(10, TimeUnit.SECONDS);
validateContent(destinationContainer, destinationKey);
try {
client.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);
client.copyBlob(containerName, sourceKey, destinationContainer, destinationKey,
ifSourceETagDoesntMatch(badETag)).get(10, TimeUnit.SECONDS);
validateContent(destinationContainer, destinationKey);
try {
client.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");
client.copyBlob(containerName, sourceKey, destinationContainer, destinationKey,
overrideMetadataWith(metadata)).get(10, TimeUnit.SECONDS);
validateContent(destinationContainer, destinationKey);
ObjectMetadata objectMeta = client.blobMetadata(destinationContainer, destinationKey);
assertEquals(objectMeta.getUserMetadata(), metadata);
} finally {
returnContainer(containerName);
returnContainer(destinationContainer);
}
}
}

View File

@ -21,51 +21,71 @@
* under the License.
* ====================================================================
*/
package org.jclouds.aws.s3.commands;
package org.jclouds.aws.s3.integration;
import static org.jclouds.aws.s3.options.CopyObjectOptions.Builder.overrideAcl;
import static org.jclouds.aws.s3.options.PutObjectOptions.Builder.withAcl;
import java.net.URL;
import java.util.concurrent.TimeUnit;
import org.jclouds.aws.s3.S3IntegrationTest;
import org.jclouds.aws.s3.S3BlobStore;
import org.jclouds.aws.s3.domain.BucketMetadata;
import org.jclouds.aws.s3.domain.CannedAccessPolicy;
import org.jclouds.aws.s3.domain.ObjectMetadata;
import org.jclouds.aws.s3.domain.S3Object;
import org.jclouds.blobstore.integration.internal.BaseBlobLiveTest;
import org.jclouds.util.Utils;
import org.testng.annotations.Test;
/**
* Tests integrated functionality of all copyObject commands.
* <p/>
* Each test uses a different bucket name, so it should be perfectly fine to run in parallel.
*
* @author James Murty
* @author Adrian Cole
*/
@Test(testName = "s3.CopyObjectLiveTest")
public class CopyObjectLiveTest extends S3IntegrationTest {
@Test(groups = { "live" }, testName = "s3.S3BlobLiveTest")
public class S3BlobLiveTest extends
BaseBlobLiveTest<S3BlobStore, BucketMetadata, ObjectMetadata, S3Object> {
public void testPutCannedAccessPolicyPublic() throws Exception {
String containerName = getContainerName();
try {
String key = "hello";
client.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";
@Test(groups = "live")
void testCannedAccessPolicyPublic() throws Exception {
String bucketName = getBucketName();
String destinationBucket = getScratchBucketName();
public void testCopyCannedAccessPolicyPublic() throws Exception {
String containerName = getContainerName();
String destinationContainer = getContainerName();
try {
addObjectToBucket(bucketName, sourceKey);
validateContent(bucketName, sourceKey);
addBlobToContainer(containerName, sourceKey);
validateContent(containerName, sourceKey);
client.copyObject(bucketName, sourceKey, destinationBucket, destinationKey,
client.copyBlob(containerName, sourceKey, destinationContainer, destinationKey,
overrideAcl(CannedAccessPolicy.PUBLIC_READ)).get(10, TimeUnit.SECONDS);
validateContent(destinationBucket, destinationKey);
validateContent(destinationContainer, destinationKey);
URL url = new URL(String.format("http://%1$s.s3.amazonaws.com/%2$s", destinationBucket,
URL url = new URL(String.format("http://%1$s.s3.amazonaws.com/%2$s", destinationContainer,
destinationKey));
Utils.toStringAndClose(url.openStream());
} finally {
returnBucket(bucketName);
returnScratchBucket(destinationBucket);
returnContainer(containerName);
returnContainer(destinationContainer);
}
}

View File

@ -21,21 +21,20 @@
* under the License.
* ====================================================================
*/
package org.jclouds.aws.s3;
package org.jclouds.aws.s3.integration;
import org.jclouds.aws.s3.S3BlobStore;
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.integration.internal.BaseBlobMapIntegrationTest;
import org.testng.annotations.Test;
/**
* This performs the same test as {@link S3ConnectionLiveTest}, except using SSL.
*
* @author Adrian Cole
*/
@Test(groups = { "live" }, testName = "s3.SecureS3ConnectionLiveTest")
public class SecureS3ConnectionLiveTest extends S3ConnectionLiveTest {
@Override
protected S3ContextBuilder buildS3ContextFactory(String AWSAccessKeyId, String AWSSecretAccessKey) {
return (S3ContextBuilder) S3ContextBuilder.newBuilder(AWSAccessKeyId, AWSSecretAccessKey)
.withHttpSecure(true).relaxSSLHostname().withHttpPort(443);
}
@Test(groups = { "integration", "live" }, testName = "s3.S3BlobMapIntegrationTest")
public class S3BlobMapIntegrationTest extends
BaseBlobMapIntegrationTest<S3BlobStore, BucketMetadata, ObjectMetadata, S3Object> {
}

View File

@ -0,0 +1,187 @@
/**
*
* 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.integration;
import static org.jclouds.aws.s3.internal.StubS3BlobStore.TEST_ACL_EMAIL;
import static org.jclouds.aws.s3.internal.StubS3BlobStore.TEST_ACL_ID;
import static org.jclouds.aws.s3.options.ListBucketOptions.Builder.afterMarker;
import static org.jclouds.aws.s3.options.ListBucketOptions.Builder.delimiter;
import static org.jclouds.aws.s3.options.ListBucketOptions.Builder.maxResults;
import static org.jclouds.aws.s3.options.ListBucketOptions.Builder.withPrefix;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.jclouds.aws.s3.S3BlobStore;
import org.jclouds.aws.s3.domain.AccessControlList;
import org.jclouds.aws.s3.domain.BucketMetadata;
import org.jclouds.aws.s3.domain.ListBucketResponse;
import org.jclouds.aws.s3.domain.ObjectMetadata;
import org.jclouds.aws.s3.domain.S3Object;
import org.jclouds.aws.s3.domain.AccessControlList.CanonicalUserGrantee;
import org.jclouds.aws.s3.domain.AccessControlList.EmailAddressGrantee;
import org.jclouds.aws.s3.domain.AccessControlList.GroupGranteeURI;
import org.jclouds.aws.s3.domain.AccessControlList.Permission;
import org.jclouds.blobstore.integration.internal.BaseContainerIntegrationTest;
import org.testng.annotations.Test;
/**
* @author James Murty
* @author Adrian Cole
*/
@Test(groups = { "integration", "live" }, testName = "s3.S3ContainerIntegrationTest")
public class S3ContainerIntegrationTest extends
BaseContainerIntegrationTest<S3BlobStore, 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 = client.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 = client.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 = client.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 = client.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(client.putContainerACL(containerName, acl).get(10, TimeUnit.SECONDS));
// Confirm that the updated ACL has stuck.
acl = client.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 = client.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 = client.listBlobs(containerName, maxResults(5)).get(10,
TimeUnit.SECONDS);
assertEquals(container.getMaxResults(), 5);
assert container.isTruncated();
assertEquals(container.size(), 5);
} finally {
returnContainer(containerName);
}
}
}

View File

@ -21,7 +21,7 @@
* under the License.
* ====================================================================
*/
package org.jclouds.aws.s3.commands;
package org.jclouds.aws.s3.integration;
import static org.jclouds.aws.s3.options.PutBucketOptions.Builder.createIn;
import static org.jclouds.aws.s3.options.PutBucketOptions.Builder.withBucketAcl;
@ -31,81 +31,71 @@ import java.io.IOException;
import java.net.URL;
import java.util.concurrent.TimeUnit;
import org.jclouds.aws.s3.S3IntegrationTest;
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.CannedAccessPolicy;
import org.jclouds.aws.s3.domain.ObjectMetadata;
import org.jclouds.aws.s3.domain.S3Object;
import org.jclouds.aws.s3.domain.AccessControlList.GroupGranteeURI;
import org.jclouds.aws.s3.domain.AccessControlList.Permission;
import org.jclouds.aws.s3.domain.S3Bucket.Metadata.LocationConstraint;
import org.jclouds.aws.s3.domain.BucketMetadata.LocationConstraint;
import org.jclouds.blobstore.integration.internal.BaseContainerLiveTest;
import org.jclouds.util.Utils;
import org.testng.annotations.Test;
/**
* Tests integrated functionality of all PutBucket commands.
* <p/>
* Each test uses a different bucket name, so it should be perfectly fine to run in parallel.
*
* @author James Murty
* @author Adrian Cole
*/
@Test(testName = "s3.PutBucketLiveTest")
public class PutBucketLiveTest extends S3IntegrationTest {
@Test(groups = { "live" }, testName = "s3.S3ContainerLiveTest")
public class S3ContainerLiveTest extends
BaseContainerLiveTest<S3BlobStore, BucketMetadata, ObjectMetadata, S3Object> {
@Test(groups = { "live" })
void testPublicReadAccessPolicy() throws Exception {
String bucketName = getScratchBucketName();
public void testPublicReadAccessPolicy() throws Exception {
String containerName = getScratchContainerName();
try {
deleteBucket(bucketName);
client.putBucketIfNotExists(bucketName, withBucketAcl(CannedAccessPolicy.PUBLIC_READ))
.get(10, TimeUnit.SECONDS);
AccessControlList acl = client.getBucketACL(bucketName).get(10, TimeUnit.SECONDS);
client.createContainer(containerName, withBucketAcl(CannedAccessPolicy.PUBLIC_READ)).get(
10, TimeUnit.SECONDS);
AccessControlList acl = client.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("https://%s.s3.amazonaws.com", bucketName));
// URL url = new URL(String.format("http://%s.s3.amazonaws.com", containerName));
// Utils.toStringAndClose(url.openStream());
} finally {
returnScratchBucket(bucketName);
destroyContainer(containerName);
}
}
@Test(groups = { "live" })
void testPutTwiceIsOk() throws Exception {
String bucketName = getBucketName();
@Test(expectedExceptions = IOException.class)
public void testDefaultAccessPolicy() throws Exception {
String containerName = getContainerName();
try {
client.putBucketIfNotExists(bucketName).get(10, TimeUnit.SECONDS);
} finally {
returnBucket(bucketName);
}
}
@Test(expectedExceptions = IOException.class, groups = { "live" })
void testDefaultAccessPolicy() throws Exception {
String bucketName = getBucketName();
try {
URL url = new URL(String.format("https://%s.s3.amazonaws.com", bucketName));
URL url = new URL(String.format("https://%s.s3.amazonaws.com", containerName));
Utils.toStringAndClose(url.openStream());
} finally {
returnBucket(bucketName);
returnContainer(containerName);
}
}
/**
* using scratch bucketName as we are changing location
* using scratch containerName as we are changing location
*/
@Test(groups = "live")
void testEu() throws Exception {
final String bucketName = getScratchBucketName();
public void testEu() throws Exception {
final String containerName = getScratchContainerName();
try {
deleteBucket(bucketName);
client.putBucketIfNotExists(bucketName,
client.createContainer(containerName + "eu",
createIn(LocationConstraint.EU).withBucketAcl(CannedAccessPolicy.PUBLIC_READ))
.get(30, TimeUnit.SECONDS);
assertEventually(new Runnable() {
public void run() {
try {
AccessControlList acl = client.getBucketACL(bucketName).get(10, TimeUnit.SECONDS);
assertTrue(acl.hasPermission(GroupGranteeURI.ALL_USERS, Permission.READ), acl.toString());
AccessControlList acl = client.getContainerACL(containerName + "eu").get(30,
TimeUnit.SECONDS);
assertTrue(acl.hasPermission(GroupGranteeURI.ALL_USERS, Permission.READ), acl
.toString());
} catch (Exception e) {
Utils.<RuntimeException> rethrowIfRuntimeOrSameType(e);
}
@ -113,10 +103,10 @@ public class PutBucketLiveTest extends S3IntegrationTest {
});
// 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("https://%s.s3.amazonaws.com", bucketName));
// URL url = new URL(String.format("http://%s.s3.amazonaws.com", containerName));
// Utils.toStringAndClose(url.openStream());
} finally {
returnScratchBucket(bucketName);
destroyContainer(containerName + "eu");
}
}
}

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.aws.s3.integration;
import org.jclouds.aws.s3.S3BlobStore;
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.integration.internal.BaseInputStreamMapIntegrationTest;
import org.testng.annotations.Test;
/**
* @author Adrian Cole
*/
@Test(groups = { "integration", "live" }, testName = "s3.S3InputStreamMapIntegrationTest")
public class S3InputStreamMapIntegrationTest extends
BaseInputStreamMapIntegrationTest<S3BlobStore, BucketMetadata, ObjectMetadata, S3Object> {
}

View File

@ -21,41 +21,34 @@
* under the License.
* ====================================================================
*/
package org.jclouds.aws.s3.commands;
package org.jclouds.aws.s3.integration;
import java.util.List;
import org.jclouds.aws.s3.S3IntegrationTest;
import org.jclouds.aws.s3.domain.S3Bucket;
import org.jclouds.aws.s3.S3BlobStore;
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.integration.internal.BaseServiceIntegrationTest;
import org.testng.annotations.Test;
/**
* Tests integrated functionality of all listOwnedBucket commands.
* <p/>
* Each test uses a different bucket name, so it should be perfectly fine to run in parallel.
*
* @author Adrian Cole
*/
@Test(groups = { "integration", "live" }, testName = "s3.ListOwnedBucketsIntegrationTest")
public class ListOwnedBucketsIntegrationTest extends S3IntegrationTest {
@Test(groups = { "integration", "live" }, testName = "s3.S3ServiceIntegrationTest")
public class S3ServiceIntegrationTest extends
BaseServiceIntegrationTest<S3BlobStore, BucketMetadata, ObjectMetadata, S3Object> {
@Test()
void bucketDoesntExist() throws Exception {
List<S3Bucket.Metadata> list = client.listOwnedBuckets();
assert !list.contains(new S3Bucket("shouldntexist"));
}
@Test()
void bucketExists() throws Exception {
String bucketName = getBucketName();
void containerExists() throws Exception {
String containerName = getContainerName();
try {
List<S3Bucket.Metadata> list = client.listOwnedBuckets();
S3Bucket.Metadata firstBucket = list.get(0);
S3Bucket.Metadata toMatch = new S3Bucket.Metadata(bucketName);
toMatch.setOwner(firstBucket.getOwner());
List<BucketMetadata> list = client.listContainers();
BucketMetadata firstContainer = list.get(0);
BucketMetadata toMatch = objectFactory.createContainerMetadata(containerName);
toMatch.setOwner(firstContainer.getOwner());
assert list.contains(toMatch);
} finally {
returnBucket(bucketName);
returnContainer(containerName);
}
}
}

View File

@ -0,0 +1,128 @@
/**
*
* 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.integration;
import org.jclouds.aws.s3.S3BlobStore;
import org.jclouds.aws.s3.S3Context;
import org.jclouds.aws.s3.S3ContextBuilder;
import org.jclouds.aws.s3.S3ContextFactory;
import org.jclouds.aws.s3.config.StubS3BlobStoreModule;
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.BlobStoreContext;
import org.jclouds.blobstore.integration.internal.BaseBlobStoreIntegrationTest;
import org.jclouds.blobstore.integration.internal.BaseBlobStoreIntegrationTest.BlobStoreObjectFactory;
import org.jclouds.logging.log4j.config.Log4JLoggingModule;
import org.testng.ITestContext;
import com.google.inject.Module;
/**
*
* @author Adrian Cole
*/
public class S3TestInitializer
implements
BaseBlobStoreIntegrationTest.TestInitializer<S3BlobStore, BucketMetadata, ObjectMetadata, S3Object> {
public BaseBlobStoreIntegrationTest.TestInitializer.Result<S3BlobStore, BucketMetadata, ObjectMetadata, S3Object> init(
Module configurationModule, ITestContext testContext) throws Exception {
String account = System.getProperty("jclouds.test.user");
String key = System.getProperty("jclouds.test.key");
if (account != null)
testContext.setAttribute("jclouds.test.user", account);
if (key != null)
testContext.setAttribute("jclouds.test.key", key);
final S3Context context;
if (account != null) {
context = createLiveS3Context(configurationModule, account, key);
} else {
context = createStubS3Context();
}
assert context != null;
final S3BlobStore client = context.getApi();
assert client != null;
final BlobStoreObjectFactory<BucketMetadata, S3Object> objectFactory = new BaseBlobStoreIntegrationTest.BlobStoreObjectFactory<BucketMetadata, S3Object>() {
public S3Object createBlob(String key) {
return new S3Object(key);
}
public BucketMetadata createContainerMetadata(String key) {
return new BucketMetadata(key);
}
};
assert objectFactory != null;
return new BaseBlobStoreIntegrationTest.TestInitializer.Result<S3BlobStore, BucketMetadata, ObjectMetadata, S3Object>() {
public S3BlobStore getClient() {
return client;
}
public BlobStoreContext<S3BlobStore, ObjectMetadata, S3Object> getContext() {
return context;
}
public BlobStoreObjectFactory<BucketMetadata, S3Object> getObjectFactory() {
return objectFactory;
}
};
}
protected S3Context createStubS3Context() {
BaseBlobStoreIntegrationTest.SANITY_CHECK_RETURNED_BUCKET_NAME = true;
return S3ContextFactory.createS3Context("stub", "stub", new StubS3BlobStoreModule());
}
protected S3Context createLiveS3Context(Module configurationModule, String AWSAccessKeyId,
String AWSSecretAccessKey) {
return buildS3ContextFactory(configurationModule, AWSAccessKeyId, AWSSecretAccessKey)
.buildContext();
}
// protected String createScratchContainerInEU() throws InterruptedException, ExecutionException,
// TimeoutException {
// String containerName = getScratchContainerName();
// deleteContainer(containerName);
// client.createContainer(containerName, PutBucketOptions.Builder
// .createIn(LocationConstraint.EU));
// return containerName;
// }
protected S3ContextBuilder buildS3ContextFactory(Module configurationModule,
String AWSAccessKeyId, String AWSSecretAccessKey) {
return (S3ContextBuilder) S3ContextBuilder.newBuilder(AWSAccessKeyId, AWSSecretAccessKey)
.withSaxDebug().relaxSSLHostname().withModules(configurationModule,
new Log4JLoggingModule());
}
}

View File

@ -0,0 +1,346 @@
/**
*
* 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.internal;
import java.util.Arrays;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import org.jclouds.aws.s3.S3BlobStore;
import org.jclouds.aws.s3.domain.AccessControlList;
import org.jclouds.aws.s3.domain.ArrayListBucketResponse;
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.Grant;
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.reference.S3Constants;
import org.jclouds.blobstore.ContainerNotFoundException;
import org.jclouds.blobstore.KeyNotFoundException;
import org.jclouds.blobstore.integration.internal.StubBlobStore;
import org.jclouds.http.HttpUtils;
import org.jclouds.http.options.GetOptions;
import org.joda.time.DateTime;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.inject.internal.Nullable;
/**
* Implementation of {@link S3BlobStore} which keeps all data in a local Map object.
*
* @author Adrian Cole
* @author James Murty
*/
public class StubS3BlobStore extends StubBlobStore<BucketMetadata, ObjectMetadata, S3Object>
implements S3BlobStore {
public static final String TEST_ACL_ID = "1a405254c932b52e5b5caaa88186bc431a1bacb9ece631f835daddaf0c47677c";
public static final String TEST_ACL_EMAIL = "james@misterm.org";
private static Map<String, BucketMetadata.LocationConstraint> bucketToLocation = new ConcurrentHashMap<String, BucketMetadata.LocationConstraint>();
/**
* An S3 item's "ACL" may be a {@link CannedAccessPolicy} or an {@link AccessControlList}.
*/
private static Map<String, Object> keyToAcl = new ConcurrentHashMap<String, Object>();
public static final String DEFAULT_OWNER_ID = "abc123";
public Future<Boolean> createContainer(String name, @Nullable PutBucketOptions nullableOptions) {
final PutBucketOptions options = (nullableOptions == null) ? new PutBucketOptions()
: nullableOptions;
if (options.getLocationConstraint() != null)
bucketToLocation.put(name, options.getLocationConstraint());
keyToAcl.put(name, options.getAcl());
return super.createContainer(name);
}
public Future<ListBucketResponse> listBlobs(final String name, final ListBucketOptions options) {
return new FutureBase<ListBucketResponse>() {
public ListBucketResponse get() throws InterruptedException, ExecutionException {
final Map<String, S3Object> realContents = getContainerToBlobs().get(name);
if (realContents == null)
throw new ContainerNotFoundException("name");
SortedSet<ObjectMetadata> contents = Sets.newTreeSet(Iterables.transform(realContents
.keySet(), new Function<String, ObjectMetadata>() {
public ObjectMetadata apply(String key) {
return realContents.get(key).getMetadata();
}
}));
String marker = getFirstQueryOrNull(S3Constants.MARKER, options);
if (marker != null) {
final String finalMarker = marker;
ObjectMetadata lastMarkerMetadata = Iterables.find(contents,
new Predicate<ObjectMetadata>() {
public boolean apply(ObjectMetadata metadata) {
return metadata.getKey().equals(finalMarker);
}
});
contents = contents.tailSet(lastMarkerMetadata);
// amazon spec means after the marker, not including it.
contents.remove(lastMarkerMetadata);
}
final String prefix = getFirstQueryOrNull(S3Constants.PREFIX, options);
if (prefix != null) {
contents = Sets.newTreeSet(Iterables.filter(contents,
new Predicate<ObjectMetadata>() {
public boolean apply(ObjectMetadata o) {
return (o != null && o.getKey().startsWith(prefix));
}
}));
}
final String delimiter = getFirstQueryOrNull(S3Constants.DELIMITER, options);
SortedSet<String> commonPrefixes = null;
if (delimiter != null) {
Iterable<String> iterable = Iterables.transform(contents, new CommonPrefixes(
prefix != null ? prefix : null, delimiter));
commonPrefixes = iterable != null ? Sets.newTreeSet(iterable)
: new TreeSet<String>();
commonPrefixes.remove(CommonPrefixes.NO_PREFIX);
contents = Sets.newTreeSet(Iterables.filter(contents, new DelimiterFilter(
prefix != null ? prefix : null, delimiter)));
}
final String maxKeysString = getFirstQueryOrNull(S3Constants.MAX_KEYS, options);
int maxResults = contents.size();
boolean truncated = false;
if (maxKeysString != null) {
int maxKeys = Integer.parseInt(maxKeysString);
SortedSet<ObjectMetadata> contentsSlice = firstSliceOfSize(contents, maxKeys);
maxResults = maxKeys;
if (!contentsSlice.contains(contents.last())) {
// Partial listing
truncated = true;
marker = contentsSlice.last().getKey();
} else {
marker = null;
}
contents = contentsSlice;
}
return new ArrayListBucketResponse(name, Lists.newArrayList(contents), prefix, marker,
maxResults, delimiter, truncated, commonPrefixes);
}
};
}
public Future<ObjectMetadata> copyBlob(final String sourceBucket, final String sourceObject,
final String destinationBucket, final String destinationObject,
@Nullable CopyObjectOptions nullableOptions) {
final CopyObjectOptions options = (nullableOptions == null) ? new CopyObjectOptions()
: nullableOptions;
return new FutureBase<ObjectMetadata>() {
public ObjectMetadata get() throws InterruptedException, ExecutionException {
Map<String, S3Object> source = getContainerToBlobs().get(sourceBucket);
Map<String, S3Object> dest = getContainerToBlobs().get(destinationBucket);
if (source.containsKey(sourceObject)) {
S3Object object = source.get(sourceObject);
if (options.getIfMatch() != null) {
if (!Arrays.equals(object.getMetadata().getETag(), HttpUtils
.fromHexString(options.getIfMatch().replaceAll("\"", ""))))
throwResponseException(412);
}
if (options.getIfNoneMatch() != null) {
if (Arrays.equals(object.getMetadata().getETag(), HttpUtils.fromHexString(options
.getIfNoneMatch().replaceAll("\"", ""))))
throwResponseException(412);
}
if (options.getIfModifiedSince() != null) {
DateTime modifiedSince = dateService
.rfc822DateParse(options.getIfModifiedSince());
if (modifiedSince.isAfter(object.getMetadata().getLastModified()))
throwResponseException(412);
}
if (options.getIfUnmodifiedSince() != null) {
DateTime unmodifiedSince = dateService.rfc822DateParse(options
.getIfUnmodifiedSince());
if (unmodifiedSince.isBefore(object.getMetadata().getLastModified()))
throwResponseException(412);
}
S3Object sourceS3 = source.get(sourceObject);
ObjectMetadata newMd = copy(sourceS3.getMetadata(), destinationObject);
if (options.getAcl() != null)
keyToAcl.put(destinationBucket + "/" + destinationObject, options.getAcl());
if (options.getMetadata() != null) {
newMd.setUserMetadata(options.getMetadata());
}
newMd.setLastModified(new DateTime());
dest.put(destinationObject, new S3Object(newMd, sourceS3.getData()));
return copy(newMd);
}
throw new KeyNotFoundException(sourceBucket, sourceObject);
}
};
}
public Future<byte[]> putBlob(final String bucketName, final S3Object object,
@Nullable PutObjectOptions nullableOptions) {
final PutObjectOptions options = (nullableOptions == null) ? new PutObjectOptions()
: nullableOptions;
if (options.getAcl() != null)
keyToAcl.put(bucketName + "/" + object.getKey(), options.getAcl());
return super.putBlob(bucketName, object);
}
protected AccessControlList getACLforS3Item(String bucketAndObjectKey) {
AccessControlList acl = null;
Object aclObj = keyToAcl.get(bucketAndObjectKey);
if (aclObj instanceof AccessControlList) {
acl = (AccessControlList) aclObj;
} else if (aclObj instanceof CannedAccessPolicy) {
acl = AccessControlList.fromCannedAccessPolicy((CannedAccessPolicy) aclObj,
DEFAULT_OWNER_ID);
} else if (aclObj == null) {
// Default to private access policy
acl = AccessControlList.fromCannedAccessPolicy(CannedAccessPolicy.PRIVATE,
DEFAULT_OWNER_ID);
}
return acl;
}
public Future<AccessControlList> getContainerACL(final String bucket) {
return new FutureBase<AccessControlList>() {
public AccessControlList get() throws InterruptedException, ExecutionException {
return getACLforS3Item(bucket);
}
};
}
public Future<AccessControlList> getBlobACL(final String bucket, final String objectKey) {
return new FutureBase<AccessControlList>() {
public AccessControlList get() throws InterruptedException, ExecutionException {
return getACLforS3Item(bucket + "/" + objectKey);
}
};
}
/**
* Replace any AmazonCustomerByEmail grantees with a somewhat-arbitrary canonical user grantee,
* to match S3 which substitutes each email address grantee with that user's corresponding ID. In
* short, although you can PUT email address grantees, these are actually subsequently returned
* by S3 as canonical user grantees.
*
* @param acl
* @return
*/
protected AccessControlList sanitizeUploadedACL(AccessControlList acl) {
// Replace any email address grantees with canonical user grantees, using
// the acl's owner ID as the surrogate replacement.
for (Grant grant : acl.getGrants()) {
if (grant.getGrantee() instanceof EmailAddressGrantee) {
EmailAddressGrantee emailGrantee = (EmailAddressGrantee) grant.getGrantee();
String id = emailGrantee.getEmailAddress().equals(TEST_ACL_EMAIL) ? TEST_ACL_ID : acl
.getOwner().getId();
grant.setGrantee(new CanonicalUserGrantee(id, acl.getOwner().getDisplayName()));
}
}
return acl;
}
public Future<Boolean> putContainerACL(final String bucket, final AccessControlList acl) {
return new FutureBase<Boolean>() {
public Boolean get() throws InterruptedException, ExecutionException {
keyToAcl.put(bucket, sanitizeUploadedACL(acl));
return true;
}
};
}
public Future<Boolean> putBlobACL(final String bucket, final String objectKey,
final AccessControlList acl) {
return new FutureBase<Boolean>() {
public Boolean get() throws InterruptedException, ExecutionException {
keyToAcl.put(bucket + "/" + objectKey, sanitizeUploadedACL(acl));
return true;
}
};
}
public Future<ObjectMetadata> copyBlob(String sourceBucket, String sourceObject,
String destinationBucket, String destinationObject) {
return copyBlob(sourceBucket, sourceObject, destinationBucket, destinationObject,
CopyObjectOptions.NONE);
}
public Future<Boolean> createContainer(String bucketName) {
return createContainer(bucketName, PutBucketOptions.NONE);
}
@Override
public Future<S3Object> getBlob(String bucketName, String key) {
return getBlob(bucketName, key, GetOptions.NONE);
}
public Future<ListBucketResponse> listBlobs(String bucketName) {
return listBlobs(bucketName, ListBucketOptions.NONE);
}
public Future<byte[]> putBlob(String bucketName, S3Object object) {
return putBlob(bucketName, object, PutObjectOptions.NONE);
}
@Override
protected S3Object createBlob(String name) {
return new S3Object(name);
}
@Override
protected S3Object createBlob(ObjectMetadata metadata) {
return new S3Object(metadata);
}
@Override
protected BucketMetadata createContainerMetadata(String name) {
return new BucketMetadata(name);
}
/**
* note this must be final and static so that tests coming from multiple threads will pass.
*/
private static final Map<String, Map<String, S3Object>> containerToBlobs = new ConcurrentHashMap<String, Map<String, S3Object>>();
@Override
public Map<String, Map<String, S3Object>> getContainerToBlobs() {
return containerToBlobs;
}
}

View File

@ -1,613 +0,0 @@
/**
*
* 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.internal;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.easymock.classextension.EasyMock.createNiceMock;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import javax.ws.rs.core.HttpHeaders;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.jclouds.aws.s3.S3Connection;
import org.jclouds.aws.s3.domain.AccessControlList;
import org.jclouds.aws.s3.domain.CannedAccessPolicy;
import org.jclouds.aws.s3.domain.S3Bucket;
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.Grant;
import org.jclouds.aws.s3.domain.S3Bucket.Metadata;
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.reference.S3Constants;
import org.jclouds.http.HttpCommand;
import org.jclouds.http.HttpResponse;
import org.jclouds.http.HttpResponseException;
import org.jclouds.http.HttpUtils;
import org.jclouds.http.options.GetOptions;
import org.jclouds.http.options.HttpRequestOptions;
import org.jclouds.util.DateService;
import org.joda.time.DateTime;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.thoughtworks.xstream.XStream;
/**
* Implementation of {@link S3Connection} which keeps all data in a local Map object.
*
* @author Adrian Cole
* @author James Murty
*/
public class StubS3Connection implements S3Connection {
public static final String TEST_ACL_ID = "1a405254c932b52e5b5caaa88186bc431a1bacb9ece631f835daddaf0c47677c";
public static final String TEST_ACL_EMAIL = "james@misterm.org";
private static Map<String, Map<String, S3Object>> bucketToContents = new ConcurrentHashMap<String, Map<String, S3Object>>();
private static Map<String, Metadata.LocationConstraint> bucketToLocation = new ConcurrentHashMap<String, Metadata.LocationConstraint>();
/**
* An S3 item's "ACL" may be a {@link CannedAccessPolicy} or an {@link AccessControlList}.
*/
private static Map<String, Object> keyToAcl = new ConcurrentHashMap<String, Object>();
public static final String DEFAULT_OWNER_ID = "abc123";
/**
* @throws java.io.IOException
*/
public static byte[] toByteArray(Object data) throws IOException {
checkNotNull(data, "data must be set before calling generateETag()");
byte[] bytes = null;
if (data == null || data instanceof byte[]) {
bytes = (byte[]) data;
} else if (data instanceof String) {
bytes = ((String) data).getBytes();
} else if (data instanceof File || data instanceof InputStream) {
InputStream io = (data instanceof InputStream) ? (InputStream) data : new FileInputStream(
(File) data);
bytes = IOUtils.toByteArray(io);
IOUtils.closeQuietly(io);
} else {
throw new UnsupportedOperationException("Content not supported " + data.getClass());
}
return bytes;
}
public Future<S3Object> getObject(final String s3Bucket, final String key) {
return getObject(s3Bucket, key, new GetOptions());
}
public S3Object.Metadata copy(S3Object.Metadata in) {
return (S3Object.Metadata) xstream.fromXML(xstream.toXML(in));
}
public S3Object.Metadata copy(S3Object.Metadata in, String newKey) {
return (S3Object.Metadata) xstream.fromXML(xstream.toXML(in).replaceAll(in.getKey(), newKey));
}
public S3Object.Metadata headObject(final String s3Bucket, final String key) {
if (!bucketToContents.containsKey(s3Bucket))
return S3Object.Metadata.NOT_FOUND;
Map<String, S3Object> realContents = bucketToContents.get(s3Bucket);
if (!realContents.containsKey(key))
return S3Object.Metadata.NOT_FOUND;
return realContents.get(key).getMetadata();
}
public Future<Boolean> deleteObject(final String s3Bucket, final String key) {
return new FutureBase<Boolean>() {
public Boolean get() throws InterruptedException, ExecutionException {
if (bucketToContents.containsKey(s3Bucket)) {
bucketToContents.get(s3Bucket).remove(key);
}
return true;
}
};
}
public Future<byte[]> putObject(final String s3Bucket, final S3Object object) {
return putObject(s3Bucket, object, new PutObjectOptions());
}
public Future<Boolean> putBucketIfNotExists(final String s3Bucket) {
return new FutureBase<Boolean>() {
public Boolean get() throws InterruptedException, ExecutionException {
if (!bucketToContents.containsKey(s3Bucket)) {
bucketToContents.put(s3Bucket, new ConcurrentHashMap<String, S3Object>());
}
return bucketToContents.containsKey(s3Bucket);
}
};
}
public boolean deleteBucketIfEmpty(final String s3Bucket) {
if (bucketToContents.containsKey(s3Bucket)) {
if (bucketToContents.get(s3Bucket).size() == 0)
bucketToContents.remove(s3Bucket);
else
return false;
}
return true;
}
XStream xstream = new XStream();
public Future<S3Object.Metadata> copyObject(final String sourceBucket,
final String sourceObject, final String destinationBucket,
final String destinationObject) {
return copyObject(sourceBucket, sourceObject, destinationBucket, destinationObject,
new CopyObjectOptions());
}
public boolean bucketExists(final String s3Bucket) {
return bucketToContents.containsKey(s3Bucket);
}
public Future<S3Bucket> listBucket(final String s3Bucket) {
return listBucket(s3Bucket, new ListBucketOptions());
}
private abstract class FutureBase<V> implements Future<V> {
public boolean cancel(boolean b) {
return false;
}
public boolean isCancelled() {
return false;
}
public boolean isDone() {
return true;
}
public V get(long l, TimeUnit timeUnit) throws InterruptedException, ExecutionException,
TimeoutException {
return get();
}
}
public List<Metadata> listOwnedBuckets() {
return Lists.newArrayList(Iterables.transform(bucketToContents.keySet(),
new Function<String, Metadata>() {
public Metadata apply(String name) {
return new S3Bucket.Metadata(name);
}
}));
}
public Future<Boolean> putBucketIfNotExists(String name, PutBucketOptions options) {
if (options.getLocationConstraint() != null)
bucketToLocation.put(name, options.getLocationConstraint());
keyToAcl.put(name, options.getAcl());
return putBucketIfNotExists(name);
}
class DelimiterFilter implements Predicate<S3Object.Metadata> {
private final String prefix;
private final String delimiter;
DelimiterFilter(String prefix, String delimiter) {
this.prefix = prefix;
this.delimiter = delimiter;
}
public boolean apply(S3Object.Metadata metadata) {
if (prefix == null)
return metadata.getKey().indexOf(delimiter) == -1;
if (metadata.getKey().startsWith(prefix))
return metadata.getKey().replaceFirst(prefix, "").indexOf(delimiter) == -1;
return false;
}
}
class CommonPrefixes implements Function<S3Object.Metadata, String> {
private final String prefix;
private final String delimiter;
static final String NO_PREFIX = "NO_PREFIX";
CommonPrefixes(String prefix, String delimiter) {
this.prefix = prefix;
this.delimiter = delimiter;
}
public String apply(S3Object.Metadata metadata) {
String working = metadata.getKey();
if (prefix != null) {
if (working.startsWith(prefix)) {
working = working.replaceFirst(prefix, "");
}
}
if (working.contains(delimiter)) {
return working.substring(0, working.indexOf(delimiter));
}
return NO_PREFIX;
}
}
public String getFirstQueryOrNull(String string, HttpRequestOptions options) {
Collection<String> values = options.buildQueryParameters().get(string);
return (values != null && values.size() >= 1) ? values.iterator().next() : null;
}
public Future<S3Bucket> listBucket(final String name, final ListBucketOptions options) {
return new FutureBase<S3Bucket>() {
public S3Bucket get() throws InterruptedException, ExecutionException {
final Map<String, S3Object> realContents = bucketToContents.get(name);
if (realContents == null)
return S3Bucket.NOT_FOUND;
SortedSet<S3Object.Metadata> contents = Sets.newTreeSet(Iterables.transform(
realContents.keySet(), new Function<String, S3Object.Metadata>() {
public S3Object.Metadata apply(String key) {
return realContents.get(key).getMetadata();
}
}));
S3Bucket returnVal = new S3Bucket(name);
final String marker = getFirstQueryOrNull(S3Constants.MARKER, options);
if (marker != null) {
S3Object.Metadata lastMarkerMetadata = Iterables.find(contents,
new Predicate<S3Object.Metadata>() {
public boolean apply(S3Object.Metadata metadata) {
return metadata.getKey().equals(marker);
}
});
contents = contents.tailSet(lastMarkerMetadata);
// amazon spec means after the marker, not including it.
contents.remove(lastMarkerMetadata);
returnVal.setMarker(marker);
}
final String prefix = getFirstQueryOrNull(S3Constants.PREFIX, options);
if (prefix != null) {
contents = Sets.newTreeSet(Iterables.filter(contents,
new Predicate<S3Object.Metadata>() {
public boolean apply(S3Object.Metadata o) {
return (o != null && o.getKey().startsWith(prefix));
}
}));
returnVal.setPrefix(prefix);
}
final String delimiter = getFirstQueryOrNull(S3Constants.DELIMITER, options);
if (delimiter != null) {
Iterable<String> iterable = Iterables.transform(contents, new CommonPrefixes(
prefix != null ? prefix : null, delimiter));
SortedSet<String> commonPrefixes = iterable != null ? Sets.newTreeSet(iterable)
: new TreeSet<String>();
commonPrefixes.remove(CommonPrefixes.NO_PREFIX);
contents = Sets.newTreeSet(Iterables.filter(contents, new DelimiterFilter(
prefix != null ? prefix : null, delimiter)));
returnVal.setCommonPrefixes(commonPrefixes);
returnVal.setDelimiter(delimiter);
}
final String maxKeysString = getFirstQueryOrNull(S3Constants.MAX_KEYS, options);
if (maxKeysString != null) {
int maxKeys = Integer.parseInt(maxKeysString);
SortedSet<S3Object.Metadata> contentsSlice = firstSliceOfSize(contents, maxKeys);
returnVal.setMaxKeys(maxKeys);
if (!contentsSlice.contains(contents.last())) {
// Partial listing
returnVal.setTruncated(true);
returnVal.setMarker(contentsSlice.last().getKey());
} else {
returnVal.setTruncated(false);
returnVal.setMarker(null);
}
contents = contentsSlice;
}
returnVal.setContents(contents);
return returnVal;
}
};
}
public static <T extends Comparable<?>> SortedSet<T> firstSliceOfSize(Iterable<T> elements,
int size) {
List<List<T>> slices = Lists.partition(Lists.newArrayList(elements), size);
return Sets.newTreeSet(slices.get(0));
}
public Future<org.jclouds.aws.s3.domain.S3Object.Metadata> copyObject(final String sourceBucket,
final String sourceObject, final String destinationBucket,
final String destinationObject, final CopyObjectOptions options) {
return new FutureBase<S3Object.Metadata>() {
public S3Object.Metadata get() throws InterruptedException, ExecutionException {
Map<String, S3Object> source = bucketToContents.get(sourceBucket);
Map<String, S3Object> dest = bucketToContents.get(destinationBucket);
if (source.containsKey(sourceObject)) {
S3Object object = source.get(sourceObject);
if (options.getIfMatch() != null) {
if (!Arrays.equals(object.getMetadata().getETag(), HttpUtils
.fromHexString(options.getIfMatch().replaceAll("\"", ""))))
throwResponseException(412);
}
if (options.getIfNoneMatch() != null) {
if (Arrays.equals(object.getMetadata().getETag(), HttpUtils.fromHexString(options
.getIfNoneMatch().replaceAll("\"", ""))))
throwResponseException(412);
}
if (options.getIfModifiedSince() != null) {
DateTime modifiedSince = dateService
.rfc822DateParse(options.getIfModifiedSince());
if (modifiedSince.isAfter(object.getMetadata().getLastModified()))
throw new ExecutionException(new RuntimeException("after"));
}
if (options.getIfUnmodifiedSince() != null) {
DateTime unmodifiedSince = dateService.rfc822DateParse(options
.getIfUnmodifiedSince());
if (unmodifiedSince.isAfter(object.getMetadata().getLastModified()))
throw new ExecutionException(new RuntimeException("after"));
}
S3Object sourceS3 = source.get(sourceObject);
S3Object.Metadata newMd = copy(sourceS3.getMetadata(), destinationObject);
if (options.getAcl() != null)
keyToAcl.put(destinationBucket + "/" + destinationObject, options.getAcl());
if (options.getMetadata() != null) {
newMd.setUserMetadata(options.getMetadata());
}
newMd.setLastModified(new DateTime());
dest.put(destinationObject, new S3Object(newMd, sourceS3.getData()));
return copy(newMd);
}
return S3Object.Metadata.NOT_FOUND;
}
};
}
private void throwResponseException(int code) throws ExecutionException {
HttpResponse response = null;
try {
response = new HttpResponse(new URL("file:///unused")); // TODO: Get real object URL?
} catch (MalformedURLException e) {
// This shouldn't ever happen.
e.printStackTrace();
assert false;
}
response.setStatusCode(code);
throw new ExecutionException(new HttpResponseException(createNiceMock(HttpCommand.class),
response));
}
public Future<byte[]> putObject(final String bucketName, final S3Object object,
final PutObjectOptions options) {
if (!bucketToContents.containsKey(bucketName)) {
new RuntimeException("bucketName not found: " + bucketName);
}
try {
S3Object.Metadata newMd = copy(object.getMetadata());
newMd.setLastModified(new DateTime());
byte[] data = toByteArray(object.getData());
final byte[] eTag = HttpUtils.eTag(data);
newMd.setETag(eTag);
newMd.setContentType(object.getMetadata().getContentType());
if (options.getAcl() != null)
keyToAcl.put(bucketName + "/" + object.getKey(), options.getAcl());
bucketToContents.get(bucketName).put(object.getKey(), new S3Object(newMd, data));
// Set HTTP headers to match metadata
newMd.getAllHeaders().put(HttpHeaders.LAST_MODIFIED,
dateService.rfc822DateFormat(newMd.getLastModified()));
newMd.getAllHeaders().put(HttpHeaders.ETAG, HttpUtils.toHexString(eTag));
newMd.getAllHeaders().put(HttpHeaders.CONTENT_TYPE, newMd.getContentType());
newMd.getAllHeaders().put(HttpHeaders.CONTENT_LENGTH, newMd.getSize() + "");
for (Entry<String, String> userMD : newMd.getUserMetadata().entries()) {
newMd.getAllHeaders().put(userMD.getKey(), userMD.getValue());
}
return new FutureBase<byte[]>() {
public byte[] get() throws InterruptedException, ExecutionException {
return eTag;
}
};
} catch (IOException e) {
throw new RuntimeException(e);
}
}
DateService dateService = new DateService();
public Future<S3Object> getObject(final String bucketName, final String key,
final GetOptions options) {
return new FutureBase<S3Object>() {
public S3Object get() throws InterruptedException, ExecutionException {
if (!bucketToContents.containsKey(bucketName))
return S3Object.NOT_FOUND;
Map<String, S3Object> realContents = bucketToContents.get(bucketName);
if (!realContents.containsKey(key))
return S3Object.NOT_FOUND;
S3Object object = realContents.get(key);
if (options.getIfMatch() != null) {
if (!Arrays.equals(object.getMetadata().getETag(), HttpUtils.fromHexString(options
.getIfMatch().replaceAll("\"", ""))))
throwResponseException(412);
}
if (options.getIfNoneMatch() != null) {
if (Arrays.equals(object.getMetadata().getETag(), HttpUtils.fromHexString(options
.getIfNoneMatch().replaceAll("\"", ""))))
throwResponseException(304);
}
if (options.getIfModifiedSince() != null) {
DateTime modifiedSince = dateService.rfc822DateParse(options.getIfModifiedSince());
if (object.getMetadata().getLastModified().isBefore(modifiedSince))
throw new ExecutionException(new RuntimeException(String.format(
"%1$s is before %2$s", object.getMetadata().getLastModified(),
modifiedSince)));
}
if (options.getIfUnmodifiedSince() != null) {
DateTime unmodifiedSince = dateService.rfc822DateParse(options
.getIfUnmodifiedSince());
if (object.getMetadata().getLastModified().isAfter(unmodifiedSince))
throw new ExecutionException(new RuntimeException(String.format(
"%1$s is after %2$s", object.getMetadata().getLastModified(),
unmodifiedSince)));
}
S3Object returnVal = new S3Object(copy(object.getMetadata()), object.getData());
if (options.getRange() != null) {
byte[] data = (byte[]) returnVal.getData();
ByteArrayOutputStream out = new ByteArrayOutputStream();
for (String s : options.getRange().replaceAll("bytes=", "").split(",")) {
if (s.startsWith("-")) {
int length = Integer.parseInt(s.replaceAll("\\-", ""));
out.write(data, data.length - length, length);
} else if (s.endsWith("-")) {
int offset = Integer.parseInt(s.replaceAll("\\-", ""));
out.write(data, offset, data.length - offset);
} else if (s.contains("-")) {
String[] firstLast = s.split("\\-");
int offset = Integer.parseInt(firstLast[0]);
int last = Integer.parseInt(firstLast[1]);
int length = (last < data.length) ? last + 1 : data.length - offset;
out.write(data, offset, length);
} else {
throw new IllegalArgumentException("first and last were null!");
}
}
returnVal.setData(out.toByteArray());
returnVal.setContentLength(out.size());
returnVal.getMetadata().setSize(data.length);
}
returnVal.setData(new ByteArrayInputStream((byte[]) returnVal.getData()));
return returnVal;
}
};
}
protected AccessControlList getACLforS3Item(String bucketAndObjectKey) {
AccessControlList acl = null;
Object aclObj = keyToAcl.get(bucketAndObjectKey);
if (aclObj instanceof AccessControlList) {
acl = (AccessControlList) aclObj;
} else if (aclObj instanceof CannedAccessPolicy) {
acl = AccessControlList.fromCannedAccessPolicy((CannedAccessPolicy) aclObj,
DEFAULT_OWNER_ID);
} else if (aclObj == null) {
// Default to private access policy
acl = AccessControlList.fromCannedAccessPolicy(CannedAccessPolicy.PRIVATE,
DEFAULT_OWNER_ID);
}
return acl;
}
public Future<AccessControlList> getBucketACL(final String bucket) {
return new FutureBase<AccessControlList>() {
public AccessControlList get() throws InterruptedException, ExecutionException {
return getACLforS3Item(bucket);
}
};
}
public Future<AccessControlList> getObjectACL(final String bucket, final String objectKey) {
return new FutureBase<AccessControlList>() {
public AccessControlList get() throws InterruptedException, ExecutionException {
return getACLforS3Item(bucket + "/" + objectKey);
}
};
}
/**
* Replace any AmazonCustomerByEmail grantees with a somewhat-arbitrary canonical user grantee,
* to match S3 which substitutes each email address grantee with that user's corresponding ID. In
* short, although you can PUT email address grantees, these are actually subsequently returned
* by S3 as canonical user grantees.
*
* @param acl
* @return
*/
protected AccessControlList sanitizeUploadedACL(AccessControlList acl) {
// Replace any email address grantees with canonical user grantees, using
// the acl's owner ID as the surrogate replacement.
for (Grant grant : acl.getGrants()) {
if (grant.getGrantee() instanceof EmailAddressGrantee) {
EmailAddressGrantee emailGrantee = (EmailAddressGrantee) grant.getGrantee();
String id = emailGrantee.getEmailAddress().equals(TEST_ACL_EMAIL) ? TEST_ACL_ID : acl
.getOwner().getId();
grant.setGrantee(new CanonicalUserGrantee(id, acl.getOwner().getDisplayName()));
}
}
return acl;
}
public Future<Boolean> putBucketACL(final String bucket, final AccessControlList acl) {
return new FutureBase<Boolean>() {
public Boolean get() throws InterruptedException, ExecutionException {
keyToAcl.put(bucket, sanitizeUploadedACL(acl));
return true;
}
};
}
public Future<Boolean> putObjectACL(final String bucket, final String objectKey,
final AccessControlList acl) {
return new FutureBase<Boolean>() {
public Boolean get() throws InterruptedException, ExecutionException {
keyToAcl.put(bucket + "/" + objectKey, sanitizeUploadedACL(acl));
return true;
}
};
}
}

View File

@ -36,7 +36,6 @@ import static org.testng.Assert.assertTrue;
import java.io.UnsupportedEncodingException;
import org.jclouds.aws.s3.domain.CannedAccessPolicy;
import org.jclouds.aws.s3.options.CopyObjectOptions;
import org.jclouds.aws.s3.reference.S3Headers;
import org.jclouds.http.HttpUtils;
import org.jclouds.util.DateService;
@ -76,6 +75,8 @@ public class CopyObjectOptionsTest {
@Test
void testGoodMetaStatic() {
CopyObjectOptions options = overrideMetadataWith(goodMeta);
options.setMetadataPrefix("x-amz-meta-");
assertGoodMeta(options);
}
@ -84,16 +85,6 @@ public class CopyObjectOptionsTest {
overrideMetadataWith(null);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadMeta() {
overrideMetadataWith(badMeta);
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testBadMetaStatic() {
overrideMetadataWith(badMeta);
}
private void assertGoodMeta(CopyObjectOptions options) {
assert options != null;
assert options.getMetadata() != null;
@ -108,6 +99,7 @@ public class CopyObjectOptionsTest {
@Test
void testGoodMeta() {
CopyObjectOptions options = new CopyObjectOptions();
options.setMetadataPrefix("x-amz-meta-");
options.overrideMetadataWith(goodMeta);
assertGoodMeta(options);
}
@ -283,14 +275,18 @@ public class CopyObjectOptionsTest {
@Test
void testBuildRequestHeadersWhenMetadataNull() throws UnsupportedEncodingException {
assert new CopyObjectOptions().buildRequestHeaders() != null;
CopyObjectOptions options = new CopyObjectOptions();
options.setMetadataPrefix("x-amz-meta-");
assert options.buildRequestHeaders() != null;
}
@Test
void testBuildRequestHeaders() throws UnsupportedEncodingException {
CopyObjectOptions options = ifSourceModifiedSince(now).ifSourceETagDoesntMatch(testBytes)
.overrideMetadataWith(goodMeta);
options.setMetadataPrefix("x-amz-meta-");
Multimap<String, String> headers = ifSourceModifiedSince(now).ifSourceETagDoesntMatch(
testBytes).overrideMetadataWith(goodMeta).buildRequestHeaders();
Multimap<String, String> headers = options.buildRequestHeaders();
assertEquals(headers.get("x-amz-copy-source-if-modified-since").iterator().next(),
new DateService().rfc822DateFormat(now));
assertEquals(headers.get("x-amz-copy-source-if-none-match").iterator().next(), "\""
@ -314,9 +310,11 @@ public class CopyObjectOptionsTest {
@Test
void testBuildRequestHeadersACL() throws UnsupportedEncodingException {
CopyObjectOptions options = overrideAcl(CannedAccessPolicy.AUTHENTICATED_READ);
options.setMetadataPrefix("x-amz-meta-");
Multimap<String, String> headers = options.buildRequestHeaders();
Multimap<String, String> headers = overrideAcl(CannedAccessPolicy.AUTHENTICATED_READ)
.buildRequestHeaders();
assertEquals(headers.get(S3Headers.CANNED_ACL).iterator().next(),
CannedAccessPolicy.AUTHENTICATED_READ.toString());
}

View File

@ -29,7 +29,7 @@ import static org.jclouds.aws.s3.options.PutBucketOptions.Builder.createIn;
import static org.jclouds.aws.s3.options.PutBucketOptions.Builder.withBucketAcl;
import org.jclouds.aws.s3.domain.CannedAccessPolicy;
import org.jclouds.aws.s3.domain.S3Bucket.Metadata.LocationConstraint;
import org.jclouds.aws.s3.domain.BucketMetadata.LocationConstraint;
import org.jclouds.aws.s3.options.PutBucketOptions;
import org.jclouds.aws.s3.reference.S3Headers;
import static org.testng.Assert.assertEquals;

View File

@ -21,7 +21,7 @@
* under the License.
* ====================================================================
*/
package org.jclouds.aws.s3.commands;
package org.jclouds.aws.s3.xml;
import static org.testng.Assert.assertEquals;
@ -35,9 +35,10 @@ import java.util.concurrent.ExecutorCompletionService;
import org.apache.commons.io.IOUtils;
import org.jclouds.PerformanceTest;
import org.jclouds.aws.s3.domain.BucketMetadata;
import org.jclouds.aws.s3.domain.CanonicalUser;
import org.jclouds.aws.s3.domain.S3Bucket;
import org.jclouds.aws.s3.domain.S3Object;
import org.jclouds.aws.s3.domain.ListBucketResponse;
import org.jclouds.aws.s3.domain.ObjectMetadata;
import org.jclouds.aws.s3.xml.S3ParserFactory;
import org.jclouds.aws.s3.xml.config.S3ParserModule;
import org.jclouds.http.HttpException;
@ -62,7 +63,7 @@ import com.google.inject.Injector;
public class S3ParserTest extends PerformanceTest {
Injector injector = null;
public static final String listAllMyBucketsResultOn200 = "<ListAllMyBucketsResult xmlns=\"http://s3.amazonaws.com/doc/callables/\"><Owner ><ID>e1a5f66a480ca99a4fdfe8e318c3020446c9989d7004e7778029fbcc5d990fa0</ID></Owner><Buckets><Bucket><Name>adrianjbosstest</Name><CreationDate>2009-03-12T02:00:07.000Z</CreationDate></Bucket><Bucket><Name>adrianjbosstest2</Name><CreationDate>2009-03-12T02:00:09.000Z</CreationDate></Bucket></Buckets></ListAllMyBucketsResult>";
public static final String listAllMyBucketsResultOn200 = "<ListAllMyBucketsResult xmlns=\"http://s3.amazonaws.com/doc/callables/\"><Owner><ID>e1a5f66a480ca99a4fdfe8e318c3020446c9989d7004e7778029fbcc5d990fa0</ID></Owner><Buckets><Bucket><Name>adrianjbosstest</Name><CreationDate>2009-03-12T02:00:07.000Z</CreationDate></Bucket><Bucket><Name>adrianjbosstest2</Name><CreationDate>2009-03-12T02:00:09.000Z</CreationDate></Bucket></Buckets></ListAllMyBucketsResult>";
S3ParserFactory parserFactory = null;
@ -85,7 +86,7 @@ public class S3ParserTest extends PerformanceTest {
runParseListAllMyBuckets();
}
private List<S3Bucket.Metadata> runParseListAllMyBuckets() throws HttpException {
private List<BucketMetadata> runParseListAllMyBuckets() throws HttpException {
return parserFactory.createListBucketsParser().parse(
IOUtils.toInputStream(listAllMyBucketsResultOn200));
}
@ -93,11 +94,11 @@ public class S3ParserTest extends PerformanceTest {
@Test
void testParseListAllMyBucketsParallelResponseTime() throws InterruptedException,
ExecutionException {
CompletionService<List<S3Bucket.Metadata>> completer = new ExecutorCompletionService<List<S3Bucket.Metadata>>(
CompletionService<List<BucketMetadata>> completer = new ExecutorCompletionService<List<BucketMetadata>>(
exec);
for (int i = 0; i < LOOP_COUNT; i++)
completer.submit(new Callable<List<S3Bucket.Metadata>>() {
public List<S3Bucket.Metadata> call() throws IOException, SAXException, HttpException {
completer.submit(new Callable<List<BucketMetadata>>() {
public List<BucketMetadata> call() throws IOException, SAXException, HttpException {
return runParseListAllMyBuckets();
}
});
@ -107,32 +108,32 @@ public class S3ParserTest extends PerformanceTest {
@Test
public void testCanParseListAllMyBuckets() throws HttpException {
List<S3Bucket.Metadata> s3Buckets = runParseListAllMyBuckets();
S3Bucket.Metadata bucket1 = s3Buckets.get(0);
assert bucket1.getName().equals("adrianjbosstest");
List<BucketMetadata> s3Buckets = runParseListAllMyBuckets();
BucketMetadata container1 = s3Buckets.get(0);
assert container1.getName().equals("adrianjbosstest");
DateTime expectedDate1 = new DateTime("2009-03-12T02:00:07.000Z");
DateTime date1 = bucket1.getCreationDate();
DateTime date1 = container1.getCreationDate();
assert date1.equals(expectedDate1);
S3Bucket.Metadata bucket2 = s3Buckets.get(1);
assert bucket2.getName().equals("adrianjbosstest2");
BucketMetadata container2 = s3Buckets.get(1);
assert container2.getName().equals("adrianjbosstest2");
DateTime expectedDate2 = new DateTime("2009-03-12T02:00:09.000Z");
DateTime date2 = bucket2.getCreationDate();
DateTime date2 = container2.getCreationDate();
assert date2.equals(expectedDate2);
assert s3Buckets.size() == 2;
CanonicalUser owner = new CanonicalUser(
"e1a5f66a480ca99a4fdfe8e318c3020446c9989d7004e7778029fbcc5d990fa0");
assert bucket1.getOwner().equals(owner);
assert bucket2.getOwner().equals(owner);
assert container1.getOwner().equals(owner);
assert container2.getOwner().equals(owner);
}
public static final String listBucketResult = "<ListBucketHandler xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\"><Name>adrianjbosstest</Name><Prefix></Prefix><Marker></Marker><MaxKeys>1000</MaxKeys><IsTruncated>false</IsTruncated><Contents><Key>3366</Key><LastModified>2009-03-12T02:00:13.000Z</LastModified><ETag>&quot;9d7bb64e8e18ee34eec06dd2cf37b766&quot;</ETag><Size>136</Size><Owner><ID>e1a5f66a480ca99a4fdfe8e318c3020446c9989d7004e7778029fbcc5d990fa0</ID><DisplayName>ferncam</DisplayName></Owner><StorageClass>STANDARD</StorageClass></Contents></ListBucketHandler>";
public static final String listContainerResult = "<ListContainerHandler xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\"><Name>adrianjbosstest</Name><Prefix></Prefix><Marker></Marker><MaxKeys>1000</MaxKeys><IsTruncated>false</IsTruncated><Contents><Key>3366</Key><LastModified>2009-03-12T02:00:13.000Z</LastModified><ETag>&quot;9d7bb64e8e18ee34eec06dd2cf37b766&quot;</ETag><Size>136</Size><Owner><ID>e1a5f66a480ca99a4fdfe8e318c3020446c9989d7004e7778029fbcc5d990fa0</ID><DisplayName>ferncam</DisplayName></Owner><StorageClass>STANDARD</StorageClass></Contents></ListContainerHandler>";
public void testCanParseListBucketResult() throws HttpException, UnsupportedEncodingException {
S3Bucket bucket = runParseListBucketResult();
assert !bucket.isTruncated();
assert bucket.getName().equals("adrianjbosstest");
assert bucket.getContents().size() == 1;
S3Object.Metadata object = bucket.getContents().iterator().next();
public void testCanParseListContainerResult() throws HttpException, UnsupportedEncodingException {
ListBucketResponse container = runParseListContainerResult();
assert !container.isTruncated();
assert container.getBucketName().equals("adrianjbosstest");
assert container.size() == 1;
ObjectMetadata object = container.iterator().next();
assert object.getKey().equals("3366");
DateTime expected = new DateTime("2009-03-12T02:00:13.000Z");
assert object.getLastModified().equals(expected) : String.format(
@ -146,39 +147,40 @@ public class S3ParserTest extends PerformanceTest {
assert object.getStorageClass().equals("STANDARD");
}
private S3Bucket runParseListBucketResult() throws HttpException {
ParseSax<S3Bucket> parser = parserFactory.createListBucketParser();
return parser.parse(IOUtils.toInputStream(listBucketResult));
private ListBucketResponse runParseListContainerResult() throws HttpException {
ParseSax<ListBucketResponse> parser = parserFactory.createListBucketParser();
return parser.parse(IOUtils.toInputStream(listContainerResult));
}
public static final String successfulCopyObject200 = "<CopyObjectResult xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\"><LastModified>2009-03-19T13:23:27.000Z</LastModified><ETag>\"92836a3ea45a6984d1b4d23a747d46bb\"</ETag></CopyObjectResult>";
private S3Object.Metadata runParseCopyObjectResult() throws HttpException {
ParseSax<S3Object.Metadata> parser = parserFactory.createCopyObjectParser();
private ObjectMetadata runParseCopyObjectResult() throws HttpException {
ParseSax<ObjectMetadata> parser = parserFactory.createCopyObjectParser();
return parser.parse(IOUtils.toInputStream(successfulCopyObject200));
}
public void testCanParseCopyObjectResult() throws HttpException, UnsupportedEncodingException {
S3Object.Metadata metadata = runParseCopyObjectResult();
ObjectMetadata metadata = runParseCopyObjectResult();
DateTime expected = new DateTime("2009-03-19T13:23:27.000Z");
assertEquals(metadata.getLastModified(), expected);
assertEquals(HttpUtils.toHexString(metadata.getETag()), "92836a3ea45a6984d1b4d23a747d46bb");
}
@Test
void testParseListBucketResultSerialResponseTime() throws HttpException {
void testParseListContainerResultSerialResponseTime() throws HttpException {
for (int i = 0; i < LOOP_COUNT; i++)
runParseListBucketResult();
runParseListContainerResult();
}
@Test
void testParseListBucketResultParallelResponseTime() throws InterruptedException,
void testParseListContainerResultParallelResponseTime() throws InterruptedException,
ExecutionException {
CompletionService<S3Bucket> completer = new ExecutorCompletionService<S3Bucket>(exec);
CompletionService<ListBucketResponse> completer = new ExecutorCompletionService<ListBucketResponse>(
exec);
for (int i = 0; i < LOOP_COUNT; i++)
completer.submit(new Callable<S3Bucket>() {
public S3Bucket call() throws IOException, SAXException, HttpException {
return runParseListBucketResult();
completer.submit(new Callable<ListBucketResponse>() {
public ListBucketResponse call() throws IOException, SAXException, HttpException {
return runParseListContainerResult();
}
});
for (int i = 0; i < LOOP_COUNT; i++)

View File

@ -31,9 +31,10 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.jclouds.aws.s3.S3Connection;
import org.jclouds.aws.s3.S3BlobStore;
import org.jclouds.aws.s3.S3Context;
import org.jclouds.aws.s3.S3ContextBuilder;
import org.jclouds.aws.s3.domain.ListBucketResponse;
import org.jclouds.aws.s3.options.CopyObjectOptions;
import org.jclouds.aws.s3.options.ListBucketOptions;
import org.jclouds.aws.s3.options.PutObjectOptions;
@ -62,7 +63,7 @@ public class JCloudsS3Service extends S3Service {
private static final long serialVersionUID = 1L;
private final S3Context context;
private final S3Connection connection;
private final S3BlobStore connection;
private final long requestTimeoutMilliseconds = 10000;
@ -81,7 +82,7 @@ public class JCloudsS3Service extends S3Service {
super(awsCredentials);
context = S3ContextBuilder.newBuilder(awsCredentials.getAccessKey(),
awsCredentials.getSecretKey()).withModules(modules).buildContext();
connection = context.getConnection();
connection = context.getApi();
}
@Override
@ -102,7 +103,7 @@ public class JCloudsS3Service extends S3Service {
try {
CopyObjectOptions options = Util.convertCopyObjectOptions(acl, destinationMetadata,
ifModifiedSince, ifUnmodifiedSince, ifMatchTags, ifNoneMatchTags);
org.jclouds.aws.s3.domain.S3Object.Metadata jcObjectMetadata = connection.copyObject(
org.jclouds.aws.s3.domain.ObjectMetadata jcObjectMetadata = connection.copyBlob(
sourceBucketName, sourceObjectKey, destinationBucketName, destinationObjectKey,
options).get(requestTimeoutMilliseconds, TimeUnit.MILLISECONDS);
@ -126,7 +127,7 @@ public class JCloudsS3Service extends S3Service {
throw new UnsupportedOperationException("Bucket ACL is not yet supported");
try {
if (connection.putBucketIfNotExists(bucketName).get(requestTimeoutMilliseconds,
if (connection.createContainer(bucketName).get(requestTimeoutMilliseconds,
TimeUnit.MILLISECONDS)) {
// Bucket created.
}
@ -140,12 +141,13 @@ public class JCloudsS3Service extends S3Service {
/**
* {@inheritDoc}
*
* @see S3Connection#deleteBucketIfEmpty(String)
* @see S3BlobStore#deleteContainer(String)
*/
@Override
protected void deleteBucketImpl(String bucketName) throws S3ServiceException {
try {
connection.deleteBucketIfEmpty(bucketName);
connection.deleteContainer(bucketName).get(requestTimeoutMilliseconds,
TimeUnit.MILLISECONDS);
} catch (Exception e) {
Utils.<S3ServiceException> rethrowIfRuntimeOrSameType(e);
throw new S3ServiceException("error deleting bucket: " + bucketName, e);
@ -155,12 +157,12 @@ public class JCloudsS3Service extends S3Service {
/**
* {@inheritDoc}
*
* @see S3Connection#deleteObject(String, String)
* @see S3BlobStore#removeBlob(String, String)
*/
@Override
protected void deleteObjectImpl(String bucketName, String objectKey) throws S3ServiceException {
try {
connection.deleteObject(bucketName, objectKey).get(requestTimeoutMilliseconds,
connection.removeBlob(bucketName, objectKey).get(requestTimeoutMilliseconds,
TimeUnit.MILLISECONDS);
} catch (Exception e) {
Utils.<S3ServiceException> rethrowIfRuntimeOrSameType(e);
@ -172,7 +174,7 @@ public class JCloudsS3Service extends S3Service {
@Override
protected AccessControlList getBucketAclImpl(String bucketName) throws S3ServiceException {
try {
org.jclouds.aws.s3.domain.AccessControlList jcACL = connection.getBucketACL(bucketName)
org.jclouds.aws.s3.domain.AccessControlList jcACL = connection.getContainerACL(bucketName)
.get(requestTimeoutMilliseconds, TimeUnit.MILLISECONDS);
return Util.convertAccessControlList(jcACL);
} catch (Exception e) {
@ -198,7 +200,7 @@ public class JCloudsS3Service extends S3Service {
protected AccessControlList getObjectAclImpl(String bucketName, String objectKey)
throws S3ServiceException {
try {
org.jclouds.aws.s3.domain.AccessControlList jcACL = connection.getObjectACL(bucketName,
org.jclouds.aws.s3.domain.AccessControlList jcACL = connection.getBlobACL(bucketName,
objectKey).get(requestTimeoutMilliseconds, TimeUnit.MILLISECONDS);
return Util.convertAccessControlList(jcACL);
} catch (Exception e) {
@ -221,7 +223,7 @@ public class JCloudsS3Service extends S3Service {
if (ifNoneMatchTags != null)
throw new IllegalArgumentException("ifNoneMatchTags");
return Util.convertObjectHead(connection.headObject(bucketName, objectKey));
return Util.convertObjectHead(connection.blobMetadata(bucketName, objectKey));
} catch (Exception e) {
Utils.<S3ServiceException> rethrowIfRuntimeOrSameType(e);
throw new S3ServiceException(String.format("error retrieving object head: %1$s:%2$s",
@ -236,7 +238,8 @@ public class JCloudsS3Service extends S3Service {
try {
GetOptions options = Util.convertGetObjectOptions(ifModifiedSince, ifUnmodifiedSince,
ifMatchTags, ifNoneMatchTags);
return Util.convertObject(connection.getObject(bucketName, objectKey, options).get());
return Util.convertObject(connection.getBlob(bucketName, objectKey, options).get(
requestTimeoutMilliseconds, TimeUnit.MILLISECONDS));
} catch (Exception e) {
Utils.<S3ServiceException> rethrowIfRuntimeOrSameType(e);
throw new S3ServiceException(String.format("error retrieving object: %1$s:%2$s",
@ -259,8 +262,7 @@ public class JCloudsS3Service extends S3Service {
@Override
protected S3Bucket[] listAllBucketsImpl() throws S3ServiceException {
try {
List<org.jclouds.aws.s3.domain.S3Bucket.Metadata> jcBucketList = connection
.listOwnedBuckets();
List<org.jclouds.aws.s3.domain.BucketMetadata> jcBucketList = connection.listContainers();
return Util.convertBuckets(jcBucketList);
} catch (Exception e) {
Utils.<S3ServiceException> rethrowIfRuntimeOrSameType(e);
@ -275,15 +277,15 @@ public class JCloudsS3Service extends S3Service {
try {
List<S3Object> jsObjects = new ArrayList<S3Object>();
List<String> commonPrefixes = new ArrayList<String>();
org.jclouds.aws.s3.domain.S3Bucket jcBucket = null;
ListBucketResponse jcBucket = null;
do {
ListBucketOptions options = Util.convertListObjectOptions(prefix, priorLastKey,
delimiter, maxListingLength);
jcBucket = connection.listBucket(bucketName, options).get(requestTimeoutMilliseconds,
jcBucket = connection.listBlobs(bucketName, options).get(requestTimeoutMilliseconds,
TimeUnit.MILLISECONDS);
jsObjects.addAll(Arrays.asList(Util.convertObjectHeads(jcBucket.getContents())));
jsObjects.addAll(Arrays.asList(Util.convertObjectHeads(jcBucket)));
commonPrefixes.addAll(jcBucket.getCommonPrefixes());
if (jcBucket.isTruncated()) {
priorLastKey = jsObjects.get(jsObjects.size() - 1).getKey();
@ -320,7 +322,7 @@ public class JCloudsS3Service extends S3Service {
throws S3ServiceException {
try {
org.jclouds.aws.s3.domain.AccessControlList jcACL = Util.convertAccessControlList(jsACL);
connection.putBucketACL(bucketName, jcACL).get(requestTimeoutMilliseconds,
connection.putContainerACL(bucketName, jcACL).get(requestTimeoutMilliseconds,
TimeUnit.MILLISECONDS);
} catch (Exception e) {
Utils.<S3ServiceException> rethrowIfRuntimeOrSameType(e);
@ -333,7 +335,7 @@ public class JCloudsS3Service extends S3Service {
throws S3ServiceException {
try {
org.jclouds.aws.s3.domain.AccessControlList jcACL = Util.convertAccessControlList(jsACL);
connection.putObjectACL(bucketName, objectKey, jcACL).get(requestTimeoutMilliseconds,
connection.putBlobACL(bucketName, objectKey, jcACL).get(requestTimeoutMilliseconds,
TimeUnit.MILLISECONDS);
} catch (Exception e) {
Utils.<S3ServiceException> rethrowIfRuntimeOrSameType(e);
@ -346,7 +348,7 @@ public class JCloudsS3Service extends S3Service {
try {
PutObjectOptions options = Util.convertPutObjectOptions(jsObject.getAcl());
org.jclouds.aws.s3.domain.S3Object jcObject = Util.convertObject(jsObject);
byte eTag[] = connection.putObject(bucketName, jcObject, options).get(
byte eTag[] = connection.putBlob(bucketName, jcObject, options).get(
requestTimeoutMilliseconds, TimeUnit.MILLISECONDS);
jsObject.setMd5Hash(eTag);
return jsObject;

View File

@ -32,17 +32,16 @@ import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import javax.ws.rs.core.MediaType;
import org.jclouds.aws.s3.domain.CannedAccessPolicy;
import org.jclouds.aws.s3.domain.CanonicalUser;
import org.jclouds.aws.s3.domain.ListBucketResponse;
import org.jclouds.aws.s3.options.CopyObjectOptions;
import org.jclouds.aws.s3.options.ListBucketOptions;
import org.jclouds.aws.s3.options.PutObjectOptions;
import org.jclouds.aws.s3.reference.S3Constants;
import org.jclouds.http.HttpUtils;
import org.jclouds.http.options.GetOptions;
import org.jclouds.util.DateService;
@ -72,7 +71,7 @@ import com.google.common.collect.Multimap;
*/
public class Util {
public static S3Bucket convertBucket(org.jclouds.aws.s3.domain.S3Bucket.Metadata jcBucketMD) {
public static S3Bucket convertBucket(org.jclouds.aws.s3.domain.BucketMetadata jcBucketMD) {
S3Bucket jsBucket = new S3Bucket(jcBucketMD.getName());
if (jcBucketMD.getOwner() != null) {
jsBucket.setOwner(new S3Owner(jcBucketMD.getOwner().getId(), jcBucketMD.getOwner()
@ -82,24 +81,23 @@ public class Util {
}
public static S3Bucket[] convertBuckets(
List<org.jclouds.aws.s3.domain.S3Bucket.Metadata> jcBucketMDs) {
List<org.jclouds.aws.s3.domain.BucketMetadata> jcBucketMDs) {
List<S3Bucket> jsBuckets = new ArrayList<S3Bucket>(jcBucketMDs.size());
for (org.jclouds.aws.s3.domain.S3Bucket.Metadata jcBucketMD : jcBucketMDs) {
for (org.jclouds.aws.s3.domain.BucketMetadata jcBucketMD : jcBucketMDs) {
jsBuckets.add(convertBucket(jcBucketMD));
}
return (S3Bucket[]) jsBuckets.toArray(new S3Bucket[jsBuckets.size()]);
}
public static S3Object[] convertObjectHeads(
Set<org.jclouds.aws.s3.domain.S3Object.Metadata> jcObjectMDs) {
List<S3Object> jsObjects = new ArrayList<S3Object>(jcObjectMDs.size());
for (org.jclouds.aws.s3.domain.S3Object.Metadata jcObjectMD : jcObjectMDs) {
public static S3Object[] convertObjectHeads(ListBucketResponse jcBucket) {
List<S3Object> jsObjects = new ArrayList<S3Object>(jcBucket.size());
for (org.jclouds.aws.s3.domain.ObjectMetadata jcObjectMD : jcBucket) {
jsObjects.add(convertObjectHead(jcObjectMD));
}
return (S3Object[]) jsObjects.toArray(new S3Object[jsObjects.size()]);
}
public static S3Object convertObjectHead(org.jclouds.aws.s3.domain.S3Object.Metadata jcObjectMD) {
public static S3Object convertObjectHead(org.jclouds.aws.s3.domain.ObjectMetadata jcObjectMD) {
S3Object jsObject = new S3Object(jcObjectMD.getKey());
if (jcObjectMD.getOwner() != null) {
jsObject.setOwner(new S3Owner(jcObjectMD.getOwner().getId(), jcObjectMD.getOwner()
@ -119,8 +117,8 @@ public class Util {
value = dateService.rfc822DateParse(value.toString()).toDate();
}
if (key.startsWith(S3Constants.USER_METADATA_PREFIX)) {
key = key.substring(S3Constants.USER_METADATA_PREFIX.length());
if (key.startsWith("x-amz-meta-")) {
key = key.substring("x-amz-meta-".length());
}
jsObject.addMetadata(key, value);

View File

@ -44,11 +44,13 @@ import java.util.concurrent.TimeoutException;
import javax.ws.rs.core.MediaType;
import org.apache.commons.io.IOUtils;
import org.jclouds.aws.s3.S3IntegrationTest;
import org.jclouds.aws.s3.config.StubS3ConnectionModule;
import org.jclouds.aws.s3.S3BlobStore;
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.AccessControlList.GroupGranteeURI;
import org.jclouds.aws.s3.domain.AccessControlList.Permission;
import org.jclouds.aws.s3.reference.S3Constants;
import org.jclouds.blobstore.integration.internal.BaseBlobStoreIntegrationTest;
import org.jclouds.http.HttpUtils;
import org.jets3t.service.S3ObjectsChunk;
import org.jets3t.service.S3Service;
@ -63,7 +65,8 @@ import org.jets3t.service.multithread.S3ServiceEventAdaptor;
import org.jets3t.service.multithread.S3ServiceEventListener;
import org.jets3t.service.multithread.S3ServiceMulti;
import org.jets3t.service.security.AWSCredentials;
import org.testng.annotations.BeforeMethod;
import org.testng.ITestContext;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import com.google.common.base.Predicate;
@ -76,125 +79,119 @@ import com.google.common.collect.Iterators;
* @author James Murty
* @author Adrian Cole
*/
@Test(groups = { "integration", "live" }, testName = "s3.JCloudsS3ServiceIntegrationTest")
public class JCloudsS3ServiceIntegrationTest extends S3IntegrationTest {
@Test(groups = { "live" }, testName = "jets3t.JCloudsS3ServiceIntegrationTest")
public class JCloudsS3ServiceLiveTest
extends
BaseBlobStoreIntegrationTest<S3BlobStore, BucketMetadata, ObjectMetadata, org.jclouds.aws.s3.domain.S3Object> {
AWSCredentials credentials;
S3Service service;
/**
* overridden only to get access to the amazon credentials used for jets3t initialization.
*/
@Override
protected void createLiveS3Context(String AWSAccessKeyId, String AWSSecretAccessKey) {
credentials = new AWSCredentials(AWSAccessKeyId, AWSSecretAccessKey);
super.createLiveS3Context(AWSAccessKeyId, AWSSecretAccessKey);
}
/**
* initialize a new JCloudsS3Service, but passing JavaUrlHttpCommandExecutorServiceModule(), as
* it is easier to debug in unit tests.
*
* @throws S3ServiceException
*/
@BeforeMethod
public void testJCloudsS3Service() throws S3ServiceException {
service = (credentials != null) ? new JCloudsS3Service(credentials) : new JCloudsS3Service(
new AWSCredentials("foo", "bar"), new StubS3ConnectionModule());
assert service != null;
@BeforeClass(groups = { "live" }, dependsOnMethods = "setUpResourcesOnThisThread")
protected void createLiveS3Context(ITestContext testContext) throws S3ServiceException {
String account = System.getProperty("jclouds.test.user");
String key = System.getProperty("jclouds.test.key");
if (account != null) {
credentials = new AWSCredentials(account, key);
service = new JCloudsS3Service(credentials);
} else {
assert false : "credentials not present";
}
}
@Test
public void testCreateBucketImpl() throws S3ServiceException, InterruptedException,
ExecutionException, TimeoutException {
String bucketName = getScratchBucketName();
String bucketName = getContainerName();
try {
S3Bucket bucket = service.createBucket(new S3Bucket(bucketName));
assertEquals(bucket.getName(), bucketName);
assertTrue(client.bucketExists(bucketName));
assertTrue(client.containerExists(bucketName));
} finally {
returnScratchBucket(bucketName);
returnContainer(bucketName);
}
}
@Test
public void testDeleteBucketImpl() throws S3ServiceException, InterruptedException,
ExecutionException, TimeoutException {
String bucketName = getScratchBucketName();
String bucketName = getContainerName();
try {
service.deleteBucket(bucketName);
assertFalse(client.bucketExists(bucketName));
assertFalse(client.containerExists(bucketName));
} finally {
returnScratchBucket(bucketName);
returnContainer(bucketName);
}
}
@Test
public void testDeleteObjectImpl() throws InterruptedException, ExecutionException,
TimeoutException, S3ServiceException, IOException {
String bucketName = getBucketName();
String bucketName = getContainerName();
try {
String objectKey = "key-testDeleteObjectImpl";
String objectValue = "test";
org.jclouds.aws.s3.domain.S3Object s3Object = new org.jclouds.aws.s3.domain.S3Object(
objectKey, objectValue);
addObjectToBucket(bucketName, s3Object);
addBlobToContainer(bucketName, s3Object);
service.deleteObject(bucketName, objectKey);
assertEquals(client.headObject(bucketName, objectKey),
org.jclouds.aws.s3.domain.S3Object.Metadata.NOT_FOUND);
} finally {
returnBucket(bucketName);
returnContainer(bucketName);
}
}
@Test
public void testGetObjectDetailsImpl() throws InterruptedException, ExecutionException,
TimeoutException, S3ServiceException, IOException {
String bucketName = getBucketName();
String bucketName = getContainerName();
try {
String objectKey = "key-testGetObjectDetailsImpl";
String objectKey = "key-testGetObjectDetailsImpl".toLowerCase();
String objectValue = "test";
String metadataName = "metadata-name-1";
String metadataValue = "metadata-value-1";
org.jclouds.aws.s3.domain.S3Object s3Object = new org.jclouds.aws.s3.domain.S3Object(
objectKey, objectValue);
s3Object.getMetadata().getUserMetadata().put(
S3Constants.USER_METADATA_PREFIX + metadataName, metadataValue);
addObjectToBucket(bucketName, s3Object);
s3Object.getMetadata().getUserMetadata().put("x-amz-meta-" + metadataName, metadataValue);
addBlobToContainer(bucketName, s3Object);
S3Object objectDetails = service.getObjectDetails(new S3Bucket(bucketName), objectKey);
assertEquals(objectDetails.getKey(), objectKey);
// TODO null keys from s3object! assertEquals(objectDetails.getKey(), objectKey);
assertEquals(objectDetails.getContentLength(), 4);
assertNull(objectDetails.getDataInputStream());
assertEquals(objectDetails.getMetadata(metadataName), metadataValue);
} finally {
returnBucket(bucketName);
returnContainer(bucketName);
}
}
@Test
public void testGetObjectImpl() throws InterruptedException, ExecutionException,
TimeoutException, S3ServiceException, IOException {
String bucketName = getBucketName();
String bucketName = getContainerName();
try {
String objectKey = "key-testGetObjectImpl";
String objectKey = "key-testGetObjectImpl".toLowerCase();
String objectValue = "test";
String metadataName = "metadata-name-2";
String metadataValue = "metadata-value-2";
org.jclouds.aws.s3.domain.S3Object s3Object = new org.jclouds.aws.s3.domain.S3Object(
objectKey, objectValue);
s3Object.getMetadata().getUserMetadata().put(
S3Constants.USER_METADATA_PREFIX + metadataName, metadataValue);
addObjectToBucket(bucketName, s3Object);
s3Object.getMetadata().getUserMetadata().put("x-amz-meta-" + metadataName, metadataValue);
addBlobToContainer(bucketName, s3Object);
S3Object object = service.getObject(new S3Bucket(bucketName), objectKey);
assertEquals(object.getKey(), objectKey);
// TODO null keys from s3object! assertEquals(object.getKey(), objectKey);
assertNotNull(object.getDataInputStream());
assertEquals(IOUtils.toString(object.getDataInputStream()), objectValue);
assertEquals(object.getContentLength(), objectValue.length());
@ -202,43 +199,43 @@ public class JCloudsS3ServiceIntegrationTest extends S3IntegrationTest {
// TODO: Test conditional gets
} finally {
returnBucket(bucketName);
returnContainer(bucketName);
}
}
@Test
public void testListAllBucketsImpl() throws InterruptedException, ExecutionException,
TimeoutException, S3ServiceException {
String bucketName = getBucketName();
String bucketName = getContainerName();
try {
// Ensure there is at least 1 bucket in S3 account to list and compare.
S3Bucket[] jsBuckets = service.listAllBuckets();
List<org.jclouds.aws.s3.domain.S3Bucket.Metadata> jcBuckets = client.listOwnedBuckets();
List<org.jclouds.aws.s3.domain.BucketMetadata> jcBuckets = client.listContainers();
assert jsBuckets.length == jcBuckets.size();
Iterator<org.jclouds.aws.s3.domain.S3Bucket.Metadata> jcBucketsIter = jcBuckets.iterator();
Iterator<org.jclouds.aws.s3.domain.BucketMetadata> jcBucketsIter = jcBuckets.iterator();
for (S3Bucket jsBucket : jsBuckets) {
assert jcBucketsIter.hasNext();
org.jclouds.aws.s3.domain.S3Bucket.Metadata jcBucket = jcBucketsIter.next();
org.jclouds.aws.s3.domain.BucketMetadata jcBucket = jcBucketsIter.next();
assert jsBucket.getName().equals(jcBucket.getName());
}
} finally {
returnBucket(bucketName);
returnContainer(bucketName);
}
}
@Test
public void testListObjectsChunkedImpl() throws InterruptedException, ExecutionException,
TimeoutException, IOException, S3ServiceException {
String bucketName = getBucketName();
String bucketName = getContainerName();
try {
addObjectToBucket(bucketName, "item1/subobject2");
addObjectToBucket(bucketName, "item2");
addObjectToBucket(bucketName, "object1");
addObjectToBucket(bucketName, "object2/subobject1");
addBlobToContainer(bucketName, "item1/subobject2");
addBlobToContainer(bucketName, "item2");
addBlobToContainer(bucketName, "object1");
addBlobToContainer(bucketName, "object2/subobject1");
S3ObjectsChunk chunk;
@ -306,7 +303,7 @@ public class JCloudsS3ServiceIntegrationTest extends S3IntegrationTest {
assertEquals(chunk.getPrefix(), "item");
assertNull(chunk.getPriorLastKey());
} finally {
returnBucket(bucketName);
returnContainer(bucketName);
}
}
@ -315,11 +312,11 @@ public class JCloudsS3ServiceIntegrationTest extends S3IntegrationTest {
TimeoutException, IOException, S3ServiceException {
String bucketName = null;
try {
bucketName = getScratchBucketName();
addObjectToBucket(bucketName, "item1/subobject2");
addObjectToBucket(bucketName, "item2");
addObjectToBucket(bucketName, "object1");
addObjectToBucket(bucketName, "object2/subobject1");
bucketName = getContainerName();
addBlobToContainer(bucketName, "item1/subobject2");
addBlobToContainer(bucketName, "item2");
addBlobToContainer(bucketName, "object1");
addBlobToContainer(bucketName, "object2/subobject1");
S3Object[] objects;
@ -348,14 +345,14 @@ public class JCloudsS3ServiceIntegrationTest extends S3IntegrationTest {
assertEquals(objects.length, 1);
assertEquals(objects[0].getKey(), "item2");
} finally {
returnBucket(bucketName);
returnContainer(bucketName);
}
}
@Test
public void testPutObjectImpl() throws S3ServiceException, InterruptedException,
ExecutionException, TimeoutException, NoSuchAlgorithmException, IOException {
String bucketName = getBucketName();
String bucketName = getContainerName();
try {
String objectKey = "putObject";
@ -365,8 +362,8 @@ public class JCloudsS3ServiceIntegrationTest extends S3IntegrationTest {
// Upload empty object
requestObject = new S3Object(objectKey);
jsResultObject = service.putObject(new S3Bucket(bucketName), requestObject);
jcObject = client.getObject(bucketName, objectKey).get(10, TimeUnit.SECONDS);
assertEquals(jcObject.getKey(), objectKey);
jcObject = client.getBlob(bucketName, objectKey).get(10, TimeUnit.SECONDS);
// TODO null keys from s3object! assertEquals(jcObject.getKey(), objectKey);
assertEquals(jcObject.getMetadata().getSize(), 0);
assertEquals(jcObject.getMetadata().getContentType(), MediaType.APPLICATION_OCTET_STREAM);
assertEquals(jsResultObject.getKey(), requestObject.getKey());
@ -376,8 +373,8 @@ public class JCloudsS3ServiceIntegrationTest extends S3IntegrationTest {
// Upload unicode-named object
requestObject = new S3Object("Ÿn<EFBFBD>˜dŽ-object");
jsResultObject = service.putObject(new S3Bucket(bucketName), requestObject);
jcObject = client.getObject(bucketName, requestObject.getKey()).get(10, TimeUnit.SECONDS);
assertEquals(jcObject.getKey(), requestObject.getKey());
jcObject = client.getBlob(bucketName, requestObject.getKey()).get(10, TimeUnit.SECONDS);
// TODO null keys from s3object! assertEquals(jcObject.getKey(), requestObject.getKey());
assertEquals(jcObject.getMetadata().getSize(), 0);
assertEquals(jcObject.getMetadata().getContentType(), MediaType.APPLICATION_OCTET_STREAM);
assertEquals(jsResultObject.getKey(), requestObject.getKey());
@ -388,7 +385,7 @@ public class JCloudsS3ServiceIntegrationTest extends S3IntegrationTest {
String data = "This is my Ÿn<6E>˜dŽ data";
requestObject = new S3Object(objectKey, data);
jsResultObject = service.putObject(new S3Bucket(bucketName), requestObject);
jcObject = client.getObject(bucketName, objectKey).get(10, TimeUnit.SECONDS);
jcObject = client.getBlob(bucketName, objectKey).get(10, TimeUnit.SECONDS);
assertEquals(jcObject.getMetadata().getSize(), data.getBytes("UTF-8").length);
assertTrue(jcObject.getMetadata().getContentType().startsWith("text/plain"));
assertEquals(jsResultObject.getContentLength(), data.getBytes("UTF-8").length);
@ -396,19 +393,18 @@ public class JCloudsS3ServiceIntegrationTest extends S3IntegrationTest {
// Upload object with metadata
requestObject = new S3Object(objectKey);
requestObject.addMetadata(S3Constants.USER_METADATA_PREFIX + "my-metadata-1", "value-1");
requestObject.addMetadata("x-amz-meta-" + "my-metadata-1", "value-1");
jsResultObject = service.putObject(new S3Bucket(bucketName), requestObject);
jcObject = client.getObject(bucketName, objectKey).get(10, TimeUnit.SECONDS);
jcObject = client.getBlob(bucketName, objectKey).get(10, TimeUnit.SECONDS);
assertEquals(Iterables.getLast(jcObject.getMetadata().getUserMetadata().get(
S3Constants.USER_METADATA_PREFIX + "my-metadata-1")), "value-1");
assertEquals(jsResultObject
.getMetadata(S3Constants.USER_METADATA_PREFIX + "my-metadata-1"), "value-1");
"my-metadata-1")), "value-1");
assertEquals(jsResultObject.getMetadata("x-amz-meta-" + "my-metadata-1"), "value-1");
// Upload object with canned public-read ACL
requestObject = new S3Object(objectKey);
requestObject.setAcl(AccessControlList.REST_CANNED_PUBLIC_READ);
jsResultObject = service.putObject(new S3Bucket(bucketName), requestObject);
org.jclouds.aws.s3.domain.AccessControlList jcACL = client.getObjectACL(bucketName,
org.jclouds.aws.s3.domain.AccessControlList jcACL = client.getBlobACL(bucketName,
objectKey).get(10, TimeUnit.SECONDS);
assertTrue(jcACL.hasPermission(GroupGranteeURI.ALL_USERS, Permission.READ));
assertTrue(jcACL.hasPermission(jcACL.getOwner().getId(), Permission.FULL_CONTROL));
@ -424,12 +420,12 @@ public class JCloudsS3ServiceIntegrationTest extends S3IntegrationTest {
data = "Here is some d‡tˆ for you";
requestObject.setDataInputStream(new ByteArrayInputStream(data.getBytes("UTF-8")));
jsResultObject = service.putObject(new S3Bucket(bucketName), requestObject);
jcObject = client.getObject(bucketName, objectKey).get(10, TimeUnit.SECONDS);
jcObject = client.getBlob(bucketName, objectKey).get(10, TimeUnit.SECONDS);
assertTrue(jsResultObject.verifyData(data.getBytes("UTF-8")));
assertEquals(jsResultObject.getMd5HashAsHex(), HttpUtils.toHexString(jcObject
.getMetadata().getETag()));
} finally {
returnBucket(bucketName);
returnContainer(bucketName);
}
}
@ -437,7 +433,7 @@ public class JCloudsS3ServiceIntegrationTest extends S3IntegrationTest {
@SuppressWarnings("unchecked")
public void testCopyObjectImpl() throws InterruptedException, ExecutionException,
TimeoutException, IOException, S3ServiceException {
String bucketName = getBucketName();
String bucketName = getContainerName();
try {
String data = "This is my data";
String sourceObjectKey = "šriginalObject"; // Notice the use of non-ASCII
@ -448,9 +444,9 @@ public class JCloudsS3ServiceIntegrationTest extends S3IntegrationTest {
org.jclouds.aws.s3.domain.S3Object sourceObject = new org.jclouds.aws.s3.domain.S3Object(
sourceObjectKey, data);
sourceObject.getMetadata().getUserMetadata().put(
S3Constants.USER_METADATA_PREFIX + metadataName, sourceMetadataValue);
addObjectToBucket(bucketName, sourceObject);
sourceObject.getMetadata().getUserMetadata().put("x-amz-meta-" + metadataName,
sourceMetadataValue);
addBlobToContainer(bucketName, sourceObject);
S3Object destinationObject;
Map copyResult;
@ -460,33 +456,32 @@ public class JCloudsS3ServiceIntegrationTest extends S3IntegrationTest {
destinationObject = new S3Object(destinationObjectKey);
copyResult = service.copyObject(bucketName, sourceObjectKey, bucketName,
destinationObject, false);
jcDestinationObject = client.getObject(bucketName, destinationObject.getKey()).get(10,
jcDestinationObject = client.getBlob(bucketName, destinationObject.getKey()).get(10,
TimeUnit.SECONDS);
assertEquals(jcDestinationObject.getKey(), destinationObjectKey);
// TODO null keys from s3object! assertEquals(jcDestinationObject.getKey(),
// destinationObjectKey);
assertEquals(Iterators.getLast(jcDestinationObject.getMetadata().getUserMetadata().get(
S3Constants.USER_METADATA_PREFIX + metadataName).iterator()), sourceMetadataValue);
metadataName).iterator()), sourceMetadataValue);
assertEquals(copyResult.get("ETag"), HttpUtils.toHexString(jcDestinationObject
.getMetadata().getETag()));
// Test destination ACL is unchanged (ie private)
org.jclouds.aws.s3.domain.AccessControlList jcACL = client.getObjectACL(bucketName,
org.jclouds.aws.s3.domain.AccessControlList jcACL = client.getBlobACL(bucketName,
destinationObject.getKey()).get(10, TimeUnit.SECONDS);
assertEquals(jcACL.getGrants().size(), 1);
assertTrue(jcACL.hasPermission(jcACL.getOwner().getId(), Permission.FULL_CONTROL));
// Copy with metadata replaced
destinationObject = new S3Object(destinationObjectKey);
destinationObject.addMetadata(S3Constants.USER_METADATA_PREFIX + metadataName,
destinationMetadataValue);
destinationObject.addMetadata("x-amz-meta-" + metadataName, destinationMetadataValue);
copyResult = service.copyObject(bucketName, sourceObjectKey, bucketName,
destinationObject, true);
jcDestinationObject = client.getObject(bucketName, destinationObject.getKey()).get(10,
jcDestinationObject = client.getBlob(bucketName, destinationObject.getKey()).get(10,
TimeUnit.SECONDS);
assertEquals(Iterators.getLast(jcDestinationObject.getMetadata().getUserMetadata().get(
S3Constants.USER_METADATA_PREFIX + metadataName).iterator()),
destinationMetadataValue);
metadataName).iterator()), destinationMetadataValue);
// Test destination ACL is unchanged (ie private)
jcACL = client.getObjectACL(bucketName, destinationObject.getKey()).get(10,
TimeUnit.SECONDS);
jcACL = client.getBlobACL(bucketName, destinationObject.getKey())
.get(10, TimeUnit.SECONDS);
assertEquals(jcACL.getGrants().size(), 1);
assertTrue(jcACL.hasPermission(jcACL.getOwner().getId(), Permission.FULL_CONTROL));
@ -496,14 +491,14 @@ public class JCloudsS3ServiceIntegrationTest extends S3IntegrationTest {
copyResult = service.copyObject(bucketName, sourceObjectKey, bucketName,
destinationObject, false);
// Test destination ACL is changed (ie public-read)
jcACL = client.getObjectACL(bucketName, destinationObject.getKey()).get(10,
TimeUnit.SECONDS);
jcACL = client.getBlobACL(bucketName, destinationObject.getKey())
.get(10, TimeUnit.SECONDS);
assertEquals(jcACL.getGrants().size(), 2);
assertTrue(jcACL.hasPermission(jcACL.getOwner().getId(), Permission.FULL_CONTROL));
assertTrue(jcACL.hasPermission(GroupGranteeURI.ALL_USERS, Permission.READ));
} finally {
returnBucket(bucketName);
returnContainer(bucketName);
}
}
@ -511,7 +506,7 @@ public class JCloudsS3ServiceIntegrationTest extends S3IntegrationTest {
@SuppressWarnings("unchecked")
public void testPutAndGetBucketAclImpl() throws InterruptedException, ExecutionException,
TimeoutException, S3ServiceException {
String bucketName = getScratchBucketName();
String bucketName = getContainerName();
try {
S3Bucket bucket = new S3Bucket(bucketName);
AccessControlList acl = null;
@ -563,7 +558,8 @@ public class JCloudsS3ServiceIntegrationTest extends S3IntegrationTest {
});
assertEquals(gap.getPermission(), org.jets3t.service.acl.Permission.PERMISSION_READ_ACP);
} finally {
returnScratchBucket(bucketName);
// need to delete this container as we've modified its acls
destroyContainer(bucketName);
}
}
@ -571,7 +567,7 @@ public class JCloudsS3ServiceIntegrationTest extends S3IntegrationTest {
@SuppressWarnings("unchecked")
public void testGetAndPutObjectAclImpl() throws InterruptedException, ExecutionException,
TimeoutException, S3ServiceException, NoSuchAlgorithmException, IOException {
String bucketName = getBucketName();
String bucketName = getContainerName();
try {
S3Bucket bucket = new S3Bucket(bucketName);
S3Object object = new S3Object("testGetAndPutObjectAclImpl", "my data");
@ -627,7 +623,7 @@ public class JCloudsS3ServiceIntegrationTest extends S3IntegrationTest {
});
assertEquals(gap.getPermission(), org.jets3t.service.acl.Permission.PERMISSION_READ_ACP);
} finally {
returnBucket(bucketName);
returnContainer(bucketName);
}
}
@ -639,7 +635,7 @@ public class JCloudsS3ServiceIntegrationTest extends S3IntegrationTest {
byte[] dataBuffer = new byte[OBJECT_SIZE];
String bucketName = getBucketName();
String bucketName = getContainerName();
try {
S3Bucket bucket = new S3Bucket(bucketName);
S3Object[] objects = new S3Object[OBJECT_COUNT];
@ -675,12 +671,11 @@ public class JCloudsS3ServiceIntegrationTest extends S3IntegrationTest {
multiService.putObjects(bucket, objects);
assertEquals(countOfUploadCompletions[0], OBJECT_COUNT);
org.jclouds.aws.s3.domain.S3Bucket theBucket = client.listBucket(bucketName).get(10,
TimeUnit.SECONDS);
assertEquals(theBucket.getSize(), OBJECT_COUNT);
ListBucketResponse theBucket = client.listBlobs(bucketName).get(10, TimeUnit.SECONDS);
assertEquals(theBucket.size(), OBJECT_COUNT);
} finally {
returnBucket(bucketName);
returnContainer(bucketName);
}
}

View File

@ -126,9 +126,21 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<executions>
<execution>
<id>integration</id>
<phase>integration-test</phase>
<goals>
<goal>test</goal>
</goals>
<configuration>
<!-- to keep garbage from previous tests from affecting the performance of the next -->
<forkMode>pertest</forkMode>
<parallel>classes</parallel>
<threadCount>1</threadCount>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>

View File

@ -43,7 +43,7 @@ import java.util.concurrent.ExecutorCompletionService;
* @author Adrian Cole
*/
@Test(sequential = true, timeOut = 2 * 60 * 1000, testName = "s3.S3ParserTest")
public class S3ParserTest extends org.jclouds.aws.s3.commands.S3ParserTest {
public class S3ParserTest extends org.jclouds.aws.s3.xml.S3ParserTest {
class MockHttpURLConnection extends HttpURLConnection {
private String content;
@ -81,10 +81,9 @@ public class S3ParserTest extends org.jclouds.aws.s3.commands.S3ParserTest {
}
@Test
void testAmazonParseListAllMyBucketsParallelResponseTime()
throws InterruptedException, ExecutionException {
CompletionService<Boolean> completer = new ExecutorCompletionService<Boolean>(
exec);
void testAmazonParseListAllMyBucketsParallelResponseTime() throws InterruptedException,
ExecutionException {
CompletionService<Boolean> completer = new ExecutorCompletionService<Boolean>(exec);
for (int i = 0; i < LOOP_COUNT; i++)
completer.submit(new Callable<Boolean>() {
@ -115,19 +114,18 @@ public class S3ParserTest extends org.jclouds.aws.s3.commands.S3ParserTest {
assert buckets.size() == 2;
}
private ListAllMyBucketsResponse runAmazonParseListAllMyBuckets()
throws IOException {
ListAllMyBucketsResponse response = new ListAllMyBucketsResponse(
new MockHttpURLConnection(listAllMyBucketsResultOn200));
private ListAllMyBucketsResponse runAmazonParseListAllMyBuckets() throws IOException {
ListAllMyBucketsResponse response = new ListAllMyBucketsResponse(new MockHttpURLConnection(
listAllMyBucketsResultOn200));
return response;
}
@Test(enabled = false)
public void testAmazonCanParseListBucketResult() throws IOException {
ListBucketResponse response = runAmazonParseListBucketResult();
ListEntry content = (ListEntry) response.entries.get(0);
assert content.key.equals("3366");
assert content.lastModified.equals(new DateTime(
"2009-03-12T02:00:13.000Z").toDate());
assert content.lastModified.equals(new DateTime("2009-03-12T02:00:13.000Z").toDate());
assert content.eTag.equals("\"9d7bb64e8e18ee34eec06dd2cf37b766\"");
assert content.size == 136;
assert content.owner.id
@ -136,24 +134,22 @@ public class S3ParserTest extends org.jclouds.aws.s3.commands.S3ParserTest {
assert content.storageClass.equals("STANDARD");
}
private ListBucketResponse runAmazonParseListBucketResult()
throws IOException {
ListBucketResponse response = new ListBucketResponse(
new MockHttpURLConnection(listBucketResult));
private ListBucketResponse runAmazonParseListBucketResult() throws IOException {
ListBucketResponse response = new ListBucketResponse(new MockHttpURLConnection(
listAllMyBucketsResultOn200));
return response;
}
@Test
@Test(enabled = false)
void testAmazonParseListBucketResultSerialResponseTime() throws IOException {
for (int i = 0; i < LOOP_COUNT; i++)
runAmazonParseListBucketResult();
}
@Test
void testAmazonParseListBucketResultParallelResponseTime()
throws InterruptedException, ExecutionException {
CompletionService<Boolean> completer = new ExecutorCompletionService<Boolean>(
exec);
@Test(enabled = false)
void testAmazonParseListBucketResultParallelResponseTime() throws InterruptedException,
ExecutionException {
CompletionService<Boolean> completer = new ExecutorCompletionService<Boolean>(exec);
for (int i = 0; i < LOOP_COUNT; i++)
completer.submit(new Callable<Boolean>() {

View File

@ -23,8 +23,6 @@
*/
package org.jclouds.aws.s3;
import static com.google.common.base.Preconditions.checkNotNull;
import java.io.File;
import java.io.InputStream;
import java.util.Arrays;
@ -33,11 +31,9 @@ import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.ExecutionException;
import org.jclouds.aws.s3.reference.S3Constants;
import org.jets3t.service.S3ServiceException;
import org.testng.ITestContext;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Optional;
import org.testng.annotations.Parameters;
import org.testng.annotations.Test;
import com.amazon.s3.AWSAuthConnection;
@ -47,18 +43,18 @@ import com.amazon.s3.AWSAuthConnection;
*
* @author Adrian Cole
*/
@Test(sequential = true, timeOut = 2 * 60 * 1000, testName = "s3.AmazonPerformanceLiveTest", groups = { "live" })
public class AmazonPerformanceLiveTest extends BasePerformance {
@Test(sequential = true, timeOut = 2 * 60 * 1000, testName = "perftest.AmazonPerformanceLiveTest", groups = { "live" })
public class AmazonPerformanceLiveTest extends BasePerformanceLiveTest {
private AWSAuthConnection amzClient;
@BeforeClass(inheritGroups = false, groups = { "live" })
@Parameters( { S3Constants.PROPERTY_AWS_ACCESSKEYID, S3Constants.PROPERTY_AWS_SECRETACCESSKEY })
public void setUpAmazon(@Optional String AWSAccessKeyId, @Optional String AWSSecretAccessKey)
throws S3ServiceException {
AWSAccessKeyId = AWSAccessKeyId != null ? AWSAccessKeyId : sysAWSAccessKeyId;
AWSSecretAccessKey = AWSSecretAccessKey != null ? AWSSecretAccessKey : sysAWSSecretAccessKey;
amzClient = new AWSAuthConnection(checkNotNull(AWSAccessKeyId, "AWSAccessKeyId"),
checkNotNull(AWSSecretAccessKey, "AWSSecretAccessKey"), false);
@BeforeClass(groups = { "live" }, dependsOnMethods = "setUpResourcesOnThisThread")
protected void createLiveS3Context(ITestContext testContext) throws S3ServiceException {
if (testContext.getAttribute("jclouds.test.user") != null) {
amzClient = new AWSAuthConnection((String) testContext.getAttribute("jclouds.test.user"),
(String) testContext.getAttribute("jclouds.test.key"), false);
} else {
throw new RuntimeException("not configured properly");
}
}
@Override

View File

@ -32,7 +32,7 @@ import java.util.concurrent.TimeUnit;
*
* @author Adrian Cole
*/
public abstract class BaseJCloudsPerformance extends BasePerformance {
public abstract class BaseJCloudsPerformanceLiveTest extends BasePerformanceLiveTest {
// boolean get
// (
// int id) throws Exception {
@ -57,7 +57,7 @@ public abstract class BaseJCloudsPerformance extends BasePerformance {
org.jclouds.aws.s3.domain.S3Object object = new org.jclouds.aws.s3.domain.S3Object(key);
object.getMetadata().setContentType(contentType);
object.setData(data);
return client.putObject(bucket, object).get(120, TimeUnit.SECONDS) != null;
return client.putBlob(bucket, object).get(120, TimeUnit.SECONDS) != null;
}
@Override
@ -66,7 +66,7 @@ public abstract class BaseJCloudsPerformance extends BasePerformance {
org.jclouds.aws.s3.domain.S3Object object = new org.jclouds.aws.s3.domain.S3Object(key);
object.getMetadata().setContentType(contentType);
object.setData(data);
return client.putObject(bucket, object).get(120, TimeUnit.SECONDS) != null;
return client.putBlob(bucket, object).get(120, TimeUnit.SECONDS) != null;
}
@Override
@ -76,7 +76,7 @@ public abstract class BaseJCloudsPerformance extends BasePerformance {
object.getMetadata().setContentType(contentType);
object.setData(data);
object.getMetadata().setSize(data.available());
return client.putObject(bucket, object).get(120, TimeUnit.SECONDS) != null;
return client.putBlob(bucket, object).get(120, TimeUnit.SECONDS) != null;
}
@Override
@ -85,6 +85,6 @@ public abstract class BaseJCloudsPerformance extends BasePerformance {
org.jclouds.aws.s3.domain.S3Object object = new org.jclouds.aws.s3.domain.S3Object(key);
object.getMetadata().setContentType(contentType);
object.setData(data);
return client.putObject(bucket, object).get(120, TimeUnit.SECONDS) != null;
return client.putBlob(bucket, object).get(120, TimeUnit.SECONDS) != null;
}
}

View File

@ -23,6 +23,8 @@
*/
package org.jclouds.aws.s3;
import static org.jclouds.aws.s3.options.PutBucketOptions.Builder.createIn;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
@ -36,8 +38,13 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import org.testng.annotations.AfterGroups;
import org.testng.annotations.BeforeGroups;
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.BucketMetadata.LocationConstraint;
import org.jclouds.blobstore.integration.internal.BaseBlobStoreIntegrationTest;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import com.google.inject.Provider;
@ -47,127 +54,144 @@ import com.google.inject.Provider;
*
* @author Adrian Cole
*/
@Test(groups = { "live" }, testName = "s3.S3Performance")
public abstract class BasePerformance extends S3IntegrationTest {
public abstract class BasePerformanceLiveTest extends
BaseBlobStoreIntegrationTest<S3BlobStore, BucketMetadata, ObjectMetadata, S3Object> {
protected int timeoutSeconds = 10;
protected int loopCount = 100;
protected ExecutorService exec;
protected CompletionService<Boolean> completer;
@BeforeGroups(groups = { "live" })
@BeforeClass(groups = { "live" }, dependsOnMethods = "setUpResourcesOnThisThread")
public void setUpCallables() throws InterruptedException, ExecutionException, TimeoutException {
exec = Executors.newCachedThreadPool();
completer = new ExecutorCompletionService<Boolean>(exec);
}
@AfterGroups(groups = { "live" })
@AfterClass(groups = { "live" })
public void tearDownExecutor() throws Exception {
exec.shutdownNow();
exec = null;
}
@Test
// too slow...
@Test(enabled = false)
public void testPutBytesSerialEU() throws Exception {
String euBucketName = createScratchBucketInEU();
String euContainerName = createScratchContainerInEU();
try {
doSerial(new PutBytesCallable(euBucketName), loopCount);
doSerial(new PutBytesCallable(euContainerName), loopCount);
} finally {
returnBucket(euBucketName);
destroyContainer(euContainerName);
}
}
@Test
/**
* using scratch containerName as we are changing location
*
* @throws TimeoutException
* @throws ExecutionException
* @throws InterruptedException
*/
protected String createScratchContainerInEU() throws InterruptedException, ExecutionException,
TimeoutException {
String containerName = getScratchContainerName();
client.createContainer(containerName, createIn(LocationConstraint.EU)).get(30,
TimeUnit.SECONDS);
return containerName;
}
// too slow...
@Test(enabled = false)
public void testPutBytesParallelEU() throws InterruptedException, ExecutionException,
TimeoutException {
String euBucketName = createScratchBucketInEU();
String euContainerName = createScratchContainerInEU();
try {
doParallel(new PutBytesCallable(euBucketName), loopCount);
doParallel(new PutBytesCallable(euContainerName), loopCount);
} finally {
returnBucket(euBucketName);
destroyContainer(euContainerName);
}
}
@Test
public void testPutBytesSerial() throws Exception {
String bucketName = getBucketName();
String bucketName = getContainerName();
try {
doSerial(new PutBytesCallable(bucketName), loopCount / 10);
} finally {
returnBucket(bucketName);
returnContainer(bucketName);
}
}
@Test
public void testPutBytesParallel() throws InterruptedException, ExecutionException,
TimeoutException {
String bucketName = getBucketName();
String bucketName = getContainerName();
try {
doParallel(new PutBytesCallable(bucketName), loopCount);
} finally {
returnBucket(bucketName);
returnContainer(bucketName);
}
}
@Test
public void testPutFileSerial() throws Exception {
String bucketName = getBucketName();
String bucketName = getContainerName();
try {
doSerial(new PutFileCallable(bucketName), loopCount / 10);
} finally {
returnBucket(bucketName);
returnContainer(bucketName);
}
}
@Test
public void testPutFileParallel() throws InterruptedException, ExecutionException,
TimeoutException {
String bucketName = getBucketName();
String bucketName = getContainerName();
try {
doParallel(new PutFileCallable(bucketName), loopCount);
} finally {
returnBucket(bucketName);
returnContainer(bucketName);
}
}
@Test
public void testPutInputStreamSerial() throws Exception {
String bucketName = getBucketName();
String bucketName = getContainerName();
try {
doSerial(new PutInputStreamCallable(bucketName), loopCount / 10);
} finally {
returnBucket(bucketName);
returnContainer(bucketName);
}
}
@Test
public void testPutInputStreamParallel() throws InterruptedException, ExecutionException,
TimeoutException {
String bucketName = getBucketName();
String bucketName = getContainerName();
try {
doParallel(new PutInputStreamCallable(bucketName), loopCount);
} finally {
returnBucket(bucketName);
returnContainer(bucketName);
}
}
@Test
public void testPutStringSerial() throws Exception {
String bucketName = getBucketName();
String bucketName = getContainerName();
try {
doSerial(new PutStringCallable(bucketName), loopCount / 10);
} finally {
returnBucket(bucketName);
returnContainer(bucketName);
}
}
@Test
public void testPutStringParallel() throws InterruptedException, ExecutionException,
TimeoutException {
String bucketName = getBucketName();
String bucketName = getContainerName();
try {
doParallel(new PutStringCallable(bucketName), loopCount);
} finally {
returnBucket(bucketName);
returnContainer(bucketName);
}
}

View File

@ -25,12 +25,14 @@ package org.jclouds.aws.s3;
import java.io.File;
import java.io.InputStream;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import org.jclouds.gae.config.GaeHttpCommandExecutorServiceModule;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import org.testng.v6.Maps;
import com.google.appengine.tools.development.ApiProxyLocalImpl;
import com.google.apphosting.api.ApiProxy;
@ -42,8 +44,8 @@ import com.google.inject.Module;
*
* @author Adrian Cole
*/
@Test(enabled = false, sequential = true, testName = "s3.JCloudsGaePerformanceLiveTest", groups = { "disabled" })
public class JCloudsGaePerformanceLiveTest extends BaseJCloudsPerformance {
@Test(enabled = false, sequential = true, testName = "perftest.JCloudsGaePerformanceLiveTest", groups = { "disabled" })
public class JCloudsGaePerformanceLiveTest extends BaseJCloudsPerformanceLiveTest {
@Override
@Test(enabled = false)
@ -146,12 +148,6 @@ public class JCloudsGaePerformanceLiveTest extends BaseJCloudsPerformance {
return super.putString(bucket, key, data, contentType);
}
@Override
protected void deleteEverything() throws Exception {
setupApiProxy();
super.deleteEverything();
}
@BeforeMethod
void setupApiProxy() {
ApiProxy.setEnvironmentForCurrentThread(new TestEnvironment());
@ -194,6 +190,10 @@ public class JCloudsGaePerformanceLiveTest extends BaseJCloudsPerformance {
public boolean isAdmin() {
return false;
}
public Map<String, Object> getAttributes() {
return Maps.newHashMap();
}
}
@Override

View File

@ -28,8 +28,8 @@ import org.testng.annotations.Test;
import com.google.inject.Module;
@Test(sequential = true, testName = "s3.JCloudsNioPerformanceLiveTest", groups = { "live" })
public class JCloudsNioPerformanceLiveTest extends BaseJCloudsPerformance {
@Test(sequential = true, testName = "perftest.JCloudsNioPerformanceLiveTest", groups = { "live" })
public class JCloudsNioPerformanceLiveTest extends BaseJCloudsPerformanceLiveTest {
@Override
protected Module createHttpModule() {

View File

@ -22,15 +22,21 @@
* ====================================================================
*/
package org.jclouds.aws.s3;
import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule;
import org.testng.annotations.Test;
import com.google.inject.Module;
/**
* Tests the default JClouds client.
*
* @author Adrian Cole
*
*/
@Test(sequential = true, timeOut = 2 * 60 * 1000, testName = "s3.JCloudsPerformanceLiveTest", groups = {"live"})
public class JCloudsPerformanceLiveTest extends BaseJCloudsPerformance {
@Test(sequential = true, timeOut = 2 * 60 * 1000, testName = "perftest.JCloudsPerformanceLiveTest", groups = { "live" })
public class JCloudsPerformanceLiveTest extends BaseJCloudsPerformanceLiveTest {
@Override
protected Module createHttpModule() {
return new JavaUrlHttpCommandExecutorServiceModule();
}
}

View File

@ -46,21 +46,17 @@ package org.jclouds.aws.s3;
* under the License.
* ====================================================================
*/
import static com.google.common.base.Preconditions.checkNotNull;
import java.io.File;
import java.io.InputStream;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import org.jclouds.aws.s3.reference.S3Constants;
import org.jets3t.service.S3Service;
import org.jets3t.service.S3ServiceException;
import org.jets3t.service.impl.rest.httpclient.RestS3Service;
import org.jets3t.service.security.AWSCredentials;
import org.testng.ITestContext;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Optional;
import org.testng.annotations.Parameters;
import org.testng.annotations.Test;
/**
@ -68,18 +64,20 @@ import org.testng.annotations.Test;
*
* @author Adrian Cole
*/
@Test(sequential = true, timeOut = 2 * 60 * 1000, testName = "s3.Jets3tPerformanceLiveTest", groups = { "live" })
public class Jets3tPerformanceLiveTest extends BasePerformance {
@Test(sequential = true, timeOut = 2 * 60 * 1000, testName = "perftest.Jets3tPerformanceLiveTest", groups = { "live" })
public class Jets3tPerformanceLiveTest extends BasePerformanceLiveTest {
private S3Service jetClient;
@BeforeClass(inheritGroups = false, groups = { "live" })
@Parameters( { S3Constants.PROPERTY_AWS_ACCESSKEYID, S3Constants.PROPERTY_AWS_SECRETACCESSKEY })
public void setUpJetS3t(@Optional String AWSAccessKeyId, @Optional String AWSSecretAccessKey)
throws S3ServiceException {
AWSAccessKeyId = AWSAccessKeyId != null ? AWSAccessKeyId : sysAWSAccessKeyId;
AWSSecretAccessKey = AWSSecretAccessKey != null ? AWSSecretAccessKey : sysAWSSecretAccessKey;
jetClient = new RestS3Service(new AWSCredentials(checkNotNull(AWSAccessKeyId,
"AWSAccessKeyId"), checkNotNull(AWSSecretAccessKey, "AWSSecretAccessKey")));
@BeforeClass(groups = { "live" }, dependsOnMethods = "setUpResourcesOnThisThread")
protected void createLiveS3Context(ITestContext testContext) throws S3ServiceException {
if (testContext.getAttribute("jclouds.test.user") != null) {
AWSCredentials credentials = new AWSCredentials((String) testContext
.getAttribute("jclouds.test.user"), (String) testContext
.getAttribute("jclouds.test.key"));
jetClient = new RestS3Service(credentials);
} else {
throw new RuntimeException("not configured properly");
}
}
@Override

View File

@ -42,17 +42,34 @@
<module>extensions</module>
<module>samples</module>
</modules>
<properties>
<jclouds.test.initializer>org.jclouds.aws.s3.integration.S3TestInitializer</jclouds.test.initializer>
</properties>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>jclouds-keyvaluestore-core</artifactId>
<artifactId>jclouds-blobstore-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>jclouds-blobstore-core</artifactId>
<version>${project.version}</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>jclouds-aws-core</artifactId>
<version>${project.version}</version>
</dependency>
<!-- why do I have to explicitly import transitive deps of blobstore-test! -->
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>jclouds-httpnio</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>jclouds-aws-core</artifactId>
@ -61,112 +78,4 @@
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.4.3</version>
<executions>
<execution>
<id>integration</id>
<phase>integration-test</phase>
<goals>
<goal>test</goal>
</goals>
<configuration>
<!-- note that the groups/excluded groups don't work due to some problem
in surefire or testng. instead, we have to exclude via file path
<groups>integration</groups>
<excludedGroups>unit,performance,live</excludedGroups> -->
<excludes>
<exclude>**/*LiveTest.java</exclude>
</excludes>
<includes>
<include>**/*IntegrationTest.java</include>
</includes>
<systemProperties>
<property>
<name>jclouds.s3.httpstream.url</name>
<value>${jclouds.s3.httpstream.url}</value>
</property>
<property>
<name>jclouds.s3.httpstream.md5</name>
<value>${jclouds.s3.httpstream.md5}</value>
</property>
</systemProperties>
</configuration>
</execution>
</executions>
<configuration>
<!-- note that the groups/excluded groups don't work due to some problem
in surefire or testng. instead, we have to exclude via file path
<groups>unit,performance</groups>
<excludedGroups>integration,live</excludedGroups> -->
<excludes>
<exclude>**/*IntegrationTest.java</exclude>
<exclude>**/*LiveTest.java</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>live</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.4.3</version>
<executions>
<execution>
<id>integration</id>
<phase>integration-test</phase>
<goals>
<goal>test</goal>
</goals>
<configuration>
<!-- note that the groups/excluded groups don't work due to some problem
in surefire or testng. instead, we have to exclude via file path
<groups>live,integration</groups>
<excludedGroups>unit,performance</excludedGroups> -->
<excludes>
<exclude>none</exclude>
</excludes>
<includes>
<include>**/*IntegrationTest.java</include>
<include>**/*LiveTest.java</include>
</includes>
<systemProperties>
<property>
<name>file.encoding</name>
<value>UTF-8</value>
</property>
<property>
<name>jclouds.aws.accesskeyid</name>
<value>${jclouds.aws.accesskeyid}</value>
</property>
<property>
<name>jclouds.aws.secretaccesskey</name>
<value>${jclouds.aws.secretaccesskey}</value>
</property>
<property>
<name>jclouds.s3.httpstream.url</name>
<value>${jclouds.s3.httpstream.url}</value>
</property>
<property>
<name>jclouds.s3.httpstream.md5</name>
<value>${jclouds.s3.httpstream.md5}</value>
</property>
</systemProperties>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>

View File

@ -28,7 +28,7 @@ import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.jclouds.aws.s3.domain.S3Bucket;
import org.jclouds.aws.s3.domain.BucketMetadata;
/**
* CreateListOwnedBuckets is a class contaning operations to creates a bucket if it doesn't exist
@ -44,13 +44,13 @@ public class CreateListOwnedBuckets {
this.s3Context = context;
}
public List<S3Bucket.Metadata> list() throws InterruptedException, ExecutionException,
public List<BucketMetadata> list() throws InterruptedException, ExecutionException,
TimeoutException {
return s3Context.getConnection().listOwnedBuckets();
return s3Context.getApi().listContainers();
}
public Boolean createBucket(String bucketName) throws InterruptedException, ExecutionException,
TimeoutException {
return s3Context.getConnection().putBucketIfNotExists(bucketName).get(10, TimeUnit.SECONDS);
return s3Context.getApi().createContainer(bucketName).get(10, TimeUnit.SECONDS);
}
}

View File

@ -32,7 +32,7 @@ import javax.annotation.Resource;
import org.jclouds.aws.s3.CreateListOwnedBuckets;
import org.jclouds.aws.s3.S3Context;
import org.jclouds.aws.s3.S3ContextFactory;
import org.jclouds.aws.s3.domain.S3Bucket;
import org.jclouds.aws.s3.domain.BucketMetadata;
import org.jclouds.logging.Logger;
/**
@ -60,7 +60,7 @@ public class MainApp {
// Variables
S3Context context = null;
CreateListOwnedBuckets listMyOwnBuckets = null;
List<S3Bucket.Metadata> myBuckets = null;
List<BucketMetadata> myBuckets = null;
// Args
String accesskeyid = args[0];
@ -79,7 +79,7 @@ public class MainApp {
// List bucket
myBuckets = listMyOwnBuckets.list();
for (S3Bucket.Metadata bucketObj : myBuckets) {
for (BucketMetadata bucketObj : myBuckets) {
System.out.println(String.format(" %1$s", bucketObj));
System.out.println(String.format(": %1$s entries%n", context.createInputStreamMap(
bucketObj.getName()).size()));

View File

@ -23,11 +23,15 @@
*/
package org.jclouds.aws.s3.samples.test;
import java.util.concurrent.TimeUnit;
import org.jclouds.aws.s3.CreateListOwnedBuckets;
import org.jclouds.aws.s3.S3Context;
import org.jclouds.aws.s3.S3ContextBuilder;
import org.jclouds.aws.s3.S3ContextFactory;
import org.jclouds.aws.s3.config.StubS3ConnectionModule;
import org.jclouds.aws.s3.config.StubS3BlobStoreModule;
import org.jclouds.aws.s3.reference.S3Constants;
import org.jclouds.logging.log4j.config.Log4JLoggingModule;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Optional;
@ -58,9 +62,10 @@ public class CreateListOwnedBucketsIntegrationTest {
AWSSecretAccessKey = AWSSecretAccessKey != null ? AWSSecretAccessKey : sysAWSSecretAccessKey;
if ((AWSAccessKeyId != null) && (AWSSecretAccessKey != null))
context = S3ContextFactory.createS3Context(AWSAccessKeyId, AWSSecretAccessKey);
context = S3ContextBuilder.newBuilder(AWSAccessKeyId, AWSSecretAccessKey).withSaxDebug()
.relaxSSLHostname().withModules(new Log4JLoggingModule()).buildContext();
else
context = S3ContextFactory.createS3Context("stub", "stub", new StubS3ConnectionModule());
context = S3ContextFactory.createS3Context("stub", "stub", new StubS3BlobStoreModule());
}
@ -89,7 +94,8 @@ public class CreateListOwnedBucketsIntegrationTest {
public void tearDownClient() throws Exception {
// Removes the bucket created for test purposes only
assert context.getConnection().deleteBucketIfEmpty(bucketPrefix + "needstoexist");
assert context.getApi().deleteContainer(bucketPrefix + "needstoexist").get(10,
TimeUnit.SECONDS);
context.close();
context = null;

View File

@ -25,12 +25,10 @@ package org.jclouds.aws.s3.samples.test;
import org.jclouds.aws.s3.CreateListOwnedBuckets;
import org.jclouds.aws.s3.S3Context;
import org.jclouds.aws.s3.S3ContextFactory;
import org.jclouds.aws.s3.reference.S3Constants;
import org.jclouds.aws.s3.S3ContextBuilder;
import org.jclouds.logging.log4j.config.Log4JLoggingModule;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Optional;
import org.testng.annotations.Parameters;
import org.testng.annotations.Test;
/**
@ -41,22 +39,17 @@ import org.testng.annotations.Test;
@Test(testName = "s3.createListOwnedBucketsLiveTest")
public class CreateListOwnedBucketsLiveTest {
private S3Context context;
private final String sysAWSAccessKeyId = System
.getProperty(S3Constants.PROPERTY_AWS_ACCESSKEYID);
private final String sysAWSSecretAccessKey = System
.getProperty(S3Constants.PROPERTY_AWS_SECRETACCESSKEY);
private String bucketPrefix = (System.getProperty("user.name") + "." + this.getClass()
.getSimpleName()).toLowerCase();
private S3Context context;
@BeforeClass(inheritGroups = false, groups = { "live" })
@Parameters( { S3Constants.PROPERTY_AWS_ACCESSKEYID, S3Constants.PROPERTY_AWS_SECRETACCESSKEY })
public void setUpTest(@Optional String AWSAccessKeyId, @Optional String AWSSecretAccessKey) {
public void setUpTest() {
String account = System.getProperty("jclouds.test.user");
String key = System.getProperty("jclouds.test.key");
AWSAccessKeyId = AWSAccessKeyId != null ? AWSAccessKeyId : sysAWSAccessKeyId;
AWSSecretAccessKey = AWSSecretAccessKey != null ? AWSSecretAccessKey : sysAWSSecretAccessKey;
context = S3ContextFactory.createS3Context(AWSAccessKeyId, AWSSecretAccessKey);
context = S3ContextBuilder.newBuilder(account, key).withSaxDebug().relaxSSLHostname()
.withModules(new Log4JLoggingModule()).buildContext();
}
@ -82,7 +75,7 @@ public class CreateListOwnedBucketsLiveTest {
public void tearDownClient() throws Exception {
// Removes the bucket created for test purposes only
context.getConnection().deleteBucketIfEmpty(bucketPrefix + "needstoexist");
context.getApi().deleteContainer(bucketPrefix + "needstoexist");
context.close();
context = null;

View File

@ -194,11 +194,11 @@
<configuration>
<systemProperties>
<property>
<name>jclouds.aws.accesskeyid</name>
<name>jclouds.test.user</name>
<value>${jclouds.aws.accesskeyid}</value>
</property>
<property>
<name>jclouds.aws.secretaccesskey</name>
<name>jclouds.test.key</name>
<value>${jclouds.aws.secretaccesskey}</value>
</property>
<property>

View File

@ -36,7 +36,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jclouds.aws.s3.S3Context;
import org.jclouds.aws.s3.domain.S3Bucket;
import org.jclouds.aws.s3.domain.BucketMetadata;
import org.jclouds.logging.Logger;
import org.jclouds.samples.googleappengine.domain.BucketResult;
import org.jclouds.samples.googleappengine.functions.MetadataToBucketResult;
@ -84,7 +84,8 @@ public class GetAllBucketsController extends HttpServlet {
private void addMyBucketsToRequest(HttpServletRequest request) throws InterruptedException,
ExecutionException, TimeoutException {
List<S3Bucket.Metadata> myBucketMetadata = context.getConnection().listOwnedBuckets();
System.err.println(context.getAccount() + ":" + context.getEndPoint());
List<BucketMetadata> myBucketMetadata = context.getApi().listContainers();
List<BucketResult> myBuckets = Lists.transform(myBucketMetadata,
metadataToBucketResultProvider.get());
request.setAttribute("buckets", myBuckets);

View File

@ -36,7 +36,9 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.jclouds.aws.s3.S3Context;
import org.jclouds.aws.s3.domain.S3Bucket;
import org.jclouds.aws.s3.domain.BucketMetadata;
import org.jclouds.aws.s3.domain.ListBucketResponse;
import org.jclouds.blobstore.ContainerNotFoundException;
import org.jclouds.logging.Logger;
import com.google.inject.Inject;
@ -102,18 +104,18 @@ public class JCloudsServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try {
List<S3Bucket.Metadata> myBucketMetadata = context.getConnection().listOwnedBuckets();
List<BucketMetadata> myBucketMetadata = context.getApi().listContainers();
List<BucketResult> myBuckets = new ArrayList<BucketResult>();
for (S3Bucket.Metadata metadata : myBucketMetadata) {
for (BucketMetadata metadata : myBucketMetadata) {
BucketResult result = new BucketResult();
result.setName(metadata.getName());
try {
S3Bucket bucket = context.getConnection().listBucket(metadata.getName()).get(10,
TimeUnit.SECONDS);
if (bucket == S3Bucket.NOT_FOUND) {
try {
ListBucketResponse bucket = context.getApi().listBlobs(metadata.getName()).get(
10, TimeUnit.SECONDS);
result.setSize(bucket.size() + "");
} catch (ContainerNotFoundException ex) {
result.setStatus("not found");
} else {
result.setSize(bucket.getSize() + "");
}
} catch (Exception e) {
logger.error(e, "Error listing bucket %1$s", result.getName());

View File

@ -27,35 +27,37 @@ import java.util.concurrent.TimeUnit;
import javax.annotation.Resource;
import org.jclouds.aws.s3.S3Connection;
import org.jclouds.aws.s3.domain.S3Bucket;
import org.jclouds.aws.s3.domain.S3Bucket.Metadata;
import org.jclouds.aws.s3.S3BlobStore;
import org.jclouds.aws.s3.domain.BucketMetadata;
import org.jclouds.aws.s3.domain.ListBucketResponse;
import org.jclouds.blobstore.ContainerNotFoundException;
import org.jclouds.logging.Logger;
import org.jclouds.samples.googleappengine.domain.BucketResult;
import com.google.common.base.Function;
import com.google.inject.Inject;
public class MetadataToBucketResult implements Function<S3Bucket.Metadata, BucketResult> {
private final S3Connection connection;
public class MetadataToBucketResult implements Function<BucketMetadata, BucketResult> {
private final S3BlobStore connection;
@Resource
protected Logger logger = Logger.NULL;
@Inject
public MetadataToBucketResult(S3Connection connection) {
public MetadataToBucketResult(S3BlobStore connection) {
this.connection = connection;
}
public BucketResult apply(Metadata from) {
public BucketResult apply(BucketMetadata from) {
BucketResult result = new BucketResult();
result.setName(from.getName());
try {
S3Bucket bucket = connection.listBucket(from.getName()).get(10, TimeUnit.SECONDS);
if (bucket == S3Bucket.NOT_FOUND) {
try {
ListBucketResponse bucket = connection.listBlobs(from.getName()).get(10,
TimeUnit.SECONDS);
result.setSize(bucket.size() + "");
} catch (ContainerNotFoundException ex) {
result.setStatus("not found");
} else {
result.setSize(bucket.getSize() + "");
}
} catch (Exception e) {
logger.error(e, "Error listing bucket %1$s", result.getName());

View File

@ -24,22 +24,21 @@
package org.jclouds.samples.googleappengine.functest;
import static com.google.common.base.Preconditions.checkNotNull;
import org.apache.commons.io.IOUtils;
import org.jclouds.aws.s3.reference.S3Constants;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Optional;
import org.testng.annotations.Parameters;
import org.testng.annotations.Test;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Properties;
import org.apache.commons.io.IOUtils;
import org.jclouds.aws.s3.reference.S3Constants;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Parameters;
import org.testng.annotations.Test;
/**
* Starts up the Google App Engine for Java Development environment and deploys
* an application which tests S3.
* Starts up the Google App Engine for Java Development environment and deploys an application which
* tests S3.
*
* @author Adrian Cole
*/
@ -47,32 +46,22 @@ import java.util.Properties;
public class GoogleAppEngineLiveTest {
GoogleDevServer server;
private static final String sysAWSAccessKeyId = System
.getProperty(S3Constants.PROPERTY_AWS_ACCESSKEYID);
private static final String sysAWSSecretAccessKey = System
.getProperty(S3Constants.PROPERTY_AWS_SECRETACCESSKEY);
private URL url;
@BeforeTest
@Parameters({"warfile", "devappserver.address", "devappserver.port",
S3Constants.PROPERTY_AWS_ACCESSKEYID,
S3Constants.PROPERTY_AWS_SECRETACCESSKEY})
public void startDevAppServer(final String warfile, final String address,
final String port, @Optional String AWSAccessKeyId,
@Optional String AWSSecretAccessKey) throws Exception {
@Parameters( { "warfile", "devappserver.address", "devappserver.port" })
public void startDevAppServer(final String warfile, final String address, final String port)
throws Exception {
url = new URL(String.format("http://%1$s:%2$s", address, port));
String account = System.getProperty("jclouds.test.user");
String key = System.getProperty("jclouds.test.key");
AWSAccessKeyId = AWSAccessKeyId != null ? AWSAccessKeyId
: sysAWSAccessKeyId;
AWSSecretAccessKey = AWSSecretAccessKey != null ? AWSSecretAccessKey
: sysAWSSecretAccessKey;
checkNotNull(AWSAccessKeyId, "AWSAccessKeyId");
checkNotNull(AWSSecretAccessKey, "AWSSecretAccessKey");
checkNotNull(account, "account");
checkNotNull(key, "key");
Properties props = new Properties();
props.put(S3Constants.PROPERTY_AWS_ACCESSKEYID, AWSAccessKeyId);
props.put(S3Constants.PROPERTY_AWS_SECRETACCESSKEY, AWSSecretAccessKey);
props.put(S3Constants.PROPERTY_AWS_ACCESSKEYID, account);
props.put(S3Constants.PROPERTY_AWS_SECRETACCESSKEY, key);
server = new GoogleDevServer();
server.writePropertiesAndStartServer(address, port, warfile, props);
}
@ -85,8 +74,7 @@ public class GoogleAppEngineLiveTest {
}
@Test(invocationCount = 5, enabled = true)
public void testGuiceJCloudsSerial() throws InterruptedException,
IOException {
public void testGuiceJCloudsSerial() throws InterruptedException, IOException {
URL gurl = new URL(url, "/guice/listbuckets.s3");
InputStream i = gurl.openStream();
String string = IOUtils.toString(i);
@ -94,8 +82,7 @@ public class GoogleAppEngineLiveTest {
}
@Test(invocationCount = 50, enabled = true, threadPoolSize = 10)
public void testGuiceJCloudsParallel() throws InterruptedException,
IOException {
public void testGuiceJCloudsParallel() throws InterruptedException, IOException {
URL gurl = new URL(url, "/guice/listbuckets.s3");
InputStream i = gurl.openStream();
String string = IOUtils.toString(i);

View File

@ -1,137 +0,0 @@
/**
*
* 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.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import org.jclouds.azure.storage.blob.domain.ContainerMetadata;
import org.jclouds.azure.storage.blob.options.CreateContainerOptions;
import org.jclouds.azure.storage.blob.xml.AccountNameEnumerationResultsHandler;
import org.jclouds.azure.storage.domain.MetadataList;
import org.jclouds.azure.storage.filters.SharedKeyAuthentication;
import org.jclouds.azure.storage.options.CreateOptions;
import org.jclouds.azure.storage.options.ListOptions;
import org.jclouds.azure.storage.reference.AzureStorageHeaders;
import org.jclouds.rest.Header;
import org.jclouds.rest.Query;
import org.jclouds.rest.RequestFilters;
import org.jclouds.rest.SkipEncoding;
import org.jclouds.rest.XMLResponseParser;
/**
* 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)
@Header(key = AzureStorageHeaders.VERSION, value = "2009-07-17")
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("/")
@Query(key = "comp", value = "list")
MetadataList<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}")
@Query(key = "restype", value = "container")
boolean createContainer(@PathParam("container") String container,
CreateContainerOptions... options);
/**
* The Delete Container operation marks the specified container for deletion. The container and
* any blobs contained within it are later deleted during garbage collection.
* <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}")
@Query(key = "restype", value = "container")
boolean 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")
@Query(key = "restype", value = "container")
boolean createRootContainer(CreateOptions... options);
/**
*
* @see deleteContainer(String)
* @see createRootContainer(CreateContainerOptions)
*/
@DELETE
@Path("$root")
@Query(key = "restype", value = "container")
boolean deleteRootContainer();
}

View File

@ -1,16 +1,42 @@
/**
*
* 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 org.jclouds.azure.storage.blob.domain.Blob;
import org.jclouds.azure.storage.blob.domain.BlobMetadata;
import org.jclouds.blobstore.BlobStoreContext;
import org.jclouds.cloud.CloudContext;
/**
* Represents an authenticated context to Azure Blob Service.
*
* @see <a href="http://msdn.microsoft.com/en-us/library/dd135733.aspx" />
* @see AzureBlobConnection
* @see AzureBlobStore
* @see CloudContext
* @author Adrian Cole
*
*/
public interface AzureBlobContext extends CloudContext<AzureBlobConnection> {
public interface AzureBlobContext extends BlobStoreContext<AzureBlobStore, BlobMetadata, Blob> {
}

View File

@ -24,6 +24,7 @@
package org.jclouds.azure.storage.blob;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.jclouds.blobstore.reference.BlobStoreConstants.PROPERTY_USER_METADATA_PREFIX;
import static org.jclouds.http.HttpConstants.PROPERTY_HTTP_ADDRESS;
import static org.jclouds.http.HttpConstants.PROPERTY_HTTP_MAX_REDIRECTS;
import static org.jclouds.http.HttpConstants.PROPERTY_HTTP_MAX_RETRIES;
@ -39,9 +40,9 @@ import java.util.List;
import java.util.Properties;
import org.jclouds.azure.storage.blob.config.AzureBlobContextModule;
import org.jclouds.azure.storage.blob.config.RestAzureBlobConnectionModule;
import org.jclouds.azure.storage.blob.config.RestAzureBlobStoreModule;
import org.jclouds.azure.storage.blob.xml.config.AzureBlobParserModule;
import org.jclouds.azure.storage.reference.AzureStorageConstants;
import org.jclouds.azure.storage.xml.config.AzureStorageParserModule;
import org.jclouds.cloud.CloudContextBuilder;
import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule;
import org.jclouds.logging.jdk.config.JDKLoggingModule;
@ -62,8 +63,7 @@ import com.google.inject.Module;
* @author Adrian Cole
* @see AzureBlobContext
*/
public class AzureBlobContextBuilder extends
CloudContextBuilder<AzureBlobConnection, AzureBlobContext> {
public class AzureBlobContextBuilder extends CloudContextBuilder<AzureBlobContext> {
public AzureBlobContextBuilder(Properties props) {
super(props);
@ -71,7 +71,7 @@ public class AzureBlobContextBuilder extends
public static AzureBlobContextBuilder newBuilder(String id, String secret) {
Properties properties = new Properties();
properties.setProperty(PROPERTY_USER_METADATA_PREFIX, "x-ms-meta-");
properties.setProperty(PROPERTY_HTTP_ADDRESS, id + ".blob.core.windows.net");
properties.setProperty(PROPERTY_HTTP_SECURE, "true");
properties.setProperty(PROPERTY_SAX_DEBUG, "false");
@ -100,7 +100,7 @@ public class AzureBlobContextBuilder extends
}
protected void addParserModule(List<Module> modules) {
modules.add(new AzureStorageParserModule());
modules.add(new AzureBlobParserModule());
}
protected void addContextModule(List<Module> modules) {
@ -108,7 +108,7 @@ public class AzureBlobContextBuilder extends
}
protected void addConnectionModule(List<Module> modules) {
modules.add(new RestAzureBlobConnectionModule());
modules.add(new RestAzureBlobStoreModule());
}
}

View File

@ -0,0 +1,238 @@
/**
*
* 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.List;
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.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.ReturnTrueIfContainerAlreadyExists;
import org.jclouds.azure.storage.blob.options.CreateContainerOptions;
import org.jclouds.azure.storage.blob.xml.AccountNameEnumerationResultsHandler;
import org.jclouds.azure.storage.blob.xml.ContainerNameEnumerationResultsHandler;
import org.jclouds.azure.storage.domain.BoundedList;
import org.jclouds.azure.storage.filters.SharedKeyAuthentication;
import org.jclouds.azure.storage.options.CreateOptions;
import org.jclouds.azure.storage.options.ListOptions;
import org.jclouds.azure.storage.reference.AzureStorageHeaders;
import org.jclouds.blobstore.BlobStore;
import org.jclouds.blobstore.binders.BlobBinder;
import org.jclouds.blobstore.functions.BlobKey;
import org.jclouds.blobstore.functions.ThrowKeyNotFoundOn404;
import org.jclouds.http.functions.ParseETagHeader;
import org.jclouds.http.functions.ReturnFalseOn404;
import org.jclouds.http.functions.ReturnTrueOn404;
import org.jclouds.http.options.GetOptions;
import org.jclouds.rest.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;
/**
* 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")
public interface AzureBlobStore extends BlobStore<ContainerMetadata, BlobMetadata, Blob> {
/**
* 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")
List<ContainerMetadata> listContainers();
@GET
@XMLResponseParser(AccountNameEnumerationResultsHandler.class)
@Path("/")
@QueryParams(keys = "comp", values = "list")
BoundedList<ContainerMetadata> listContainers(ListOptions listOptions);
@HEAD
@Path("{container}")
@ExceptionParser(ReturnFalseOn404.class)
@QueryParams(keys = "restype", values = "container")
boolean containerExists(@PathParam("container") String container);
/**
* 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);
@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
* 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(ReturnTrueOn404.class)
@QueryParams(keys = "restype", values = "container")
Future<Boolean> 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
@XMLResponseParser(ContainerNameEnumerationResultsHandler.class)
@Path("{container}")
@QueryParams(keys = { "restype", "comp" }, values = { "container", "list" })
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
@Path("{container}/{key}")
@ResponseParser(ParseETagHeader.class)
Future<byte[]> putBlob(@PathParam("container") String container,
@PathParam("key") @ParamParser(BlobKey.class) @EntityParam(BlobBinder.class) Blob object);
@GET
@ResponseParser(ParseBlobFromHeadersAndHttpContent.class)
@ExceptionParser(ThrowKeyNotFoundOn404.class)
@Path("{container}/{key}")
Future<Blob> getBlob(@PathParam("container") String container, @PathParam("key") String key);
@GET
@ResponseParser(ParseBlobFromHeadersAndHttpContent.class)
@ExceptionParser(ThrowKeyNotFoundOn404.class)
@Path("{container}/{key}")
Future<Blob> getBlob(@PathParam("container") String container, @PathParam("key") String key,
GetOptions options);
@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 blobMetadata(@PathParam("container") String container, @PathParam("key") String key);
@DELETE
@ExceptionParser(ReturnTrueOn404.class)
@Path("{container}/{key}")
Future<Boolean> removeBlob(@PathParam("container") String container, @PathParam("key") String key);
}

View File

@ -0,0 +1,61 @@
/**
*
* 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.binders;
import static com.google.common.base.Preconditions.checkArgument;
import static org.jclouds.blobstore.reference.BlobStoreConstants.PROPERTY_USER_METADATA_PREFIX;
import javax.ws.rs.core.HttpHeaders;
import org.jclouds.azure.storage.blob.domain.BlobMetadata;
import org.jclouds.blobstore.domain.Blob;
import org.jclouds.http.HttpRequest;
import com.google.inject.Inject;
import com.google.inject.name.Named;
public class BlobBinder extends org.jclouds.blobstore.binders.BlobBinder {
@Inject
public BlobBinder(@Named(PROPERTY_USER_METADATA_PREFIX) String metadataPrefix) {
super(metadataPrefix);
}
public void addEntityToRequest(Object entity, HttpRequest request) {
Blob<?> object = (Blob<?>) entity;
checkArgument(object.getMetadata().getSize() >= 0, "size must be set");
if (object.getMetadata() instanceof BlobMetadata) {
BlobMetadata md = (BlobMetadata) object.getMetadata();
if (md.getContentLanguage() != null) {
request.getHeaders().put(HttpHeaders.CONTENT_LANGUAGE, md.getContentLanguage());
}
if (md.getContentEncoding() != null) {
request.getHeaders().put(HttpHeaders.CONTENT_ENCODING, md.getContentEncoding());
}
}
super.addEntityToRequest(entity, request);
}
}

View File

@ -1,22 +1,71 @@
/**
*
* 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 org.jclouds.azure.storage.blob.AzureBlobConnection;
import org.jclouds.azure.storage.blob.AzureBlobContext;
import org.jclouds.azure.storage.blob.AzureBlobStore;
import org.jclouds.azure.storage.blob.domain.Blob;
import org.jclouds.azure.storage.blob.domain.BlobMetadata;
import org.jclouds.azure.storage.blob.internal.GuiceAzureBlobContext;
import org.jclouds.azure.storage.blob.internal.LiveAzureBlobInputStreamMap;
import org.jclouds.azure.storage.blob.internal.LiveAzureBlobObjectMap;
import org.jclouds.blobstore.functions.ParseBlobFromHeadersAndHttpContent.BlobFactory;
import org.jclouds.blobstore.functions.ParseBlobMetadataFromHeaders.BlobMetadataFactory;
import com.google.inject.AbstractModule;
import com.google.inject.TypeLiteral;
import com.google.inject.assistedinject.FactoryProvider;
/**
* Configures the {@link AzureBlobContext}; requires {@link AzureBlobConnection} bound.
* Configures the {@link AzureBlobContext}; requires {@link AzureBlobStore} bound.
*
* @author Adrian Cole
*/
public class AzureBlobContextModule extends AbstractModule {
protected final TypeLiteral<BlobMetadataFactory<BlobMetadata>> objectMetadataFactoryLiteral = new TypeLiteral<BlobMetadataFactory<BlobMetadata>>() {
};
protected final TypeLiteral<BlobFactory<BlobMetadata, Blob>> objectFactoryLiteral = new TypeLiteral<BlobFactory<BlobMetadata, Blob>>() {
};
@Override
protected void configure() {
this.requireBinding(AzureBlobConnection.class);
this.requireBinding(AzureBlobStore.class);
bind(GuiceAzureBlobContext.AzureBlobObjectMapFactory.class).toProvider(
FactoryProvider.newFactory(GuiceAzureBlobContext.AzureBlobObjectMapFactory.class,
LiveAzureBlobObjectMap.class));
bind(GuiceAzureBlobContext.AzureBlobInputStreamMapFactory.class).toProvider(
FactoryProvider.newFactory(
GuiceAzureBlobContext.AzureBlobInputStreamMapFactory.class,
LiveAzureBlobInputStreamMap.class));
bind(AzureBlobContext.class).to(GuiceAzureBlobContext.class);
bind(objectMetadataFactoryLiteral).toProvider(
FactoryProvider.newFactory(objectMetadataFactoryLiteral,
new TypeLiteral<BlobMetadata>() {
}));
bind(objectFactoryLiteral).toProvider(
FactoryProvider.newFactory(objectFactoryLiteral, new TypeLiteral<Blob>() {
}));
}
}

View File

@ -1,29 +0,0 @@
package org.jclouds.azure.storage.blob.config;
import java.net.URI;
import org.jclouds.azure.storage.blob.AzureBlobConnection;
import org.jclouds.azure.storage.config.RestAzureStorageConnectionModule;
import org.jclouds.cloud.ConfiguresCloudConnection;
import org.jclouds.http.RequiresHttp;
import org.jclouds.rest.RestClientFactory;
import com.google.inject.Provides;
import com.google.inject.Singleton;
/**
* Configures the Azure Blob Service connection, including logging and http transport.
*
* @author Adrian Cole
*/
@ConfiguresCloudConnection
@RequiresHttp
public class RestAzureBlobConnectionModule extends RestAzureStorageConnectionModule {
@Provides
@Singleton
protected AzureBlobConnection provideAzureStorageConnection(URI uri, RestClientFactory factory) {
return factory.create(uri, AzureBlobConnection.class);
}
}

View File

@ -0,0 +1,57 @@
/**
*
* 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 java.net.URI;
import org.jclouds.azure.storage.blob.AzureBlobStore;
import org.jclouds.azure.storage.config.RestAzureStorageConnectionModule;
import org.jclouds.cloud.ConfiguresCloudConnection;
import org.jclouds.http.RequiresHttp;
import org.jclouds.rest.RestClientFactory;
import com.google.inject.Provides;
import com.google.inject.Singleton;
/**
* Configures the Azure Blob Service connection, including logging and http transport.
*
* @author Adrian Cole
*/
@ConfiguresCloudConnection
@RequiresHttp
public class RestAzureBlobStoreModule extends RestAzureStorageConnectionModule {
@Override
protected void configure() {
super.configure();
}
@Provides
@Singleton
protected AzureBlobStore provideAzureBlobStore(URI uri, RestClientFactory factory) {
return factory.create(uri, AzureBlobStore.class);
}
}

View File

@ -0,0 +1,108 @@
/**
*
* 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.domain;
import java.net.URI;
import java.util.List;
import org.jclouds.azure.storage.domain.ArrayBoundedList;
/**
*
* @author Adrian Cole
*
*/
public class ArrayListBlobsResponse extends ArrayBoundedList<BlobMetadata> implements
ListBlobsResponse {
/** The serialVersionUID */
private static final long serialVersionUID = -4475709781001190244L;
protected final URI containerUrl;
protected final String blobPrefix;
protected final String delimiter;
@Override
public String toString() {
return "ArrayListBlobsResponse [blobPrefix=" + blobPrefix + ", containerUrl=" + containerUrl
+ ", delimiter=" + delimiter + ", nextMarker=" + nextMarker + ", marker=" + marker
+ ", maxResults=" + maxResults + ", prefix=" + prefix + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((blobPrefix == null) ? 0 : blobPrefix.hashCode());
result = prime * result + ((containerUrl == null) ? 0 : containerUrl.hashCode());
result = prime * result + ((delimiter == null) ? 0 : delimiter.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!super.equals(obj))
return false;
if (getClass() != obj.getClass())
return false;
ArrayListBlobsResponse other = (ArrayListBlobsResponse) obj;
if (blobPrefix == null) {
if (other.blobPrefix != null)
return false;
} else if (!blobPrefix.equals(other.blobPrefix))
return false;
if (containerUrl == null) {
if (other.containerUrl != null)
return false;
} else if (!containerUrl.equals(other.containerUrl))
return false;
if (delimiter == null) {
if (other.delimiter != null)
return false;
} else if (!delimiter.equals(other.delimiter))
return false;
return true;
}
public ArrayListBlobsResponse(URI containerUrl, List<BlobMetadata> contents, String prefix,
String marker, int maxResults, String nextMarker, String delimiter, String blobPrefix) {
super(contents, prefix, marker, maxResults, nextMarker);
this.containerUrl = containerUrl;
this.delimiter = delimiter;
this.blobPrefix = blobPrefix;
}
public String getBlobPrefix() {
return blobPrefix;
}
public String getDelimiter() {
return delimiter;
}
public URI getContainerUrl() {
return containerUrl;
}
}

View File

@ -21,22 +21,31 @@
* under the License.
* ====================================================================
*/
package org.jclouds.keyvaluestore.reference;
package org.jclouds.azure.storage.blob.domain;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
/**
* Configuration properties and constants used in ObjectStore connections.
*
* @author Adrian Cole
*/
public interface ObjectStoreConstants {
public class Blob extends org.jclouds.blobstore.domain.Blob<BlobMetadata> {
/**
* longest time a single Map operation can take before throwing an exception.
*/
public static final String PROPERTY_OBJECTMAP_TIMEOUT = "jclouds.objectmap.timeout";
/**
* time to pause before retrying a transient failure
*/
public static final String PROPERTY_OBJECTMAP_RETRY = "jclouds.objectmap.retry";
public Blob(BlobMetadata metadata, BlobMetadata data) {
super(metadata, data);
}
@Inject
public Blob(@Assisted BlobMetadata metadata) {
super(metadata);
}
public Blob(String key, BlobMetadata data) {
this(new BlobMetadata(key), data);
}
public Blob(String key) {
this(new BlobMetadata(key));
}
}

View File

@ -0,0 +1,97 @@
/**
*
* 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.domain;
import java.net.URI;
import org.joda.time.DateTime;
import com.google.inject.internal.Nullable;
/**
*
* @author Adrian Cole
*
*/
public class BlobMetadata extends org.jclouds.blobstore.domain.BlobMetadata {
/** The serialVersionUID */
private static final long serialVersionUID = 1L;
private URI url;
private String contentLanguage;
protected String dataEncoding;
public BlobMetadata() {
super();
}
public BlobMetadata(String key) {
super(key);
}
public BlobMetadata(String currentName, URI currentUrl, DateTime currentLastModified,
byte[] currentETag, long currentSize, String currentContentType,
@Nullable byte[] contentMD5, @Nullable String currentContentEncoding,
@Nullable String currentContentLanguage) {
this(currentName);
setUrl(currentUrl);
setLastModified(currentLastModified);
setETag(currentETag);
setSize(currentSize);
setContentType(currentContentType);
setContentMD5(contentMD5);
setContentEncoding(currentContentEncoding);
setContentLanguage(currentContentLanguage);
}
public void setUrl(URI url) {
this.url = url;
}
public URI getUrl() {
return url;
}
public void setContentLanguage(String contentLanguage) {
this.contentLanguage = contentLanguage;
}
public String getContentLanguage() {
return contentLanguage;
}
public void setContentEncoding(String dataEncoding) {
this.dataEncoding = dataEncoding;
}
/**
* Specifies what content encodings have been applied to the object and thus what decoding
* mechanisms must be applied in order to obtain the media-type referenced by the Content-Type
* header field.
*
* @see <a href= "http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html?sec14.11" />
*/
public String getContentEncoding() {
return dataEncoding;
}
}

View File

@ -33,38 +33,32 @@ import org.joda.time.DateTime;
* @author Adrian Cole
*
*/
public class ContainerMetadata {
public class ContainerMetadata extends org.jclouds.blobstore.domain.ContainerMetadata {
private URI url;
private DateTime lastModified;
private byte[] eTag;
@Override
public String toString() {
return "ContainerMetadata [eTag=" + Arrays.toString(eTag) + ", lastModified=" + lastModified
+ ", url=" + url + "]";
}
public ContainerMetadata(URI url, DateTime lastModified, byte[] eTag) {
this.url = url;
this.lastModified = lastModified;
this.eTag = eTag;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
int result = super.hashCode();
result = prime * result + Arrays.hashCode(eTag);
result = prime * result + ((lastModified == null) ? 0 : lastModified.hashCode());
result = prime * result + ((url == null) ? 0 : url.hashCode());
return result;
}
@Override
public String toString() {
return "ContainerMetadata [eTag=" + Arrays.toString(eTag) + ", lastModified=" + lastModified
+ ", url=" + url + ", name=" + name + "]";
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
if (!super.equals(obj))
return false;
if (getClass() != obj.getClass())
return false;
@ -84,6 +78,17 @@ public class ContainerMetadata {
return true;
}
public ContainerMetadata(URI url, DateTime lastModified, byte[] eTag) {
super(url.getPath().substring(1));
this.url = url;
this.lastModified = lastModified;
this.eTag = eTag;
}
public ContainerMetadata(String name) {
super(name);
}
public URI getUrl() {
return url;
}

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