Issue 22: request payment

git-svn-id: http://jclouds.googlecode.com/svn/trunk@2544 3d8758e0-26b5-11de-8745-db77d3ebf521
This commit is contained in:
adrian.f.cole 2009-12-30 20:04:14 +00:00
parent dff7756acb
commit bfd9fa8d53
9 changed files with 396 additions and 0 deletions

View File

@ -37,11 +37,13 @@ import javax.ws.rs.Path;
import javax.ws.rs.PathParam; import javax.ws.rs.PathParam;
import org.jclouds.aws.s3.binders.BindACLToXMLPayload; import org.jclouds.aws.s3.binders.BindACLToXMLPayload;
import org.jclouds.aws.s3.binders.BindPayerToXmlPayload;
import org.jclouds.aws.s3.binders.BindS3ObjectToPayload; import org.jclouds.aws.s3.binders.BindS3ObjectToPayload;
import org.jclouds.aws.s3.domain.AccessControlList; import org.jclouds.aws.s3.domain.AccessControlList;
import org.jclouds.aws.s3.domain.BucketMetadata; import org.jclouds.aws.s3.domain.BucketMetadata;
import org.jclouds.aws.s3.domain.ListBucketResponse; import org.jclouds.aws.s3.domain.ListBucketResponse;
import org.jclouds.aws.s3.domain.ObjectMetadata; import org.jclouds.aws.s3.domain.ObjectMetadata;
import org.jclouds.aws.s3.domain.Payer;
import org.jclouds.aws.s3.domain.S3Object; import org.jclouds.aws.s3.domain.S3Object;
import org.jclouds.aws.s3.domain.BucketMetadata.LocationConstraint; import org.jclouds.aws.s3.domain.BucketMetadata.LocationConstraint;
import org.jclouds.aws.s3.filters.RequestAuthorizeSignature; import org.jclouds.aws.s3.filters.RequestAuthorizeSignature;
@ -59,6 +61,7 @@ import org.jclouds.aws.s3.xml.CopyObjectHandler;
import org.jclouds.aws.s3.xml.ListAllMyBucketsHandler; import org.jclouds.aws.s3.xml.ListAllMyBucketsHandler;
import org.jclouds.aws.s3.xml.ListBucketHandler; import org.jclouds.aws.s3.xml.ListBucketHandler;
import org.jclouds.aws.s3.xml.LocationConstraintHandler; import org.jclouds.aws.s3.xml.LocationConstraintHandler;
import org.jclouds.aws.s3.xml.PayerHandler;
import org.jclouds.blobstore.attr.BlobScope; import org.jclouds.blobstore.attr.BlobScope;
import org.jclouds.blobstore.attr.ConsistencyModel; import org.jclouds.blobstore.attr.ConsistencyModel;
import org.jclouds.blobstore.attr.ConsistencyModels; import org.jclouds.blobstore.attr.ConsistencyModels;
@ -179,6 +182,24 @@ public interface S3AsyncClient {
@XMLResponseParser(LocationConstraintHandler.class) @XMLResponseParser(LocationConstraintHandler.class)
Future<LocationConstraint> getBucketLocation(@HostPrefixParam String bucketName); Future<LocationConstraint> getBucketLocation(@HostPrefixParam String bucketName);
/**
* @see S3Client#getBucketPayer
*/
@GET
@QueryParams(keys = "requestPayment")
@Path("/")
@XMLResponseParser(PayerHandler.class)
Future<Payer> getBucketPayer(@HostPrefixParam String bucketName);
/**
* @see S3Client#setBucketPayer
*/
@PUT
@QueryParams(keys = "requestPayment")
@Path("/")
Future<Void> setBucketPayer(@HostPrefixParam String bucketName,
@BinderParam(BindPayerToXmlPayload.class) Payer payer);
/** /**
* @see S3Client#listBucket * @see S3Client#listBucket
*/ */

View File

@ -32,6 +32,7 @@ import org.jclouds.aws.s3.domain.AccessControlList;
import org.jclouds.aws.s3.domain.BucketMetadata; import org.jclouds.aws.s3.domain.BucketMetadata;
import org.jclouds.aws.s3.domain.ListBucketResponse; import org.jclouds.aws.s3.domain.ListBucketResponse;
import org.jclouds.aws.s3.domain.ObjectMetadata; import org.jclouds.aws.s3.domain.ObjectMetadata;
import org.jclouds.aws.s3.domain.Payer;
import org.jclouds.aws.s3.domain.S3Object; import org.jclouds.aws.s3.domain.S3Object;
import org.jclouds.aws.s3.domain.BucketMetadata.LocationConstraint; import org.jclouds.aws.s3.domain.BucketMetadata.LocationConstraint;
import org.jclouds.aws.s3.options.CopyObjectOptions; import org.jclouds.aws.s3.options.CopyObjectOptions;
@ -348,4 +349,43 @@ public interface S3Client {
*/ */
LocationConstraint getBucketLocation(String bucketName); LocationConstraint getBucketLocation(String bucketName);
/**
* A GET request operation on a requestPayment resource returns the request payment configuration
* of a bucket.
* <p/>
* Only the bucket owner has permissions to get this value.
*
* @param bucketName
* the bucket you wish to know the payer status
*
* @return {@link Payer.REQUESTER} for a Requester Pays bucket, and {@link Payer.BUCKET_OWNER},
* for a normal bucket.
*
* @see <a href=
* "http://docs.amazonwebservices.com/AmazonS3/latest/index.html?RESTrequestPaymentGET.html"
* />
*/
Payer getBucketPayer(String bucketName);
/**
* The PUT request operation with a requestPayment URI configures an existing bucket to be
* Requester Pays or not. To make a bucket a Requester Pays bucket, make the Payer value
* Requester. Otherwise, make the value BucketOwner.
* <p/>
* Only a bucket owner is allowed to configure a bucket. As a result any requests for this
* resource should be signed with the bucket owner's credentials. Anonymous requests are never
* allowed to create Requester Pays buckets.
*
* @param bucketName
* the bucket you wish to know the payer status
*
* @param payer
* {@link Payer.REQUESTER} for a Requester Pays bucket, and {@link Payer.BUCKET_OWNER},
* for a normal bucket.
*
* @see <a href=
* "http://docs.amazonwebservices.com/AmazonS3/latest/index.html?RESTrequestPaymentPUT.html"
* />
*/
void setBucketPayer(String bucketName, Payer payer);
} }

