Issue 271 updated to support google storage

This commit is contained in:
Adrian Cole 2010-06-15 17:04:01 -04:00
parent cba3f660fb
commit 518b453c6d
15 changed files with 323 additions and 170 deletions

View File

@ -27,8 +27,8 @@ import com.google.common.collect.ImmutableSet;
* Regions used for all aws commands. * Regions used for all aws commands.
* *
* @author Adrian Cole * @author Adrian Cole
* @see <a href= * @see <a href="http://docs.amazonwebservices.com/AmazonS3/latest/dev/index.html?LocationSelection.html"
* "http://docs.amazonwebservices.com/AmazonS3/latest/dev/index.html?LocationSelection.html" /> * />
* *
*/ */
public class Region { public class Region {
@ -37,8 +37,9 @@ public class Region {
* <p/> * <p/>
* <h3>S3</h3> * <h3>S3</h3>
* <p/> * <p/>
* In Amazon S3, the EU (Ireland) Region provides read-after-write consistency for PUTS of new * In Amazon S3, the EU (Ireland) Region provides read-after-write
* objects in your Amazon S3 bucket and eventual consistency for overwrite PUTS and DELETES. * consistency for PUTS of new objects in your Amazon S3 bucket and eventual
* consistency for overwrite PUTS and DELETES.
*/ */
public static final String EU_WEST_1 = "eu-west-1"; public static final String EU_WEST_1 = "eu-west-1";
@ -48,11 +49,13 @@ public class Region {
* <p/> * <p/>
* <h3>S3</h3> * <h3>S3</h3>
* <p/> * <p/>
* This is the default Region. All requests sent to s3.amazonaws.com go to this Region unless you * This is the default Region. All requests sent to s3.amazonaws.com go to
* specify a LocationConstraint on a bucket. The US Standard Region automatically places your * this Region unless you specify a LocationConstraint on a bucket. The US
* data in either Amazon's east or west coast data centers depending on what will provide you * Standard Region automatically places your data in either Amazon's east or
* with the lowest latency. To use this region, do not set the LocationConstraint bucket * west coast data centers depending on what will provide you with the lowest
* parameter. The US Standard Region provides eventual consistency for all requests. * latency. To use this region, do not set the LocationConstraint bucket
* parameter. The US Standard Region provides eventual consistency for all
* requests.
*/ */
public static final String US_STANDARD = "us-standard"; public static final String US_STANDARD = "us-standard";
@ -62,25 +65,27 @@ public class Region {
public static final String US_EAST_1 = "us-east-1"; public static final String US_EAST_1 = "us-east-1";
/** /**
* US-West (Northern California) <h3>S3</h3> Uses Amazon S3 servers in Northern California * US-West (Northern California) <h3>S3</h3> Uses Amazon S3 servers in
* Northern California
* <p/> * <p/>
* Optionally, use the endpoint s3-us-west-1.amazonaws.com on all requests to this bucket to * Optionally, use the endpoint s3-us-west-1.amazonaws.com on all requests to
* reduce the latency you might experience after the first hour of creating a bucket in this * this bucket to reduce the latency you might experience after the first
* Region. * hour of creating a bucket in this Region.
* <p/> * <p/>
* In Amazon S3, the US-West (Northern California) Region provides read-after-write consistency * In Amazon S3, the US-West (Northern California) Region provides
* for PUTS of new objects in your Amazon S3 bucket and eventual consistency for overwrite PUTS * read-after-write consistency for PUTS of new objects in your Amazon S3
* and DELETES. * bucket and eventual consistency for overwrite PUTS and DELETES.
*/ */
public static final String US_WEST_1 = "us-west-1"; public static final String US_WEST_1 = "us-west-1";
/** /**
* Region in Singapore, launched April 28, 2010. This region improves latency for Asia-based * Region in Singapore, launched April 28, 2010. This region improves latency
* users * for Asia-based users
*/ */
public static final String AP_SOUTHEAST_1 = "ap-southeast-1"; public static final String AP_SOUTHEAST_1 = "ap-southeast-1";
public static Set<String> ALL = ImmutableSet.of(EU_WEST_1, US_STANDARD, US_EAST_1, US_WEST_1, public static Set<String> ALL_S3 = ImmutableSet.of("EU", US_STANDARD,
AP_SOUTHEAST_1); US_EAST_1, US_WEST_1, AP_SOUTHEAST_1);
public static Set<String> ALL_SQS = ImmutableSet.of(EU_WEST_1, US_STANDARD,
US_EAST_1, US_WEST_1, AP_SOUTHEAST_1);
} }

View File

@ -41,7 +41,8 @@ import com.google.common.collect.Iterables;
* @author Adrian Cole * @author Adrian Cole
*/ */
@Singleton @Singleton
public class CreateSecurityGroupIfNeeded implements Function<RegionNameAndIngressRules, String> { public class CreateSecurityGroupIfNeeded implements
Function<RegionNameAndIngressRules, String> {
@Resource @Resource
@Named(ComputeServiceConstants.COMPUTE_LOGGER) @Named(ComputeServiceConstants.COMPUTE_LOGGER)
protected Logger logger = Logger.NULL; protected Logger logger = Logger.NULL;
@ -54,16 +55,20 @@ public class CreateSecurityGroupIfNeeded implements Function<RegionNameAndIngres
@Override @Override
public String apply(RegionNameAndIngressRules from) { public String apply(RegionNameAndIngressRules from) {
createSecurityGroupInRegion(from.getRegion(), from.getName(), from.getPorts()); createSecurityGroupInRegion(from.getRegion(), from.getName(), from
.getPorts());
return from.getName(); return from.getName();
} }
private void createSecurityGroupInRegion(String region, String name, int... ports) { private void createSecurityGroupInRegion(String region, String name,
int... ports) {
checkNotNull(region, "region"); checkNotNull(region, "region");
checkNotNull(name, "name"); checkNotNull(name, "name");
logger.debug(">> creating securityGroup region(%s) name(%s)", region, name); logger.debug(">> creating securityGroup region(%s) name(%s)", region,
name);
try { try {
ec2Client.getSecurityGroupServices().createSecurityGroupInRegion(region, name, name); ec2Client.getSecurityGroupServices().createSecurityGroupInRegion(
region, name, name);
logger.debug("<< created securityGroup(%s)", name); logger.debug("<< created securityGroup(%s)", name);
for (int port : ports) { for (int port : ports) {
createIngressRuleForTCPPort(region, name, port); createIngressRuleForTCPPort(region, name, port);
@ -72,7 +77,8 @@ public class CreateSecurityGroupIfNeeded implements Function<RegionNameAndIngres
authorizeGroupToItself(region, name); authorizeGroupToItself(region, name);
} }
} catch (AWSResponseException e) { } catch (AWSResponseException e) {
if (e.getError().getCode().equals("InvalidGroup.Duplicate")) { if (e.getError().getCode().equals("InvalidGroup.Duplicate")
|| e.getError().getMessage().endsWith("already exists")) {
logger.debug("<< reused securityGroup(%s)", name); logger.debug("<< reused securityGroup(%s)", name);
} else { } else {
throw e; throw e;
@ -81,20 +87,25 @@ public class CreateSecurityGroupIfNeeded implements Function<RegionNameAndIngres
} }
private void createIngressRuleForTCPPort(String region, String name, int port) { private void createIngressRuleForTCPPort(String region, String name, int port) {
logger.debug(">> authorizing securityGroup region(%s) name(%s) port(%s)", region, name, port); logger.debug(">> authorizing securityGroup region(%s) name(%s) port(%s)",
ec2Client.getSecurityGroupServices().authorizeSecurityGroupIngressInRegion(region, name, region, name, port);
IpProtocol.TCP, port, port, "0.0.0.0/0"); ec2Client.getSecurityGroupServices()
.authorizeSecurityGroupIngressInRegion(region, name,
IpProtocol.TCP, port, port, "0.0.0.0/0");
logger.debug("<< authorized securityGroup(%s)", name); logger.debug("<< authorized securityGroup(%s)", name);
} }
private void authorizeGroupToItself(String region, String name) { private void authorizeGroupToItself(String region, String name) {
logger.debug(">> authorizing securityGroup region(%s) name(%s) permission to itself", region, logger
name); .debug(
">> authorizing securityGroup region(%s) name(%s) permission to itself",
region, name);
String myOwnerId = Iterables.get( String myOwnerId = Iterables.get(
ec2Client.getSecurityGroupServices().describeSecurityGroupsInRegion(region), 0) ec2Client.getSecurityGroupServices()
.getOwnerId(); .describeSecurityGroupsInRegion(region), 0).getOwnerId();
ec2Client.getSecurityGroupServices().authorizeSecurityGroupIngressInRegion(region, name, ec2Client.getSecurityGroupServices()
new UserIdGroupPair(myOwnerId, name)); .authorizeSecurityGroupIngressInRegion(region, name,
new UserIdGroupPair(myOwnerId, name));
logger.debug("<< authorized securityGroup(%s)", name); logger.debug("<< authorized securityGroup(%s)", name);
} }

View File

@ -0,0 +1,48 @@
package org.jclouds.aws.s3;
import static org.jclouds.aws.s3.reference.S3Constants.PROPERTY_S3_AUTH_TAG;
import static org.jclouds.aws.s3.reference.S3Constants.PROPERTY_S3_DEFAULT_REGIONS;
import static org.jclouds.aws.s3.reference.S3Constants.PROPERTY_S3_ENDPOINT;
import static org.jclouds.aws.s3.reference.S3Constants.PROPERTY_S3_HEADER_TAG;
import static org.jclouds.aws.s3.reference.S3Constants.PROPERTY_S3_REGIONS;
import static org.jclouds.aws.s3.reference.S3Constants.PROPERTY_S3_SERVICE_EXPR;
import static org.jclouds.blobstore.reference.BlobStoreConstants.PROPERTY_USER_METADATA_PREFIX;
import java.util.Properties;
/**
* Builds properties used in Walrus Clients
*
* @author Adrian Cole
*/
public class GoogleStoragePropertiesBuilder extends S3PropertiesBuilder {
@Override
protected Properties defaultProperties() {
Properties properties = super.defaultProperties();
properties.setProperty(PROPERTY_USER_METADATA_PREFIX, "x-goog-meta-");
properties.setProperty(PROPERTY_S3_AUTH_TAG, "GOOG1");
properties.setProperty(PROPERTY_S3_HEADER_TAG, "goog");
properties.setProperty(PROPERTY_S3_SERVICE_EXPR,
"\\.commondatastorage\\.googleapis\\.com");
return properties;
}
@Override
protected Properties addEndpoints(Properties properties) {
properties.setProperty(PROPERTY_S3_REGIONS, "GoogleStorage");
properties.setProperty(PROPERTY_S3_DEFAULT_REGIONS, "GoogleStorage");
properties.setProperty(PROPERTY_S3_ENDPOINT,
"https://commondatastorage.googleapis.com");
properties.setProperty(PROPERTY_S3_ENDPOINT + ".GoogleStorage",
"https://commondatastorage.googleapis.com");
return properties;
}
public GoogleStoragePropertiesBuilder(Properties properties) {
super(properties);
}
public GoogleStoragePropertiesBuilder(String id, String secret) {
super(id, secret);
}
}

View File

@ -22,8 +22,12 @@ import static com.google.common.base.Preconditions.checkNotNull;
import static org.jclouds.Constants.PROPERTY_RELAX_HOSTNAME; import static org.jclouds.Constants.PROPERTY_RELAX_HOSTNAME;
import static org.jclouds.aws.reference.AWSConstants.PROPERTY_AWS_ACCESSKEYID; import static org.jclouds.aws.reference.AWSConstants.PROPERTY_AWS_ACCESSKEYID;
import static org.jclouds.aws.reference.AWSConstants.PROPERTY_AWS_SECRETACCESSKEY; import static org.jclouds.aws.reference.AWSConstants.PROPERTY_AWS_SECRETACCESSKEY;
import static org.jclouds.aws.s3.reference.S3Constants.PROPERTY_S3_AUTH_TAG;
import static org.jclouds.aws.s3.reference.S3Constants.PROPERTY_S3_DEFAULT_REGIONS;
import static org.jclouds.aws.s3.reference.S3Constants.PROPERTY_S3_ENDPOINT; import static org.jclouds.aws.s3.reference.S3Constants.PROPERTY_S3_ENDPOINT;
import static org.jclouds.aws.s3.reference.S3Constants.PROPERTY_S3_HEADER_TAG;
import static org.jclouds.aws.s3.reference.S3Constants.PROPERTY_S3_REGIONS; import static org.jclouds.aws.s3.reference.S3Constants.PROPERTY_S3_REGIONS;
import static org.jclouds.aws.s3.reference.S3Constants.PROPERTY_S3_SERVICE_EXPR;
import static org.jclouds.aws.s3.reference.S3Constants.PROPERTY_S3_SESSIONINTERVAL; import static org.jclouds.aws.s3.reference.S3Constants.PROPERTY_S3_SESSIONINTERVAL;
import static org.jclouds.blobstore.reference.BlobStoreConstants.DIRECTORY_SUFFIX_FOLDER; import static org.jclouds.blobstore.reference.BlobStoreConstants.DIRECTORY_SUFFIX_FOLDER;
import static org.jclouds.blobstore.reference.BlobStoreConstants.PROPERTY_BLOBSTORE_DIRECTORY_SUFFIX; import static org.jclouds.blobstore.reference.BlobStoreConstants.PROPERTY_BLOBSTORE_DIRECTORY_SUFFIX;
@ -47,26 +51,34 @@ public class S3PropertiesBuilder extends PropertiesBuilder {
@Override @Override
protected Properties defaultProperties() { protected Properties defaultProperties() {
Properties properties = super.defaultProperties(); Properties properties = super.defaultProperties();
properties.setProperty(PROPERTY_RELAX_HOSTNAME, "true");
properties.setProperty(PROPERTY_USER_METADATA_PREFIX, "x-amz-meta-"); properties.setProperty(PROPERTY_USER_METADATA_PREFIX, "x-amz-meta-");
addEndpointProperties(properties); properties.setProperty(PROPERTY_S3_AUTH_TAG, "AWS");
properties.setProperty(PROPERTY_S3_HEADER_TAG, "aws");
properties.setProperty(PROPERTY_S3_SERVICE_EXPR,
"\\.s3[^.]*\\.amazonaws\\.com");
properties.setProperty(PROPERTY_RELAX_HOSTNAME, "true");
addEndpoints(properties);
properties.setProperty(PROPERTY_S3_SESSIONINTERVAL, "60"); properties.setProperty(PROPERTY_S3_SESSIONINTERVAL, "60");
properties.setProperty(PROPERTY_BLOBSTORE_DIRECTORY_SUFFIX, properties.setProperty(PROPERTY_BLOBSTORE_DIRECTORY_SUFFIX,
DIRECTORY_SUFFIX_FOLDER); DIRECTORY_SUFFIX_FOLDER);
return properties; return properties;
} }
protected Properties addEndpointProperties(Properties properties) { protected Properties addEndpoints(Properties properties) {
properties.setProperty(PROPERTY_S3_REGIONS, Joiner.on(',').join( properties.setProperty(PROPERTY_S3_REGIONS, Joiner.on(',').join(
Region.US_STANDARD, Region.US_WEST_1, Region.EU_WEST_1, Region.US_STANDARD, Region.US_EAST_1, Region.US_WEST_1, "EU",
Region.AP_SOUTHEAST_1)); Region.AP_SOUTHEAST_1));
properties.setProperty(PROPERTY_S3_DEFAULT_REGIONS, Joiner.on(',').join(
Region.US_STANDARD, Region.US_EAST_1));
properties.setProperty(PROPERTY_S3_ENDPOINT, "https://s3.amazonaws.com"); properties.setProperty(PROPERTY_S3_ENDPOINT, "https://s3.amazonaws.com");
properties.setProperty(PROPERTY_S3_ENDPOINT + "." + Region.US_STANDARD, properties.setProperty(PROPERTY_S3_ENDPOINT + "." + Region.US_STANDARD,
"https://s3.amazonaws.com"); "https://s3.amazonaws.com");
properties.setProperty(PROPERTY_S3_ENDPOINT + "." + Region.US_EAST_1,
"https://s3.amazonaws.com");
properties.setProperty(PROPERTY_S3_ENDPOINT + "." + Region.US_WEST_1, properties.setProperty(PROPERTY_S3_ENDPOINT + "." + Region.US_WEST_1,
"https://s3-us-west-1.amazonaws.com"); "https://s3-us-west-1.amazonaws.com");
properties.setProperty(PROPERTY_S3_ENDPOINT + "." + Region.EU_WEST_1, properties.setProperty(PROPERTY_S3_ENDPOINT + "." + "EU",
"https://s3-eu-west-1.amazonaws.com"); "https://s3-eu-west-1.amazonaws.com");
properties.setProperty( properties.setProperty(
PROPERTY_S3_ENDPOINT + "." + Region.AP_SOUTHEAST_1, PROPERTY_S3_ENDPOINT + "." + Region.AP_SOUTHEAST_1,

View File

@ -1,5 +1,6 @@
package org.jclouds.aws.s3; package org.jclouds.aws.s3;
import static org.jclouds.aws.s3.reference.S3Constants.PROPERTY_S3_DEFAULT_REGIONS;
import static org.jclouds.aws.s3.reference.S3Constants.PROPERTY_S3_ENDPOINT; import static org.jclouds.aws.s3.reference.S3Constants.PROPERTY_S3_ENDPOINT;
import static org.jclouds.aws.s3.reference.S3Constants.PROPERTY_S3_REGIONS; import static org.jclouds.aws.s3.reference.S3Constants.PROPERTY_S3_REGIONS;
@ -12,8 +13,9 @@ import java.util.Properties;
*/ */
public class WalrusPropertiesBuilder extends S3PropertiesBuilder { public class WalrusPropertiesBuilder extends S3PropertiesBuilder {
@Override @Override
protected Properties addEndpointProperties(Properties properties) { protected Properties addEndpoints(Properties properties) {
properties.setProperty(PROPERTY_S3_REGIONS, "Walrus"); properties.setProperty(PROPERTY_S3_REGIONS, "Walrus");
properties.setProperty(PROPERTY_S3_DEFAULT_REGIONS, "Walrus");
properties.setProperty(PROPERTY_S3_ENDPOINT, properties.setProperty(PROPERTY_S3_ENDPOINT,
"http://173.205.188.130:8773/services/Walrus"); "http://173.205.188.130:8773/services/Walrus");
properties.setProperty(PROPERTY_S3_ENDPOINT + ".Walrus", properties.setProperty(PROPERTY_S3_ENDPOINT + ".Walrus",

View File

@ -43,8 +43,10 @@ import com.google.common.collect.Iterables;
* @author Adrian Cole * @author Adrian Cole
*/ */
@Singleton @Singleton
public class BucketToResourceMetadata implements Function<BucketMetadata, StorageMetadata> { public class BucketToResourceMetadata implements
Function<BucketMetadata, StorageMetadata> {
private final S3Client client; private final S3Client client;
private final Location onlyLocation;
private final Set<? extends Location> locations; private final Set<? extends Location> locations;
@Resource @Resource
@ -53,6 +55,8 @@ public class BucketToResourceMetadata implements Function<BucketMetadata, Storag
@Inject @Inject
BucketToResourceMetadata(S3Client client, Set<? extends Location> locations) { BucketToResourceMetadata(S3Client client, Set<? extends Location> locations) {
this.client = client; this.client = client;
this.onlyLocation = locations.size() == 1 ? Iterables.get(locations, 0)
: null;
this.locations = locations; this.locations = locations;
} }
@ -60,29 +64,37 @@ public class BucketToResourceMetadata implements Function<BucketMetadata, Storag
MutableStorageMetadata to = new MutableStorageMetadataImpl(); MutableStorageMetadata to = new MutableStorageMetadataImpl();
to.setName(from.getName()); to.setName(from.getName());
to.setType(StorageType.CONTAINER); to.setType(StorageType.CONTAINER);
to.setLocation(onlyLocation != null ? onlyLocation : getLocation(from));
return to;
}
private Location getLocation(BucketMetadata from) {
try { try {
final String region = client.getBucketLocation(from.getName()); final String region = client.getBucketLocation(from.getName());
if (region != null) { if (region != null) {
try { try {
to.setLocation(Iterables.find(locations, new Predicate<Location>() { return Iterables.find(locations, new Predicate<Location>() {
@Override @Override
public boolean apply(Location input) { public boolean apply(Location input) {
return input.getId().equals(region.toString()); return input.getId().equals(region.toString());
} }
})); });
} catch (NoSuchElementException e) { } catch (NoSuchElementException e) {
logger.error("could not get location for region %s in %s", region, locations); logger.error("could not get location for region %s in %s",
region, locations);
} }
} else { } else {
logger.error("could not get region for %s", from.getName()); logger.error("could not get region for %s", from.getName());
} }
} catch (ContainerNotFoundException e) { } catch (ContainerNotFoundException e) {
logger.error(e, logger
"could not get region for %s, as service suggests the bucket doesn't exist", from .error(
.getName()); e,
"could not get region for %s, as service suggests the bucket doesn't exist",
from.getName());
} }
return to; return null;
} }
} }

View File

@ -21,6 +21,7 @@ package org.jclouds.aws.s3.config;
import java.net.URI; import java.net.URI;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.inject.Named; import javax.inject.Named;
@ -47,9 +48,10 @@ import org.jclouds.rest.ConfiguresRestClient;
import org.jclouds.rest.RequestSigner; import org.jclouds.rest.RequestSigner;
import org.jclouds.rest.config.RestClientModule; import org.jclouds.rest.config.RestClientModule;
import com.google.common.base.Predicate;
import com.google.common.base.Splitter; import com.google.common.base.Splitter;
import com.google.common.base.Supplier; import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableBiMap; import com.google.common.collect.Iterables;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.inject.Injector; import com.google.inject.Injector;
import com.google.inject.Key; import com.google.inject.Key;
@ -137,9 +139,16 @@ public class S3RestClientModule extends
@Provides @Provides
@Singleton @Singleton
@S3 @S3
String getDefaultRegion(@S3 URI uri, @S3 Map<String, URI> map) { String getDefaultRegion(@S3 final URI uri, @S3 Map<String, URI> map) {
return ImmutableBiMap.<String, URI> builder().putAll(map).build() return Iterables.find(map.entrySet(),
.inverse().get(uri); new Predicate<Entry<String, URI>>() {
@Override
public boolean apply(Entry<String, URI> input) {
return input.getValue().equals(uri);
}
}).getKey();
} }
@Override @Override

View File

@ -52,17 +52,20 @@ import com.google.common.collect.ImmutableSet;
/** /**
* Signs the S3 request. * Signs the S3 request.
* *
* @see <a href= "http://docs.amazonwebservices.com/AmazonS3/latest/RESTAuthentication.html" /> * @see <a href=
* "http://docs.amazonwebservices.com/AmazonS3/latest/RESTAuthentication.html"
* />
* @author Adrian Cole * @author Adrian Cole
* *
*/ */
@Singleton @Singleton
public class RequestAuthorizeSignature implements HttpRequestFilter, RequestSigner { public class RequestAuthorizeSignature implements HttpRequestFilter,
RequestSigner {
private final String[] firstHeadersToSign = new String[] { "Content-MD5", private final String[] firstHeadersToSign = new String[] { "Content-MD5",
HttpHeaders.CONTENT_TYPE, HttpHeaders.DATE }; HttpHeaders.CONTENT_TYPE, HttpHeaders.DATE };
public static Set<String> SPECIAL_QUERIES = ImmutableSet.of("acl", "torrent", "logging", public static Set<String> SPECIAL_QUERIES = ImmutableSet.of("acl",
"location", "requestPayment"); "torrent", "logging", "location", "requestPayment");
private final SignatureWire signatureWire; private final SignatureWire signatureWire;
private final String accessKey; private final String accessKey;
private final String secretKey; private final String secretKey;
@ -73,11 +76,22 @@ public class RequestAuthorizeSignature implements HttpRequestFilter, RequestSign
@Named(Constants.LOGGER_SIGNATURE) @Named(Constants.LOGGER_SIGNATURE)
Logger signatureLog = Logger.NULL; Logger signatureLog = Logger.NULL;
private final String authTag;
private final String headerTag;
private final String srvExpr;
@Inject @Inject
public RequestAuthorizeSignature(SignatureWire signatureWire, public RequestAuthorizeSignature(SignatureWire signatureWire,
@Named(S3Constants.PROPERTY_AWS_ACCESSKEYID) String accessKey, @Named(S3Constants.PROPERTY_S3_AUTH_TAG) String authTag,
@Named(S3Constants.PROPERTY_AWS_SECRETACCESSKEY) String secretKey, @Named(S3Constants.PROPERTY_S3_SERVICE_EXPR) String srvExpr,
@TimeStamp Provider<String> timeStampProvider, EncryptionService encryptionService) { @Named(S3Constants.PROPERTY_S3_HEADER_TAG) String headerTag,
@Named(S3Constants.PROPERTY_AWS_ACCESSKEYID) String accessKey,
@Named(S3Constants.PROPERTY_AWS_SECRETACCESSKEY) String secretKey,
@TimeStamp Provider<String> timeStampProvider,
EncryptionService encryptionService) {
this.srvExpr = srvExpr;
this.headerTag = headerTag;
this.authTag = authTag;
this.signatureWire = signatureWire; this.signatureWire = signatureWire;
this.accessKey = accessKey; this.accessKey = accessKey;
this.secretKey = secretKey; this.secretKey = secretKey;
@ -107,18 +121,21 @@ public class RequestAuthorizeSignature implements HttpRequestFilter, RequestSign
} }
private void calculateAndReplaceAuthHeader(HttpRequest request, String toSign) private void calculateAndReplaceAuthHeader(HttpRequest request, String toSign)
throws HttpException { throws HttpException {
String signature = sign(toSign); String signature = sign(toSign);
if (signatureWire.enabled()) if (signatureWire.enabled())
signatureWire.input(Utils.toInputStream(signature)); signatureWire.input(Utils.toInputStream(signature));
request.getHeaders().replaceValues(HttpHeaders.AUTHORIZATION, request.getHeaders().replaceValues(
Collections.singletonList("AWS " + accessKey + ":" + signature)); HttpHeaders.AUTHORIZATION,
Collections.singletonList(authTag + " " + accessKey + ":"
+ signature));
} }
public String sign(String toSign) { public String sign(String toSign) {
String signature; String signature;
try { try {
signature = encryptionService.hmacSha1Base64(toSign, secretKey.getBytes()); signature = encryptionService.hmacSha1Base64(toSign, secretKey
.getBytes());
} catch (Exception e) { } catch (Exception e) {
throw new HttpException("error signing request", e); throw new HttpException("error signing request", e);
} }
@ -131,16 +148,17 @@ public class RequestAuthorizeSignature implements HttpRequestFilter, RequestSign
private void replaceDateHeader(HttpRequest request) { private void replaceDateHeader(HttpRequest request) {
request.getHeaders().replaceValues(HttpHeaders.DATE, request.getHeaders().replaceValues(HttpHeaders.DATE,
Collections.singletonList(timeStampProvider.get())); Collections.singletonList(timeStampProvider.get()));
} }
private void appendAmzHeaders(HttpRequest request, StringBuilder toSign) { private void appendAmzHeaders(HttpRequest request, StringBuilder toSign) {
Set<String> headers = new TreeSet<String>(request.getHeaders().keySet()); Set<String> headers = new TreeSet<String>(request.getHeaders().keySet());
for (String header : headers) { for (String header : headers) {
if (header.startsWith("x-amz-")) { if (header.startsWith("x-" + headerTag + "-")) {
toSign.append(header.toLowerCase()).append(":"); toSign.append(header.toLowerCase()).append(":");
for (String value : request.getHeaders().get(header)) { for (String value : request.getHeaders().get(header)) {
toSign.append(Utils.replaceAll(value, NEWLINE_PATTERN, "")).append(","); toSign.append(Utils.replaceAll(value, NEWLINE_PATTERN, ""))
.append(",");
} }
toSign.deleteCharAt(toSign.lastIndexOf(",")); toSign.deleteCharAt(toSign.lastIndexOf(","));
toSign.append("\n"); toSign.append("\n");
@ -150,7 +168,8 @@ public class RequestAuthorizeSignature implements HttpRequestFilter, RequestSign
private void appendHttpHeaders(HttpRequest request, StringBuilder toSign) { private void appendHttpHeaders(HttpRequest request, StringBuilder toSign) {
for (String header : firstHeadersToSign) for (String header : firstHeadersToSign)
toSign.append(valueOrEmpty(request.getHeaders().get(header))).append("\n"); toSign.append(valueOrEmpty(request.getHeaders().get(header))).append(
"\n");
} }
@VisibleForTesting @VisibleForTesting
@ -158,9 +177,9 @@ public class RequestAuthorizeSignature implements HttpRequestFilter, RequestSign
String hostHeader = request.getFirstHeaderOrNull(HttpHeaders.HOST); String hostHeader = request.getFirstHeaderOrNull(HttpHeaders.HOST);
if (hostHeader == null) if (hostHeader == null)
hostHeader = checkNotNull(request.getEndpoint().getHost(), hostHeader = checkNotNull(request.getEndpoint().getHost(),
"request.getEndPoint().getHost()"); "request.getEndPoint().getHost()");
if (hostHeader.endsWith(".amazonaws.com") && !hostHeader.equals("s3.amazonaws.com")) if (hostHeader.matches(".*" + srvExpr))
toSign.append("/").append(hostHeader.substring(0, hostHeader.lastIndexOf(".s3"))); toSign.append("/").append(hostHeader.replaceAll(srvExpr, ""));
} }
@VisibleForTesting @VisibleForTesting
@ -168,7 +187,8 @@ public class RequestAuthorizeSignature implements HttpRequestFilter, RequestSign
toSign.append(request.getEndpoint().getRawPath()); toSign.append(request.getEndpoint().getRawPath());
// ...however, there are a few exceptions that must be included in the signed URI. // ...however, there are a few exceptions that must be included in the
// signed URI.
if (request.getEndpoint().getQuery() != null) { if (request.getEndpoint().getQuery() != null) {
StringBuilder paramsToSign = new StringBuilder("?"); StringBuilder paramsToSign = new StringBuilder("?");
@ -188,6 +208,7 @@ public class RequestAuthorizeSignature implements HttpRequestFilter, RequestSign
} }
private String valueOrEmpty(Collection<String> collection) { private String valueOrEmpty(Collection<String> collection) {
return (collection != null && collection.size() >= 1) ? collection.iterator().next() : ""; return (collection != null && collection.size() >= 1) ? collection
.iterator().next() : "";
} }
} }

View File

@ -20,19 +20,23 @@
package org.jclouds.aws.s3.functions; package org.jclouds.aws.s3.functions;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
import static org.jclouds.aws.s3.reference.S3Constants.PROPERTY_S3_DEFAULT_REGIONS;
import static org.jclouds.aws.s3.reference.S3Constants.PROPERTY_S3_REGIONS;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton; import javax.inject.Singleton;
import org.jclouds.aws.domain.Region;
import org.jclouds.aws.s3.S3;
import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpRequest;
import org.jclouds.rest.binders.BindToStringPayload; import org.jclouds.rest.binders.BindToStringPayload;
import com.google.common.base.Splitter;
import com.google.common.collect.Iterables;
/** /**
* *
* Depending on your latency and legal requirements, you can specify a location constraint that will * Depending on your latency and legal requirements, you can specify a location
* affect where your data physically resides. * constraint that will affect where your data physically resides.
* *
* @author Adrian Cole * @author Adrian Cole
* *
@ -40,35 +44,36 @@ import org.jclouds.rest.binders.BindToStringPayload;
@Singleton @Singleton
public class BindRegionToXmlPayload extends BindToStringPayload { public class BindRegionToXmlPayload extends BindToStringPayload {
private final String defaultRegion; private final Iterable<String> defaultRegions;
private final Iterable<String> regions;
@Inject @Inject
BindRegionToXmlPayload(@S3 String defaultRegion) { BindRegionToXmlPayload(
this.defaultRegion = defaultRegion; @Named(PROPERTY_S3_DEFAULT_REGIONS) String defaultRegions,
@Named(PROPERTY_S3_REGIONS) String regions) {
this.defaultRegions = Splitter.on(',').split(defaultRegions);
this.regions = Splitter.on(',').split(regions);
} }
@Override @Override
public void bindToRequest(HttpRequest request, Object input) { public void bindToRequest(HttpRequest request, Object input) {
input = input == null ? defaultRegion : input; input = input == null ? Iterables.get(defaultRegions, 0) : input;
checkArgument(input instanceof String, "this binder is only valid for Region!"); checkArgument(input instanceof String,
"this binder is only valid for Region!");
String constraint = (String) input; String constraint = (String) input;
String value = null; String value = null;
if (Region.US_STANDARD.equals(constraint) || Region.US_EAST_1.equals(constraint)) { if (Iterables.contains(defaultRegions, constraint)) {
// nothing to bind as this is default. // nothing to bind as this is default.
return; return;
} else if (Region.EU_WEST_1.equals(constraint)) } else if (Iterables.contains(regions, constraint)) {
value = "EU"; value = constraint;
else if (Region.US_WEST_1.equals(constraint)) } else {
value = "us-west-1"; throw new IllegalStateException("unimplemented location: " + constraint);
else if (Region.AP_SOUTHEAST_1.equals(constraint))
value = "ap-southeast-1";
else {
throw new IllegalStateException("unimplemented location: " + this);
} }
String payload = String String payload = String
.format( .format(
"<CreateBucketConfiguration><LocationConstraint>%s</LocationConstraint></CreateBucketConfiguration>", "<CreateBucketConfiguration><LocationConstraint>%s</LocationConstraint></CreateBucketConfiguration>",
value); value);
super.bindToRequest(request, payload); super.bindToRequest(request, payload);
} }

View File

@ -37,9 +37,14 @@ public interface S3Constants extends AWSConstants, S3Headers {
public static final String DELIMITER = "delimiter"; public static final String DELIMITER = "delimiter";
public static final String PROPERTY_S3_ENDPOINT = "jclouds.s3.endpoint"; public static final String PROPERTY_S3_ENDPOINT = "jclouds.s3.endpoint";
public static final String PROPERTY_S3_REGIONS = "jclouds.s3.regions"; public static final String PROPERTY_S3_REGIONS = "jclouds.s3.regions";
public static final String PROPERTY_S3_DEFAULT_REGIONS = "jclouds.s3.default-regions";
public static final String PROPERTY_S3_SERVICE_EXPR = "jclouds.service.expr";
/** /**
* how long do we wait before obtaining a new timestamp for requests. * how long do we wait before obtaining a new timestamp for requests.
*/ */
public static final String PROPERTY_S3_SESSIONINTERVAL = "jclouds.s3.sessioninterval"; public static final String PROPERTY_S3_SESSIONINTERVAL = "jclouds.s3.sessioninterval";
public static final String PROPERTY_S3_AUTH_TAG = "jclouds.s3.auth.tag";
public static final String PROPERTY_S3_HEADER_TAG = "jclouds.s3.header.tag";
} }

View File

@ -52,7 +52,7 @@ public class EC2ComputeServiceLiveTest extends BaseComputeServiceLiveTest {
@BeforeClass @BeforeClass
@Override @Override
public void setServiceDefaults() { public void setServiceDefaults() {
service = "ec2"; service = "eucalyptus";
} }
@Override @Override

View File

@ -90,7 +90,7 @@ public class S3AsyncClientTest extends RestClientTest<S3AsyncClient> {
public void testAllRegions() throws SecurityException, NoSuchMethodException, IOException { public void testAllRegions() throws SecurityException, NoSuchMethodException, IOException {
Method method = S3AsyncClient.class.getMethod("putBucketInRegion", String.class, Method method = S3AsyncClient.class.getMethod("putBucketInRegion", String.class,
String.class, Array.newInstance(PutBucketOptions.class, 0).getClass()); String.class, Array.newInstance(PutBucketOptions.class, 0).getClass());
for (String region : Region.ALL) { for (String region : Region.ALL_S3) {
processor.createRequest(method, region, "bucket-name"); processor.createRequest(method, region, "bucket-name");
} }
} }
@ -416,7 +416,7 @@ public class S3AsyncClientTest extends RestClientTest<S3AsyncClient> {
Method method = S3AsyncClient.class.getMethod("putBucketInRegion", String.class, Method method = S3AsyncClient.class.getMethod("putBucketInRegion", String.class,
String.class, Array.newInstance(PutBucketOptions.class, 0).getClass()); String.class, Array.newInstance(PutBucketOptions.class, 0).getClass());
GeneratedHttpRequest<S3AsyncClient> httpMethod = processor.createRequest(method, GeneratedHttpRequest<S3AsyncClient> httpMethod = processor.createRequest(method,
Region.EU_WEST_1, "bucket"); "EU", "bucket");
assertRequestLineEquals(httpMethod, "PUT https://bucket.s3.amazonaws.com/ HTTP/1.1"); assertRequestLineEquals(httpMethod, "PUT https://bucket.s3.amazonaws.com/ HTTP/1.1");
assertHeadersEqual(httpMethod, assertHeadersEqual(httpMethod,

View File

@ -53,7 +53,7 @@ public class S3StubClientModule extends RestClientModule<S3Client, S3AsyncClient
bind(URI.class).annotatedWith(S3.class).toInstance(URI.create("https://localhost/s3stub")); bind(URI.class).annotatedWith(S3.class).toInstance(URI.create("https://localhost/s3stub"));
bind(String.class).annotatedWith(S3.class).toInstance(Region.US_STANDARD); bind(String.class).annotatedWith(S3.class).toInstance(Region.US_STANDARD);
bind(new TypeLiteral<Set<String>>() { bind(new TypeLiteral<Set<String>>() {
}).annotatedWith(S3.class).toInstance(Region.ALL); }).annotatedWith(S3.class).toInstance(Region.ALL_S3);
} }
@Override @Override

View File

@ -57,91 +57,111 @@ import com.google.inject.TypeLiteral;
@Test(groups = "unit", testName = "sqs.SQSAsyncClientTest") @Test(groups = "unit", testName = "sqs.SQSAsyncClientTest")
public class SQSAsyncClientTest extends RestClientTest<SQSAsyncClient> { public class SQSAsyncClientTest extends RestClientTest<SQSAsyncClient> {
public void testListQueuesInRegion() throws SecurityException, NoSuchMethodException, public void testListQueuesInRegion() throws SecurityException,
IOException { NoSuchMethodException, IOException {
Method method = SQSAsyncClient.class.getMethod("listQueuesInRegion", String.class, Array Method method = SQSAsyncClient.class.getMethod("listQueuesInRegion",
.newInstance(ListQueuesOptions.class, 0).getClass()); String.class, Array.newInstance(ListQueuesOptions.class, 0)
GeneratedHttpRequest<SQSAsyncClient> httpMethod = processor.createRequest(method, .getClass());
(String) null); GeneratedHttpRequest<SQSAsyncClient> httpMethod = processor
.createRequest(method, (String) null);
assertRequestLineEquals(httpMethod, "POST https://sqs.us-east-1.amazonaws.com/ HTTP/1.1"); assertRequestLineEquals(httpMethod,
"POST https://sqs.us-east-1.amazonaws.com/ HTTP/1.1");
assertHeadersEqual( assertHeadersEqual(
httpMethod, httpMethod,
"Content-Length: 36\nContent-Type: application/x-www-form-urlencoded\nHost: sqs.us-east-1.amazonaws.com\n"); "Content-Length: 36\nContent-Type: application/x-www-form-urlencoded\nHost: sqs.us-east-1.amazonaws.com\n");
assertPayloadEquals(httpMethod, "Version=2009-02-01&Action=ListQueues"); assertPayloadEquals(httpMethod, "Version=2009-02-01&Action=ListQueues");
assertResponseParserClassEquals(method, httpMethod, RegexListQueuesResponseHandler.class); assertResponseParserClassEquals(method, httpMethod,
RegexListQueuesResponseHandler.class);
assertSaxResponseParserClassEquals(method, null); assertSaxResponseParserClassEquals(method, null);
assertExceptionParserClassEquals(method, null); assertExceptionParserClassEquals(method, null);
checkFilters(httpMethod); checkFilters(httpMethod);
} }
public void testListQueuesInRegionOptions() throws SecurityException, NoSuchMethodException, public void testListQueuesInRegionOptions() throws SecurityException,
IOException { NoSuchMethodException, IOException {
Method method = SQSAsyncClient.class.getMethod("listQueuesInRegion", String.class, Array Method method = SQSAsyncClient.class.getMethod("listQueuesInRegion",
.newInstance(ListQueuesOptions.class, 0).getClass()); String.class, Array.newInstance(ListQueuesOptions.class, 0)
GeneratedHttpRequest<SQSAsyncClient> httpMethod = processor.createRequest(method, null, .getClass());
ListQueuesOptions.Builder.queuePrefix("prefix")); GeneratedHttpRequest<SQSAsyncClient> httpMethod = processor
.createRequest(method, null, ListQueuesOptions.Builder
.queuePrefix("prefix"));
assertRequestLineEquals(httpMethod, "POST https://sqs.us-east-1.amazonaws.com/ HTTP/1.1"); assertRequestLineEquals(httpMethod,
"POST https://sqs.us-east-1.amazonaws.com/ HTTP/1.1");
assertHeadersEqual( assertHeadersEqual(
httpMethod, httpMethod,
"Content-Length: 59\nContent-Type: application/x-www-form-urlencoded\nHost: sqs.us-east-1.amazonaws.com\n"); "Content-Length: 59\nContent-Type: application/x-www-form-urlencoded\nHost: sqs.us-east-1.amazonaws.com\n");
assertPayloadEquals(httpMethod, "Version=2009-02-01&Action=ListQueues&QueueNamePrefix=prefix");
assertResponseParserClassEquals(method, httpMethod, RegexListQueuesResponseHandler.class);
assertSaxResponseParserClassEquals(method, null);
assertExceptionParserClassEquals(method, null);
checkFilters(httpMethod);
}
public void testCreateQueueInRegion() throws SecurityException, NoSuchMethodException,
IOException {
Method method = SQSAsyncClient.class.getMethod("createQueueInRegion", String.class,
String.class, Array.newInstance(CreateQueueOptions.class, 0).getClass());
GeneratedHttpRequest<SQSAsyncClient> httpMethod = processor.createRequest(method, null,
"queueName");
assertRequestLineEquals(httpMethod, "POST https://sqs.us-east-1.amazonaws.com/ HTTP/1.1");
assertHeadersEqual(
httpMethod,
"Content-Length: 57\nContent-Type: application/x-www-form-urlencoded\nHost: sqs.us-east-1.amazonaws.com\n");
assertPayloadEquals(httpMethod, "Version=2009-02-01&Action=CreateQueue&QueueName=queueName");
assertResponseParserClassEquals(method, httpMethod, RegexQueueHandler.class);
assertSaxResponseParserClassEquals(method, null);
assertExceptionParserClassEquals(method, null);
checkFilters(httpMethod);
}
public void testCreateQueueInRegionOptions() throws SecurityException, NoSuchMethodException,
IOException {
Method method = SQSAsyncClient.class.getMethod("createQueueInRegion", String.class,
String.class, Array.newInstance(CreateQueueOptions.class, 0).getClass());
GeneratedHttpRequest<SQSAsyncClient> httpMethod = processor.createRequest(method, null,
"queueName", CreateQueueOptions.Builder.defaultVisibilityTimeout(45));
assertRequestLineEquals(httpMethod, "POST https://sqs.us-east-1.amazonaws.com/ HTTP/1.1");
assertHeadersEqual(
httpMethod,
"Content-Length: 85\nContent-Type: application/x-www-form-urlencoded\nHost: sqs.us-east-1.amazonaws.com\n");
assertPayloadEquals(httpMethod, assertPayloadEquals(httpMethod,
"Version=2009-02-01&Action=CreateQueue&QueueName=queueName&DefaultVisibilityTimeout=45"); "Version=2009-02-01&Action=ListQueues&QueueNamePrefix=prefix");
assertResponseParserClassEquals(method, httpMethod, RegexQueueHandler.class); assertResponseParserClassEquals(method, httpMethod,
RegexListQueuesResponseHandler.class);
assertSaxResponseParserClassEquals(method, null); assertSaxResponseParserClassEquals(method, null);
assertExceptionParserClassEquals(method, null); assertExceptionParserClassEquals(method, null);
checkFilters(httpMethod); checkFilters(httpMethod);
} }
public void testAllRegions() throws SecurityException, NoSuchMethodException, IOException { public void testCreateQueueInRegion() throws SecurityException,
Method method = SQSAsyncClient.class.getMethod("createQueueInRegion", String.class, NoSuchMethodException, IOException {
String.class, Array.newInstance(CreateQueueOptions.class, 0).getClass()); Method method = SQSAsyncClient.class.getMethod("createQueueInRegion",
for (String region : Iterables.filter(Region.ALL, not(equalTo("us-standard")))) { String.class, String.class, Array.newInstance(
CreateQueueOptions.class, 0).getClass());
GeneratedHttpRequest<SQSAsyncClient> httpMethod = processor
.createRequest(method, null, "queueName");
assertRequestLineEquals(httpMethod,
"POST https://sqs.us-east-1.amazonaws.com/ HTTP/1.1");
assertHeadersEqual(
httpMethod,
"Content-Length: 57\nContent-Type: application/x-www-form-urlencoded\nHost: sqs.us-east-1.amazonaws.com\n");
assertPayloadEquals(httpMethod,
"Version=2009-02-01&Action=CreateQueue&QueueName=queueName");
assertResponseParserClassEquals(method, httpMethod,
RegexQueueHandler.class);
assertSaxResponseParserClassEquals(method, null);
assertExceptionParserClassEquals(method, null);
checkFilters(httpMethod);
}
public void testCreateQueueInRegionOptions() throws SecurityException,
NoSuchMethodException, IOException {
Method method = SQSAsyncClient.class.getMethod("createQueueInRegion",
String.class, String.class, Array.newInstance(
CreateQueueOptions.class, 0).getClass());
GeneratedHttpRequest<SQSAsyncClient> httpMethod = processor
.createRequest(method, null, "queueName",
CreateQueueOptions.Builder.defaultVisibilityTimeout(45));
assertRequestLineEquals(httpMethod,
"POST https://sqs.us-east-1.amazonaws.com/ HTTP/1.1");
assertHeadersEqual(
httpMethod,
"Content-Length: 85\nContent-Type: application/x-www-form-urlencoded\nHost: sqs.us-east-1.amazonaws.com\n");
assertPayloadEquals(
httpMethod,
"Version=2009-02-01&Action=CreateQueue&QueueName=queueName&DefaultVisibilityTimeout=45");
assertResponseParserClassEquals(method, httpMethod,
RegexQueueHandler.class);
assertSaxResponseParserClassEquals(method, null);
assertExceptionParserClassEquals(method, null);
checkFilters(httpMethod);
}
public void testAllRegions() throws SecurityException,
NoSuchMethodException, IOException {
Method method = SQSAsyncClient.class.getMethod("createQueueInRegion",
String.class, String.class, Array.newInstance(
CreateQueueOptions.class, 0).getClass());
for (String region : Iterables.filter(Region.ALL_SQS,
not(equalTo("us-standard")))) {
processor.createRequest(method, region, "queueName"); processor.createRequest(method, region, "queueName");
} }
} }
@ -163,15 +183,16 @@ public class SQSAsyncClientTest extends RestClientTest<SQSAsyncClient> {
return new SQSRestClientModule() { return new SQSRestClientModule() {
@Override @Override
protected void configure() { protected void configure() {
Names.bindProperties(binder(), new SQSPropertiesBuilder(new Properties()) Names.bindProperties(binder(), new SQSPropertiesBuilder(
.withCredentials("user", "key").build()); new Properties()).withCredentials("user", "key").build());
install(new NullLoggingModule()); install(new NullLoggingModule());
super.configure(); super.configure();
} }
@Override @Override
protected String provideTimeStamp(final DateService dateService, protected String provideTimeStamp(
@Named(SQSConstants.PROPERTY_AWS_EXPIREINTERVAL) final int expiration) { final DateService dateService,
@Named(SQSConstants.PROPERTY_AWS_EXPIREINTERVAL) final int expiration) {
return "2009-11-08T15:54:08.897Z"; return "2009-11-08T15:54:08.897Z";
} }
}; };

View File

@ -29,5 +29,7 @@ s3.contextbuilder=org.jclouds.aws.s3.S3ContextBuilder
s3.propertiesbuilder=org.jclouds.aws.s3.S3PropertiesBuilder s3.propertiesbuilder=org.jclouds.aws.s3.S3PropertiesBuilder
walrus.contextbuilder=org.jclouds.aws.s3.S3ContextBuilder walrus.contextbuilder=org.jclouds.aws.s3.S3ContextBuilder
walrus.propertiesbuilder=org.jclouds.aws.s3.WalrusPropertiesBuilder walrus.propertiesbuilder=org.jclouds.aws.s3.WalrusPropertiesBuilder
googlestorage.contextbuilder=org.jclouds.aws.s3.S3ContextBuilder
googlestorage.propertiesbuilder=org.jclouds.aws.s3.GoogleStoragePropertiesBuilder
transient.contextbuilder=org.jclouds.blobstore.TransientBlobStoreContextBuilder transient.contextbuilder=org.jclouds.blobstore.TransientBlobStoreContextBuilder
transient.propertiesbuilder=org.jclouds.blobstore.TransientBlobStorePropertiesBuilder transient.propertiesbuilder=org.jclouds.blobstore.TransientBlobStorePropertiesBuilder