diff --git a/aws/core/src/main/java/org/jclouds/aws/s3/S3AsyncClient.java b/aws/core/src/main/java/org/jclouds/aws/s3/S3AsyncClient.java index 17cd8ab31d..da2cbd73d5 100644 --- a/aws/core/src/main/java/org/jclouds/aws/s3/S3AsyncClient.java +++ b/aws/core/src/main/java/org/jclouds/aws/s3/S3AsyncClient.java @@ -54,6 +54,7 @@ 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.predicates.validators.BucketNameValidator; import org.jclouds.aws.s3.xml.AccessControlListHandler; import org.jclouds.aws.s3.xml.BucketLoggingHandler; import org.jclouds.aws.s3.xml.CopyObjectHandler; @@ -75,6 +76,7 @@ import org.jclouds.rest.annotations.ExceptionParser; import org.jclouds.rest.annotations.Headers; import org.jclouds.rest.annotations.HostPrefixParam; import org.jclouds.rest.annotations.ParamParser; +import org.jclouds.rest.annotations.ParamValidators; import org.jclouds.rest.annotations.QueryParams; import org.jclouds.rest.annotations.RequestFilters; import org.jclouds.rest.annotations.ResponseParser; @@ -116,7 +118,8 @@ public interface S3AsyncClient { @Path("{key}") @ExceptionParser(ReturnNullOnKeyNotFound.class) @ResponseParser(ParseObjectFromHeadersAndHttpContent.class) - ListenableFuture getObject(@HostPrefixParam String bucketName, + ListenableFuture getObject( + @HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName, @PathParam("key") String key, GetOptions... options); /** @@ -126,7 +129,8 @@ public interface S3AsyncClient { @Path("{key}") @ExceptionParser(ReturnNullOnKeyNotFound.class) @ResponseParser(ParseObjectMetadataFromHeaders.class) - ListenableFuture headObject(@HostPrefixParam String bucketName, + ListenableFuture headObject( + @HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName, @PathParam("key") String key); /** @@ -135,7 +139,8 @@ public interface S3AsyncClient { @HEAD @Path("{key}") @ExceptionParser(ReturnFalseOnKeyNotFound.class) - ListenableFuture objectExists(@HostPrefixParam String bucketName, + ListenableFuture objectExists( + @HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName, @PathParam("key") String key); /** @@ -144,7 +149,8 @@ public interface S3AsyncClient { @DELETE @Path("{key}") @ExceptionParser(ReturnVoidOnNotFoundOr404.class) - ListenableFuture deleteObject(@HostPrefixParam String bucketName, + ListenableFuture deleteObject( + @HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName, @PathParam("key") String key); /** @@ -154,7 +160,7 @@ public interface S3AsyncClient { @Path("{key}") @ResponseParser(ParseETagHeader.class) ListenableFuture putObject( - @HostPrefixParam String bucketName, + @HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName, @PathParam("key") @ParamParser(ObjectKey.class) @BinderParam(BindS3ObjectToPayload.class) S3Object object, PutObjectOptions... options); @@ -167,7 +173,8 @@ public interface S3AsyncClient { ListenableFuture putBucketInRegion( // TODO endpoint based on region @BinderParam(BindRegionToXmlPayload.class) @Nullable String region, - @HostPrefixParam String bucketName, PutBucketOptions... options); + @HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName, + PutBucketOptions... options); /** * @see S3Client#deleteBucketIfEmpty @@ -175,7 +182,8 @@ public interface S3AsyncClient { @DELETE @Path("/") @ExceptionParser(ReturnTrueOn404OrNotFoundFalseIfNotEmpty.class) - ListenableFuture deleteBucketIfEmpty(@HostPrefixParam String bucketName); + ListenableFuture deleteBucketIfEmpty( + @HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName); /** * @see S3Client#bucketExists @@ -184,7 +192,8 @@ public interface S3AsyncClient { @Path("/") @QueryParams(keys = "max-keys", values = "0") @ExceptionParser(ReturnFalseOnContainerNotFound.class) - ListenableFuture bucketExists(@HostPrefixParam String bucketName); + ListenableFuture bucketExists( + @HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName); /** * @see S3Client#getBucketLocation @@ -193,7 +202,8 @@ public interface S3AsyncClient { @QueryParams(keys = "location") @Path("/") @XMLResponseParser(LocationConstraintHandler.class) - ListenableFuture getBucketLocation(@HostPrefixParam String bucketName); + ListenableFuture getBucketLocation( + @HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName); /** * @see S3Client#getBucketPayer @@ -202,7 +212,8 @@ public interface S3AsyncClient { @QueryParams(keys = "requestPayment") @Path("/") @XMLResponseParser(PayerHandler.class) - ListenableFuture getBucketPayer(@HostPrefixParam String bucketName); + ListenableFuture getBucketPayer( + @HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName); /** * @see S3Client#setBucketPayer @@ -210,7 +221,8 @@ public interface S3AsyncClient { @PUT @QueryParams(keys = "requestPayment") @Path("/") - ListenableFuture setBucketPayer(@HostPrefixParam String bucketName, + ListenableFuture setBucketPayer( + @HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName, @BinderParam(BindPayerToXmlPayload.class) Payer payer); /** @@ -219,7 +231,8 @@ public interface S3AsyncClient { @GET @Path("/") @XMLResponseParser(ListBucketHandler.class) - ListenableFuture listBucket(@HostPrefixParam String bucketName, + ListenableFuture listBucket( + @HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName, ListBucketOptions... options); /** @@ -237,9 +250,10 @@ public interface S3AsyncClient { @Path("{destinationObject}") @Headers(keys = "x-amz-copy-source", values = "/{sourceBucket}/{sourceObject}") @XMLResponseParser(CopyObjectHandler.class) - ListenableFuture copyObject(@PathParam("sourceBucket") String sourceBucket, + ListenableFuture copyObject( + @PathParam("sourceBucket") String sourceBucket, @PathParam("sourceObject") String sourceObject, - @HostPrefixParam String destinationBucket, + @HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String destinationBucket, @PathParam("destinationObject") String destinationObject, CopyObjectOptions... options); /** @@ -250,7 +264,8 @@ public interface S3AsyncClient { @XMLResponseParser(AccessControlListHandler.class) @ExceptionParser(ThrowContainerNotFoundOn404.class) @Path("/") - ListenableFuture getBucketACL(@HostPrefixParam String bucketName); + ListenableFuture getBucketACL( + @HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName); /** * @see S3Client#putBucketACL @@ -258,7 +273,8 @@ public interface S3AsyncClient { @PUT @Path("/") @QueryParams(keys = "acl") - ListenableFuture putBucketACL(@HostPrefixParam String bucketName, + ListenableFuture putBucketACL( + @HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName, @BinderParam(BindACLToXMLPayload.class) AccessControlList acl); /** @@ -269,7 +285,8 @@ public interface S3AsyncClient { @Path("{key}") @XMLResponseParser(AccessControlListHandler.class) @ExceptionParser(ThrowKeyNotFoundOn404.class) - ListenableFuture getObjectACL(@HostPrefixParam String bucketName, + ListenableFuture getObjectACL( + @HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName, @PathParam("key") String key); /** @@ -278,7 +295,8 @@ public interface S3AsyncClient { @PUT @QueryParams(keys = "acl") @Path("{key}") - ListenableFuture putObjectACL(@HostPrefixParam String bucketName, + ListenableFuture putObjectACL( + @HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName, @PathParam("key") String key, @BinderParam(BindACLToXMLPayload.class) AccessControlList acl); @@ -290,7 +308,8 @@ public interface S3AsyncClient { @XMLResponseParser(BucketLoggingHandler.class) @ExceptionParser(ThrowContainerNotFoundOn404.class) @Path("/") - ListenableFuture getBucketLogging(@HostPrefixParam String bucketName); + ListenableFuture getBucketLogging( + @HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName); /** * @see S3Client#enableBucketLogging @@ -298,7 +317,8 @@ public interface S3AsyncClient { @PUT @Path("/") @QueryParams(keys = "logging") - ListenableFuture enableBucketLogging(@HostPrefixParam String bucketName, + ListenableFuture enableBucketLogging( + @HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName, @BinderParam(BindBucketLoggingToXmlPayload.class) BucketLogging logging); /** @@ -308,6 +328,6 @@ public interface S3AsyncClient { @Path("/") @QueryParams(keys = "logging") ListenableFuture disableBucketLogging( - @BinderParam(BindNoBucketLoggingToXmlPayload.class) @HostPrefixParam String bucketName); + @BinderParam(BindNoBucketLoggingToXmlPayload.class) @HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName); } diff --git a/aws/core/src/test/java/org/jclouds/aws/s3/S3AsyncClientTest.java b/aws/core/src/test/java/org/jclouds/aws/s3/S3AsyncClientTest.java index 78f16f7e76..dcb05a18a7 100644 --- a/aws/core/src/test/java/org/jclouds/aws/s3/S3AsyncClientTest.java +++ b/aws/core/src/test/java/org/jclouds/aws/s3/S3AsyncClientTest.java @@ -199,18 +199,29 @@ public class S3AsyncClientTest extends RestClientTest { checkFilters(httpMethod); } + @Test(expectedExceptions = IllegalArgumentException.class) + public void testCopyObjectInvalidName() throws ArrayIndexOutOfBoundsException, + SecurityException, IllegalArgumentException, NoSuchMethodException, IOException { + Method method = S3AsyncClient.class + .getMethod("copyObject", String.class, String.class, String.class, String.class, + Array.newInstance(CopyObjectOptions.class, 0).getClass()); + processor.createRequest(method, "sourceBucket", "sourceObject", "destinationBucket", + "destinationObject"); + + } + public void testCopyObject() throws ArrayIndexOutOfBoundsException, SecurityException, IllegalArgumentException, NoSuchMethodException, IOException { Method method = S3AsyncClient.class .getMethod("copyObject", String.class, String.class, String.class, String.class, Array.newInstance(CopyObjectOptions.class, 0).getClass()); GeneratedHttpRequest httpMethod = processor.createRequest(method, - "sourceBucket", "sourceObject", "destinationBucket", "destinationObject"); + "sourceBucket", "sourceObject", "destinationbucket", "destinationObject"); assertRequestLineEquals(httpMethod, - "PUT http://destinationBucket.stub:8080/destinationObject HTTP/1.1"); + "PUT http://destinationbucket.stub:8080/destinationObject HTTP/1.1"); assertHeadersEqual(httpMethod, - "Content-Length: 0\nHost: destinationBucket.stub\nx-amz-copy-source: /sourceBucket/sourceObject\n"); + "Content-Length: 0\nHost: destinationbucket.stub\nx-amz-copy-source: /sourceBucket/sourceObject\n"); assertPayloadEquals(httpMethod, null); assertResponseParserClassEquals(method, httpMethod, ParseSax.class); diff --git a/core/src/main/java/org/jclouds/predicates/validators/DnsNameValidator.java b/core/src/main/java/org/jclouds/predicates/validators/DnsNameValidator.java index f0873e3cec..64d96d4647 100644 --- a/core/src/main/java/org/jclouds/predicates/validators/DnsNameValidator.java +++ b/core/src/main/java/org/jclouds/predicates/validators/DnsNameValidator.java @@ -65,11 +65,15 @@ public class DnsNameValidator extends Validator { * digits 0 through 9". From Azure: Every Dash (-) Must Be Immediately Preceded and Followed * by a Letter or Number. */ - CharMatcher lettersNumbersOrDashes = inRange('a', 'z').or(inRange('0', '9').or(is('-'))); - if (!lettersNumbersOrDashes.matchesAllOf(name)) + CharMatcher range = getAcceptableRange(); + if (!range.matchesAllOf(name)) throw exception(name, "Should have lowercase ASCII letters, " + "numbers, or dashes"); } + protected CharMatcher getAcceptableRange() { + return inRange('a', 'z').or(inRange('0', '9').or(is('-'))); + } + protected IllegalArgumentException exception(String vAppName, String reason) { return new IllegalArgumentException(String.format( "Object '%s' doesn't match dns naming constraints. " + "Reason: %s.", vAppName,