View File

@ -0,0 +1,58 @@
/**
*
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.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.binders;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Collections;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import org.jclouds.aws.s3.domain.Payer;
import org.jclouds.http.HttpRequest;
import org.jclouds.rest.Binder;
/**
*
* @author Adrian Cole
*
*/
public class BindPayerToXmlPayload implements Binder {
public void bindToRequest(HttpRequest request, Object toBind) {
checkArgument(checkNotNull(toBind, "toBind") instanceof Payer,
"this binder is only valid for Payer!");
String text = String
.format(
"<RequestPaymentConfiguration xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\"><Payer>%s</Payer></RequestPaymentConfiguration>",
((Payer) toBind).value());
request.setPayload(text);
request.getHeaders().replaceValues(HttpHeaders.CONTENT_LENGTH,
Collections.singletonList(text.getBytes().length + ""));
request.getHeaders().replaceValues(HttpHeaders.CONTENT_TYPE,
Collections.singletonList(MediaType.TEXT_XML));
}
}

View File

@ -0,0 +1,54 @@
package org.jclouds.aws.s3.domain;
import com.google.common.base.CaseFormat;
/**
* Specifies who pays for the download and request fees.
* <p/>
* In general, bucket owners pay for all Amazon S3 storage and data transfer costs associated with
* their bucket. A bucket owner, however, can configure a bucket to be a Requester Pays bucket. With
* Requester Pays buckets, the requester instead of the bucket owner pays the cost of the request
* and the data download from the bucket. The bucket owner always pays the cost of storing data.
* <p/>
* Typically, you configure buckets to be Requester Pays when you want to share data but not incur
* charges associated with others accessing the data. You might, for example, use Requester Pays
* buckets when making available large data sets, such as zip code directories, reference data,
* geospatial information, or web crawling data.
* <h3>Important</h3> If you enable Requester Pays on a bucket, anonymous access to that bucket is
* not allowed.
* <p/>
* You must authenticate all requests involving Requester Pays buckets. The request authentication
* enables Amazon S3 to identify and charge the requester for their use of the Requester Pays
* bucket.
* <p/>
* After you configure a bucket to be a Requester Pays bucket, requesters must include
* x-amz-request-payer in their requests either in the header, for POST and GET requests, or as a
* parameter in a REST request to show that they understand that they will be charged for the
* request and the data download.
* <p/>
* Requester Pays buckets do not support the following.
* <ul>
* <li>Anonymous requests</li>
* <li>BitTorrent</li>
* <li>SOAP requests</li>
* </ul>
*
* You cannot use a Requester Pays bucket as the target bucket for end user logging, or vice versa.
* However, you can turn on end user logging on a Requester Pays bucket where the target bucket is a
* non Requester Pays bucket.
*
* @author Adrian Cole
* @see <a href=
* "http://docs.amazonwebservices.com/AmazonS3/latest/index.html?RESTrequestPaymentGET.html" />
*/
public enum Payer {
REQUESTER, BUCKET_OWNER;
public String value() {
return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, name());
}
public static Payer fromValue(String payer) {
return valueOf(CaseFormat.UPPER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, payer));
}
}

View File

@ -0,0 +1,52 @@
/**
*
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.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.xml;
import org.jclouds.aws.s3.domain.Payer;
import org.jclouds.http.functions.ParseSax;
/**
* Parses the response from Amazon S3 GET Request Payment
* <p/>
* RequestPaymentConfiguration is the document we expect to parse.
*
* @see <a href= "http://docs.amazonwebservices.com/AmazonS3/latest/RESTrequestPaymentGET.html" />
* @author Adrian Cole
*/
public class PayerHandler extends ParseSax.HandlerWithResult<Payer> {
private StringBuilder currentText = new StringBuilder();
private Payer constraint;
public Payer getResult() {
return constraint;
}
public void endElement(String uri, String name, String qName) {
constraint = Payer.fromValue(currentText.toString().trim());
}
public void characters(char ch[], int start, int length) {
currentText.append(ch, start, length);
}
}

View File

@ -34,6 +34,7 @@ import org.jclouds.aws.s3.blobstore.functions.BlobToObject;
import org.jclouds.aws.s3.config.S3ObjectModule; import org.jclouds.aws.s3.config.S3ObjectModule;
import org.jclouds.aws.s3.domain.AccessControlList; import org.jclouds.aws.s3.domain.AccessControlList;
import org.jclouds.aws.s3.domain.CannedAccessPolicy; import org.jclouds.aws.s3.domain.CannedAccessPolicy;
import org.jclouds.aws.s3.domain.Payer;
import org.jclouds.aws.s3.domain.S3Object; import org.jclouds.aws.s3.domain.S3Object;
import org.jclouds.aws.s3.filters.RequestAuthorizeSignature; import org.jclouds.aws.s3.filters.RequestAuthorizeSignature;
import org.jclouds.aws.s3.functions.ParseObjectFromHeadersAndHttpContent; import org.jclouds.aws.s3.functions.ParseObjectFromHeadersAndHttpContent;
@ -50,6 +51,7 @@ import org.jclouds.aws.s3.xml.CopyObjectHandler;
import org.jclouds.aws.s3.xml.ListAllMyBucketsHandler; import org.jclouds.aws.s3.xml.ListAllMyBucketsHandler;
import org.jclouds.aws.s3.xml.ListBucketHandler; import org.jclouds.aws.s3.xml.ListBucketHandler;
import org.jclouds.aws.s3.xml.LocationConstraintHandler; import org.jclouds.aws.s3.xml.LocationConstraintHandler;
import org.jclouds.aws.s3.xml.PayerHandler;
import org.jclouds.blobstore.binders.BindBlobToMultipartFormTest; import org.jclouds.blobstore.binders.BindBlobToMultipartFormTest;
import org.jclouds.blobstore.config.BlobStoreObjectModule; import org.jclouds.blobstore.config.BlobStoreObjectModule;
import org.jclouds.blobstore.functions.ReturnVoidOnNotFoundOr404; import org.jclouds.blobstore.functions.ReturnVoidOnNotFoundOr404;
@ -100,6 +102,61 @@ public class S3AsyncClientTest extends RestClientTest<S3AsyncClient> {
checkFilters(httpMethod); checkFilters(httpMethod);
} }
public void testGetBucketPayer() throws SecurityException, NoSuchMethodException, IOException {
Method method = S3AsyncClient.class.getMethod("getBucketPayer", String.class);
GeneratedHttpRequest<S3AsyncClient> httpMethod = processor.createRequest(method, "bucket");
assertRequestLineEquals(httpMethod, "GET http://bucket.stub:8080/?requestPayment HTTP/1.1");
assertHeadersEqual(httpMethod, "Host: bucket.stub\n");
assertPayloadEquals(httpMethod, null);
assertResponseParserClassEquals(method, httpMethod, ParseSax.class);
assertSaxResponseParserClassEquals(method, PayerHandler.class);
assertExceptionParserClassEquals(method, null);
checkFilters(httpMethod);
}
public void testSetBucketPayerOwner() throws SecurityException, NoSuchMethodException,
IOException {
Method method = S3AsyncClient.class.getMethod("setBucketPayer", String.class, Payer.class);
GeneratedHttpRequest<S3AsyncClient> httpMethod = processor.createRequest(method, "bucket",
Payer.BUCKET_OWNER);
assertRequestLineEquals(httpMethod, "PUT http://bucket.stub:8080/?requestPayment HTTP/1.1");
assertHeadersEqual(httpMethod,
"Content-Length: 133\nContent-Type: text/xml\nHost: bucket.stub\n");
assertPayloadEquals(
httpMethod,
"<RequestPaymentConfiguration xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\"><Payer>BucketOwner</Payer></RequestPaymentConfiguration>");
assertResponseParserClassEquals(method, httpMethod, ReturnVoidIf2xx.class);
assertSaxResponseParserClassEquals(method, null);
assertExceptionParserClassEquals(method, null);
checkFilters(httpMethod);
}
public void testSetBucketPayerRequester() throws SecurityException, NoSuchMethodException,
IOException {
Method method = S3AsyncClient.class.getMethod("setBucketPayer", String.class, Payer.class);
GeneratedHttpRequest<S3AsyncClient> httpMethod = processor.createRequest(method, "bucket",
Payer.REQUESTER);
assertRequestLineEquals(httpMethod, "PUT http://bucket.stub:8080/?requestPayment HTTP/1.1");
assertHeadersEqual(httpMethod,
"Content-Length: 131\nContent-Type: text/xml\nHost: bucket.stub\n");
assertPayloadEquals(
httpMethod,
"<RequestPaymentConfiguration xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\"><Payer>Requester</Payer></RequestPaymentConfiguration>");
assertResponseParserClassEquals(method, httpMethod, ReturnVoidIf2xx.class);
assertSaxResponseParserClassEquals(method, null);
assertExceptionParserClassEquals(method, null);
checkFilters(httpMethod);
}
public void testListBucket() throws SecurityException, NoSuchMethodException, IOException { public void testListBucket() throws SecurityException, NoSuchMethodException, IOException {
Method method = S3AsyncClient.class.getMethod("listBucket", String.class, Array.newInstance( Method method = S3AsyncClient.class.getMethod("listBucket", String.class, Array.newInstance(
ListBucketOptions.class, 0).getClass()); ListBucketOptions.class, 0).getClass());

View File

@ -47,6 +47,7 @@ import org.jclouds.aws.s3.domain.BucketMetadata;
import org.jclouds.aws.s3.domain.CannedAccessPolicy; import org.jclouds.aws.s3.domain.CannedAccessPolicy;
import org.jclouds.aws.s3.domain.ListBucketResponse; import org.jclouds.aws.s3.domain.ListBucketResponse;
import org.jclouds.aws.s3.domain.ObjectMetadata; import org.jclouds.aws.s3.domain.ObjectMetadata;
import org.jclouds.aws.s3.domain.Payer;
import org.jclouds.aws.s3.domain.S3Object; import org.jclouds.aws.s3.domain.S3Object;
import org.jclouds.aws.s3.domain.AccessControlList.CanonicalUserGrantee; import org.jclouds.aws.s3.domain.AccessControlList.CanonicalUserGrantee;
import org.jclouds.aws.s3.domain.AccessControlList.EmailAddressGrantee; import org.jclouds.aws.s3.domain.AccessControlList.EmailAddressGrantee;
@ -344,4 +345,22 @@ public class StubS3AsyncClient implements S3AsyncClient {
}; };
} }
@Override
public Future<Payer> getBucketPayer(String bucketName) {
return new FutureBase<Payer>() {
public Payer get() throws InterruptedException, ExecutionException {
return Payer.BUCKET_OWNER;
}
};
}
@Override
public Future<Void> setBucketPayer(String bucketName, Payer payer) {
return new FutureBase<Void>() {
public Void get() throws InterruptedException, ExecutionException {
return null;
}
};
}
} }

View File

@ -46,6 +46,7 @@ import org.jclouds.aws.s3.domain.AccessControlList;
import org.jclouds.aws.s3.domain.BucketMetadata; import org.jclouds.aws.s3.domain.BucketMetadata;
import org.jclouds.aws.s3.domain.CannedAccessPolicy; import org.jclouds.aws.s3.domain.CannedAccessPolicy;
import org.jclouds.aws.s3.domain.ListBucketResponse; import org.jclouds.aws.s3.domain.ListBucketResponse;
import org.jclouds.aws.s3.domain.Payer;
import org.jclouds.aws.s3.domain.S3Object; import org.jclouds.aws.s3.domain.S3Object;
import org.jclouds.aws.s3.domain.AccessControlList.CanonicalUserGrantee; import org.jclouds.aws.s3.domain.AccessControlList.CanonicalUserGrantee;
import org.jclouds.aws.s3.domain.AccessControlList.EmailAddressGrantee; import org.jclouds.aws.s3.domain.AccessControlList.EmailAddressGrantee;
@ -179,7 +180,37 @@ public class BucketsLiveTest extends BaseBlobStoreIntegrationTest<S3AsyncClient,
} finally { } finally {
returnContainer(bucketName); returnContainer(bucketName);
} }
}
public void testBucketPayer() throws Exception {
final String bucketName = getContainerName();
try {
assertEquals(Payer.BUCKET_OWNER, context.getApi().getBucketPayer(bucketName));
context.getApi().setBucketPayer(bucketName, Payer.REQUESTER);
assertConsistencyAware(new Runnable() {
public void run() {
try {
assertEquals(Payer.REQUESTER, context.getApi().getBucketPayer(bucketName));
} catch (Exception e) {
Utils.<RuntimeException> rethrowIfRuntimeOrSameType(e);
}
}
});
context.getApi().setBucketPayer(bucketName, Payer.BUCKET_OWNER);
assertConsistencyAware(new Runnable() {
public void run() {
try {
assertEquals(Payer.BUCKET_OWNER, context.getApi().getBucketPayer(bucketName));
} catch (Exception e) {
Utils.<RuntimeException> rethrowIfRuntimeOrSameType(e);
}
}
});
} finally {
destroyContainer(bucketName);
}
} }
/** /**

View File

@ -0,0 +1,64 @@
/**
*
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.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.xml;
import static org.testng.Assert.assertEquals;
import org.jclouds.aws.s3.domain.Payer;
import org.jclouds.http.HttpException;
import org.jclouds.http.functions.BaseHandlerTest;
import org.jclouds.http.functions.ParseSax;
import org.jclouds.util.Utils;
import org.testng.annotations.Test;
@Test(groups = "unit", testName = "s3.PayerHandlerTest")
public class PayerHandlerTest extends BaseHandlerTest {
ParseSax<Payer> createParser() {
ParseSax<Payer> parser = (ParseSax<Payer>) factory.create(injector
.getInstance(PayerHandler.class));
return parser;
}
@Test
public void testPayerRequester() throws HttpException {
Payer payer = createParser()
.parse(
Utils
.toInputStream("<RequestPaymentConfiguration xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\"><Payer>Requester</Payer></RequestPaymentConfiguration>"));
assertEquals(payer, Payer.REQUESTER);
}
@Test
public void testPayerBucketOwner() throws HttpException {
Payer payer = createParser()
.parse(
Utils
.toInputStream("<RequestPaymentConfiguration xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\"><Payer>BucketOwner</Payer></RequestPaymentConfiguration>"));
assertEquals(payer, Payer.BUCKET_OWNER);
}
}