Issue 27: got eucalyptus and walrus working, after refactoring error handling

This commit is contained in:
Adrian Cole 2010-07-11 01:26:56 -07:00
parent f5f031685d
commit 41e3ec6709
74 changed files with 1018 additions and 601 deletions

View File

@ -23,8 +23,8 @@ import static org.jclouds.util.Utils.propagateOrNull;
import java.net.URI;
import org.jclouds.blobstore.KeyAlreadyExistsException;
import org.jclouds.http.HttpRequest;
import org.jclouds.rest.InvocationContext;
import org.jclouds.rest.internal.GeneratedHttpRequest;
import com.google.common.base.Function;
@ -43,8 +43,10 @@ public class ReturnEndpointIfAlreadyExists implements Function<Exception, URI>,
return URI.class.cast(propagateOrNull(from));
}
public void setContext(GeneratedHttpRequest<?> request) {
@Override
public ReturnEndpointIfAlreadyExists setContext(HttpRequest request) {
this.endpoint = request == null ? null : request.getEndpoint();
return this;
}
}

View File

@ -22,6 +22,8 @@ import static org.jclouds.Constants.PROPERTY_API_VERSION;
import static org.jclouds.Constants.PROPERTY_ENDPOINT;
import static org.jclouds.aws.ec2.reference.EC2Constants.PROPERTY_EC2_AMI_OWNERS;
import static org.jclouds.aws.ec2.reference.EC2Constants.PROPERTY_ELB_ENDPOINT;
import static org.jclouds.aws.reference.AWSConstants.PROPERTY_AUTH_TAG;
import static org.jclouds.aws.reference.AWSConstants.PROPERTY_HEADER_TAG;
import java.util.Properties;
@ -36,6 +38,8 @@ public class EC2PropertiesBuilder extends PropertiesBuilder {
@Override
protected Properties defaultProperties() {
Properties properties = super.defaultProperties();
properties.setProperty(PROPERTY_AUTH_TAG, "AWS");
properties.setProperty(PROPERTY_HEADER_TAG, "amz");
properties.setProperty(PROPERTY_ENDPOINT, "https://ec2.us-east-1.amazonaws.com");
properties.setProperty(PROPERTY_API_VERSION, EC2AsyncClient.VERSION);
properties.setProperty(PROPERTY_ELB_ENDPOINT,

View File

@ -34,7 +34,7 @@ public class EucalyptusPropertiesBuilder extends EC2PropertiesBuilder {
protected Properties defaultProperties() {
Properties properties = super.defaultProperties();
properties.setProperty(PROPERTY_ENDPOINT,
"http://ecc.eucalyptus.com:8773/services/Eucalyptus");
"http://173.205.188.130:8773/services/Eucalyptus");
// TODO
// properties.setProperty(PROPERTY_ELB_ENDPOINT,
// "https://elasticloadbalancing.us-east-1.amazonaws.com");

View File

@ -29,12 +29,13 @@ import org.jclouds.aws.ec2.util.EC2Utils;
import org.jclouds.date.DateService;
import org.jclouds.http.functions.ParseSax;
import org.jclouds.logging.Logger;
import org.jclouds.rest.internal.GeneratedHttpRequest;
/**
*
* @author Adrian Cole
*/
public class AttachmentHandler extends ParseSax.HandlerWithResult<Attachment> {
public class AttachmentHandler extends ParseSax.HandlerForGeneratedRequestWithResult<Attachment> {
private StringBuilder currentText = new StringBuilder();
@Resource
@ -51,7 +52,7 @@ public class AttachmentHandler extends ParseSax.HandlerWithResult<Attachment> {
private Date attachTime;
public Attachment getResult() {
String region = EC2Utils.findRegionInArgsOrNull(request);
String region = EC2Utils.findRegionInArgsOrNull((GeneratedHttpRequest<?>) request);
if (region == null)
region = defaultRegion;
return new Attachment(region, volumeId, instanceId, device, attachmentStatus, attachTime);

View File

@ -34,8 +34,9 @@ import org.jclouds.aws.ec2.domain.RootDeviceType;
import org.jclouds.aws.ec2.domain.RunningInstance;
import org.jclouds.aws.ec2.util.EC2Utils;
import org.jclouds.date.DateService;
import org.jclouds.http.functions.ParseSax.HandlerWithResult;
import org.jclouds.http.functions.ParseSax.HandlerForGeneratedRequestWithResult;
import org.jclouds.logging.Logger;
import org.jclouds.rest.internal.GeneratedHttpRequest;
import org.xml.sax.Attributes;
import com.google.common.collect.Maps;
@ -45,15 +46,14 @@ import com.google.common.collect.Sets;
*
* @author Adrian Cole
*/
public abstract class BaseReservationHandler<T> extends HandlerWithResult<T> {
public abstract class BaseReservationHandler<T> extends HandlerForGeneratedRequestWithResult<T> {
protected final DateService dateService;
protected final String defaultRegion;
@Inject
public BaseReservationHandler(DateService dateService,
@Region String defaultRegion) {
public BaseReservationHandler(DateService dateService, @Region String defaultRegion) {
this.dateService = dateService;
this.defaultRegion = defaultRegion;
}
@ -90,8 +90,7 @@ public abstract class BaseReservationHandler<T> extends HandlerWithResult<T> {
protected boolean inProductCodes;
protected boolean inGroups;
private boolean inBlockDeviceMapping;
private Map<String, RunningInstance.EbsBlockDevice> ebsBlockDevices = Maps
.newHashMap();
private Map<String, RunningInstance.EbsBlockDevice> ebsBlockDevices = Maps.newHashMap();
private String volumeId;
private Attachment.Status attachmentStatus;
@ -101,8 +100,7 @@ public abstract class BaseReservationHandler<T> extends HandlerWithResult<T> {
private String deviceName;
private String rootDeviceName;
public void startElement(String uri, String name, String qName,
Attributes attrs) {
public void startElement(String uri, String name, String qName, Attributes attrs) {
if (qName.equals("instancesSet")) {
inInstances = true;
} else if (qName.equals("productCodesSet")) {
@ -194,14 +192,11 @@ public abstract class BaseReservationHandler<T> extends HandlerWithResult<T> {
} else if (qName.equals("volumeId")) {
volumeId = currentOrNull();
} else if (qName.equals("status")) {
attachmentStatus = Attachment.Status.fromValue(currentText.toString()
.trim());
attachmentStatus = Attachment.Status.fromValue(currentText.toString().trim());
} else if (qName.equals("attachTime")) {
attachTime = dateService.iso8601DateParse(currentText.toString()
.trim());
attachTime = dateService.iso8601DateParse(currentText.toString().trim());
} else if (qName.equals("deleteOnTermination")) {
deleteOnTermination = Boolean.parseBoolean(currentText.toString()
.trim());
deleteOnTermination = Boolean.parseBoolean(currentText.toString().trim());
} else if (qName.equals("rootDeviceName")) {
rootDeviceName = currentOrNull();
} else if (qName.equals("item")) {
@ -212,36 +207,34 @@ public abstract class BaseReservationHandler<T> extends HandlerWithResult<T> {
protected void inItem() {
if (inBlockDeviceMapping) {
ebsBlockDevices.put(deviceName, new RunningInstance.EbsBlockDevice(
volumeId, attachmentStatus, attachTime, deleteOnTermination));
ebsBlockDevices.put(deviceName, new RunningInstance.EbsBlockDevice(volumeId,
attachmentStatus, attachTime, deleteOnTermination));
this.deviceName = null;
this.volumeId = null;
this.attachmentStatus = null;
this.attachTime = null;
this.deleteOnTermination = true;
} else if (inInstances && !inProductCodes && !inBlockDeviceMapping) {
String region = EC2Utils.findRegionInArgsOrNull(request);
String region = EC2Utils.findRegionInArgsOrNull((GeneratedHttpRequest<?>) request);
// Eucalyptus
if (ipAddress == null && dnsName != null
&& dnsName.matches(".*[0-9]$")) {
if (ipAddress == null && dnsName != null && dnsName.matches(".*[0-9]$")) {
ipAddress = dnsName;
dnsName = null;
}
if (privateIpAddress == null && privateDnsName != null
&& privateDnsName.matches(".*[0-9]$")) {
&& privateDnsName.matches(".*[0-9]$")) {
privateIpAddress = privateDnsName;
privateDnsName = null;
}
if (region == null)
region = defaultRegion;
instances.add(new RunningInstance(region, groupIds, amiLaunchIndex,
dnsName, imageId, instanceId, instanceState, instanceType,
ipAddress, kernelId, keyName, launchTime, monitoring,
availabilityZone, platform, privateDnsName, privateIpAddress,
productCodes, ramdiskId, reason, subnetId, vpcId,
rootDeviceType, rootDeviceName, ebsBlockDevices));
instances.add(new RunningInstance(region, groupIds, amiLaunchIndex, dnsName, imageId,
instanceId, instanceState, instanceType, ipAddress, kernelId, keyName,
launchTime, monitoring, availabilityZone, platform, privateDnsName,
privateIpAddress, productCodes, ramdiskId, reason, subnetId, vpcId,
rootDeviceType, rootDeviceName, ebsBlockDevices));
this.amiLaunchIndex = null;
this.dnsName = null;
this.imageId = null;
@ -273,11 +266,11 @@ public abstract class BaseReservationHandler<T> extends HandlerWithResult<T> {
}
protected Reservation newReservation() {
String region = EC2Utils.findRegionInArgsOrNull(request);
String region = EC2Utils.findRegionInArgsOrNull((GeneratedHttpRequest<?>) request);
if (region == null)
region = defaultRegion;
Reservation info = new Reservation(region, groupIds, instances, ownerId,
requesterId, reservationId);
Reservation info = new Reservation(region, groupIds, instances, ownerId, requesterId,
reservationId);
this.groupIds = Sets.newTreeSet();
this.instances = Sets.newTreeSet();
this.ownerId = null;

View File

@ -32,6 +32,7 @@ import org.jclouds.aws.ec2.domain.Attachment;
import org.jclouds.aws.ec2.domain.Volume;
import org.jclouds.aws.ec2.util.EC2Utils;
import org.jclouds.date.DateService;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.functions.ParseSax;
import org.jclouds.logging.Logger;
import org.jclouds.rest.internal.GeneratedHttpRequest;
@ -43,7 +44,8 @@ import com.google.common.collect.Sets;
*
* @author Adrian Cole
*/
public class CreateVolumeResponseHandler extends ParseSax.HandlerWithResult<Volume> {
public class CreateVolumeResponseHandler extends
ParseSax.HandlerForGeneratedRequestWithResult<Volume> {
private StringBuilder currentText = new StringBuilder();
@Resource
@ -154,11 +156,11 @@ public class CreateVolumeResponseHandler extends ParseSax.HandlerWithResult<Volu
}
@Override
public void setContext(GeneratedHttpRequest<?> request) {
public CreateVolumeResponseHandler setContext(HttpRequest request) {
super.setContext(request);
region = EC2Utils.findRegionInArgsOrNull(request);
region = EC2Utils.findRegionInArgsOrNull((GeneratedHttpRequest<?>) request);
if (region == null) {
String zone = EC2Utils.findAvailabilityZoneInArgsOrNull(request);
String zone = EC2Utils.findAvailabilityZoneInArgsOrNull((GeneratedHttpRequest<?>) request);
if (zone != null) {
region = checkNotNull(availabilityZoneToRegion.get(zone), String.format(
"zone %s not in %s", zone, availabilityZoneToRegion));
@ -166,5 +168,6 @@ public class CreateVolumeResponseHandler extends ParseSax.HandlerWithResult<Volu
region = defaultRegion;
}
}
return this;
}
}

View File

@ -26,8 +26,9 @@ import javax.inject.Inject;
import org.jclouds.aws.Region;
import org.jclouds.aws.ec2.domain.PublicIpInstanceIdPair;
import org.jclouds.aws.ec2.util.EC2Utils;
import org.jclouds.http.functions.ParseSax.HandlerWithResult;
import org.jclouds.http.functions.ParseSax.HandlerForGeneratedRequestWithResult;
import org.jclouds.logging.Logger;
import org.jclouds.rest.internal.GeneratedHttpRequest;
import com.google.common.collect.Sets;
@ -36,7 +37,7 @@ import com.google.common.collect.Sets;
* @author Adrian Cole
*/
public class DescribeAddressesResponseHandler extends
HandlerWithResult<Set<PublicIpInstanceIdPair>> {
HandlerForGeneratedRequestWithResult<Set<PublicIpInstanceIdPair>> {
@Resource
protected Logger logger = Logger.NULL;
@ -59,7 +60,7 @@ public class DescribeAddressesResponseHandler extends
} else if (qName.equals("instanceId")) {
instanceId = currentOrNull();
} else if (qName.equals("item")) {
String region = EC2Utils.findRegionInArgsOrNull(request);
String region = EC2Utils.findRegionInArgsOrNull((GeneratedHttpRequest<?>) request);
if (region == null)
region = defaultRegion;
pairs.add(new PublicIpInstanceIdPair(region, ipAddress, instanceId));

View File

@ -34,6 +34,7 @@ import org.jclouds.aws.ec2.domain.Image.ImageType;
import org.jclouds.aws.ec2.util.EC2Utils;
import org.jclouds.http.functions.ParseSax;
import org.jclouds.logging.Logger;
import org.jclouds.rest.internal.GeneratedHttpRequest;
import org.xml.sax.Attributes;
import com.google.common.collect.Maps;
@ -48,7 +49,8 @@ import com.google.common.collect.Sets;
* @see <a href="http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference-query-DescribeImages.html"
* />
*/
public class DescribeImagesResponseHandler extends ParseSax.HandlerWithResult<Set<Image>> {
public class DescribeImagesResponseHandler extends
ParseSax.HandlerForGeneratedRequestWithResult<Set<Image>> {
@Inject
public DescribeImagesResponseHandler(@Region String defaultRegion) {
@ -151,7 +153,7 @@ public class DescribeImagesResponseHandler extends ParseSax.HandlerWithResult<Se
this.deleteOnTermination = true;
} else if (!inProductCodes) {
try {
String region = EC2Utils.findRegionInArgsOrNull(request);
String region = EC2Utils.findRegionInArgsOrNull((GeneratedHttpRequest<?>) request);
if (region == null)
region = defaultRegion;
contents.add(new Image(region, architecture, this.name, description, imageId,

View File

@ -26,6 +26,7 @@ import org.jclouds.aws.Region;
import org.jclouds.aws.ec2.domain.KeyPair;
import org.jclouds.aws.ec2.util.EC2Utils;
import org.jclouds.http.functions.ParseSax;
import org.jclouds.rest.internal.GeneratedHttpRequest;
import com.google.common.collect.Sets;
@ -36,7 +37,8 @@ import com.google.common.collect.Sets;
* />
* @author Adrian Cole
*/
public class DescribeKeyPairsResponseHandler extends ParseSax.HandlerWithResult<Set<KeyPair>> {
public class DescribeKeyPairsResponseHandler extends
ParseSax.HandlerForGeneratedRequestWithResult<Set<KeyPair>> {
@Inject
@Region
String defaultRegion;
@ -55,7 +57,7 @@ public class DescribeKeyPairsResponseHandler extends ParseSax.HandlerWithResult<
if (qName.equals("keyFingerprint")) {
this.keyFingerprint = currentText.toString().trim();
} else if (qName.equals("item")) {
String region = EC2Utils.findRegionInArgsOrNull(request);
String region = EC2Utils.findRegionInArgsOrNull((GeneratedHttpRequest<?>) request);
if (region == null)
region = defaultRegion;
keyPairs.add(new KeyPair(region, keyName, keyFingerprint, null));

View File

@ -29,6 +29,7 @@ import org.jclouds.aws.ec2.domain.SecurityGroup;
import org.jclouds.aws.ec2.domain.UserIdGroupPair;
import org.jclouds.aws.ec2.util.EC2Utils;
import org.jclouds.http.functions.ParseSax;
import org.jclouds.rest.internal.GeneratedHttpRequest;
import org.xml.sax.Attributes;
import com.google.common.collect.Sets;
@ -41,7 +42,7 @@ import com.google.common.collect.Sets;
* @author Adrian Cole
*/
public class DescribeSecurityGroupsResponseHandler extends
ParseSax.HandlerWithResult<SortedSet<SecurityGroup>> {
ParseSax.HandlerForGeneratedRequestWithResult<SortedSet<SecurityGroup>> {
@Inject
@Region
String defaultRegion;
@ -117,7 +118,7 @@ public class DescribeSecurityGroupsResponseHandler extends
this.userId = null;
this.userIdGroupName = null;
} else if (!inIpPermissions && !inIpRanges && !inGroups) {
String region = EC2Utils.findRegionInArgsOrNull(request);
String region = EC2Utils.findRegionInArgsOrNull((GeneratedHttpRequest<?>) request);
if (region == null)
region = defaultRegion;
securtyGroups.add(new SecurityGroup(region, groupName, ownerId, groupDescription,

View File

@ -23,8 +23,8 @@ import java.util.Set;
import javax.inject.Inject;
import org.jclouds.aws.ec2.domain.Snapshot;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.functions.ParseSax;
import org.jclouds.rest.internal.GeneratedHttpRequest;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
@ -66,8 +66,9 @@ public class DescribeSnapshotsResponseHandler extends ParseSax.HandlerWithResult
}
@Override
public void setContext(GeneratedHttpRequest<?> request) {
public DescribeSnapshotsResponseHandler setContext(HttpRequest request) {
snapshotHandler.setContext(request);
super.setContext(request);
return this;
}
}

View File

@ -23,8 +23,8 @@ import java.util.Set;
import javax.inject.Inject;
import org.jclouds.aws.ec2.domain.Volume;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.functions.ParseSax;
import org.jclouds.rest.internal.GeneratedHttpRequest;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
@ -73,8 +73,9 @@ public class DescribeVolumesResponseHandler extends ParseSax.HandlerWithResult<S
}
@Override
public void setContext(GeneratedHttpRequest<?> request) {
public DescribeVolumesResponseHandler setContext(HttpRequest request) {
volumeHandler.setContext(request);
super.setContext(request);
return this;
}
}

View File

@ -26,7 +26,8 @@ import org.jclouds.aws.Region;
import org.jclouds.aws.ec2.domain.InstanceState;
import org.jclouds.aws.ec2.domain.InstanceStateChange;
import org.jclouds.aws.ec2.util.EC2Utils;
import org.jclouds.http.functions.ParseSax.HandlerWithResult;
import org.jclouds.http.functions.ParseSax.HandlerForGeneratedRequestWithResult;
import org.jclouds.rest.internal.GeneratedHttpRequest;
import org.xml.sax.Attributes;
import com.google.common.collect.Sets;
@ -46,7 +47,8 @@ import com.google.common.collect.Sets;
* @see <a href="http://docs.amazonwebservices.com/AWSEC2/latest/APIReference/ApiReference-ItemType-StopInstancesResponseInfoType.html"
* />
*/
public class InstanceStateChangeHandler extends HandlerWithResult<SortedSet<InstanceStateChange>> {
public class InstanceStateChangeHandler extends
HandlerForGeneratedRequestWithResult<SortedSet<InstanceStateChange>> {
private StringBuilder currentText = new StringBuilder();
@Inject
@Region
@ -89,7 +91,7 @@ public class InstanceStateChangeHandler extends HandlerWithResult<SortedSet<Inst
previousState = InstanceState.fromValue(currentOrNull());
}
} else if (qName.equals("item")) {
String region = EC2Utils.findRegionInArgsOrNull(request);
String region = EC2Utils.findRegionInArgsOrNull((GeneratedHttpRequest<?>) request);
if (region == null)
region = defaultRegion;
instances.add(new InstanceStateChange(region, instanceId, shutdownState, previousState));

View File

@ -24,6 +24,7 @@ import org.jclouds.aws.Region;
import org.jclouds.aws.ec2.domain.KeyPair;
import org.jclouds.aws.ec2.util.EC2Utils;
import org.jclouds.http.functions.ParseSax;
import org.jclouds.rest.internal.GeneratedHttpRequest;
/**
*
@ -32,7 +33,7 @@ import org.jclouds.http.functions.ParseSax;
* />
* @author Adrian Cole
*/
public class KeyPairResponseHandler extends ParseSax.HandlerWithResult<KeyPair> {
public class KeyPairResponseHandler extends ParseSax.HandlerForGeneratedRequestWithResult<KeyPair> {
@Inject
@Region
String defaultRegion;
@ -42,7 +43,7 @@ public class KeyPairResponseHandler extends ParseSax.HandlerWithResult<KeyPair>
private String keyName;
public KeyPair getResult() {
String region = EC2Utils.findRegionInArgsOrNull(request);
String region = EC2Utils.findRegionInArgsOrNull((GeneratedHttpRequest<?>) request);
if (region == null)
region = defaultRegion;
return new KeyPair(region, keyName, keyFingerprint, keyMaterial);

View File

@ -28,12 +28,13 @@ import org.jclouds.aws.ec2.domain.Snapshot.Status;
import org.jclouds.aws.ec2.util.EC2Utils;
import org.jclouds.date.DateService;
import org.jclouds.http.functions.ParseSax;
import org.jclouds.rest.internal.GeneratedHttpRequest;
/**
*
* @author Adrian Cole
*/
public class SnapshotHandler extends ParseSax.HandlerWithResult<Snapshot> {
public class SnapshotHandler extends ParseSax.HandlerForGeneratedRequestWithResult<Snapshot> {
private StringBuilder currentText = new StringBuilder();
protected final DateService dateService;
@ -56,7 +57,7 @@ public class SnapshotHandler extends ParseSax.HandlerWithResult<Snapshot> {
}
public Snapshot getResult() {
String region = EC2Utils.findRegionInArgsOrNull(request);
String region = EC2Utils.findRegionInArgsOrNull((GeneratedHttpRequest<?>) request);
if (region == null)
region = defaultRegion;
Snapshot snapshot = new Snapshot(region, id, volumeId, volumeSize, status, startTime,

View File

@ -20,6 +20,8 @@ package org.jclouds.aws.elb;
import static org.jclouds.Constants.PROPERTY_API_VERSION;
import static org.jclouds.Constants.PROPERTY_ENDPOINT;
import static org.jclouds.aws.reference.AWSConstants.PROPERTY_AUTH_TAG;
import static org.jclouds.aws.reference.AWSConstants.PROPERTY_HEADER_TAG;
import static org.jclouds.aws.reference.AWSConstants.PROPERTY_REGIONS;
import java.util.Properties;
@ -38,6 +40,8 @@ public class ELBPropertiesBuilder extends PropertiesBuilder {
@Override
protected Properties defaultProperties() {
Properties properties = super.defaultProperties();
properties.setProperty(PROPERTY_AUTH_TAG, "AWS");
properties.setProperty(PROPERTY_HEADER_TAG, "amz");
properties.setProperty(PROPERTY_API_VERSION, ELBAsyncClient.VERSION);
properties.setProperty(PROPERTY_REGIONS, Joiner.on(',').join(Region.US_EAST_1,
Region.US_WEST_1, Region.EU_WEST_1, Region.AP_SOUTHEAST_1));

View File

@ -30,6 +30,7 @@ import org.jclouds.aws.elb.domain.LoadBalancer.AppCookieStickinessPolicy;
import org.jclouds.aws.elb.domain.LoadBalancer.LBCookieStickinessPolicy;
import org.jclouds.aws.elb.domain.LoadBalancer.LoadBalancerListener;
import org.jclouds.date.DateService;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.functions.ParseSax;
import org.jclouds.logging.Logger;
import org.jclouds.rest.internal.GeneratedHttpRequest;
@ -42,7 +43,7 @@ import com.google.common.collect.Sets;
* @author Lili Nadar
*/
public class DescribeLoadBalancersResponseHandler extends
ParseSax.HandlerWithResult<Set<LoadBalancer>> {
ParseSax.HandlerForGeneratedRequestWithResult<Set<LoadBalancer>> {
@Inject
public DescribeLoadBalancersResponseHandler(@Region String defaultRegion) {
this.defaultRegion = defaultRegion;
@ -129,7 +130,7 @@ public class DescribeLoadBalancersResponseHandler extends
} else if (!(inListenerDescriptions || inAppCookieStickinessPolicies || inInstances
|| inLBCookieStickinessPolicies || inAvailabilityZones)) {
try {
String region = EC2Utils.findRegionInArgsOrNull(request);
String region = EC2Utils.findRegionInArgsOrNull((GeneratedHttpRequest<?>) request);
if (region == null)
region = defaultRegion;
@ -158,9 +159,10 @@ public class DescribeLoadBalancersResponseHandler extends
}
@Override
public void setContext(GeneratedHttpRequest<?> request) {
public DescribeLoadBalancersResponseHandler setContext(HttpRequest request) {
listenerHandler.setContext(request);
super.setContext(request);
return this;
}
public class LoadBalancerListenerHandler extends

View File

@ -18,6 +18,8 @@
*/
package org.jclouds.aws.handlers;
import static org.jclouds.http.HttpUtils.closeClientButKeepContentStream;
import javax.annotation.Resource;
import javax.inject.Named;
@ -25,10 +27,8 @@ import org.jclouds.Constants;
import org.jclouds.aws.domain.AWSError;
import org.jclouds.aws.util.AWSUtils;
import org.jclouds.http.HttpCommand;
import org.jclouds.http.HttpException;
import org.jclouds.http.HttpResponse;
import org.jclouds.http.HttpRetryHandler;
import org.jclouds.http.HttpUtils;
import org.jclouds.logging.Logger;
import com.google.inject.Inject;
@ -59,20 +59,16 @@ public class AWSClientErrorRetryHandler implements HttpRetryHandler {
return false;
if (response.getStatusCode() == 400 || response.getStatusCode() == 403
|| response.getStatusCode() == 409) {
byte[] content = HttpUtils.closeClientButKeepContentStream(response);
command.incrementFailureCount();
// Content can be null in the case of HEAD requests
if (content != null) {
try {
AWSError error = utils.parseAWSErrorFromContent(command.getRequest(), response,
new String(content));
if ("RequestTimeout".equals(error.getCode())
|| "OperationAborted".equals(error.getCode())
|| "SignatureDoesNotMatch".equals(error.getCode())) {
return true;
}
} catch (HttpException e) {
logger.warn(e, "error parsing response: %s", new String(content));
if (response.getPayload() != null) {
closeClientButKeepContentStream(response);
AWSError error = utils.parseAWSErrorFromContent(command.getRequest(), response);
if (error != null
&& ("RequestTimeout".equals(error.getCode())
|| "OperationAborted".equals(error.getCode()) || "SignatureDoesNotMatch"
.equals(error.getCode()))) {
return true;
}
}
}

View File

@ -32,7 +32,6 @@ import javax.ws.rs.core.UriBuilder;
import org.jclouds.aws.domain.AWSError;
import org.jclouds.aws.util.AWSUtils;
import org.jclouds.http.HttpCommand;
import org.jclouds.http.HttpException;
import org.jclouds.http.HttpResponse;
import org.jclouds.http.handlers.BackoffLimitedRetryHandler;
import org.jclouds.http.handlers.RedirectionRetryHandler;
@ -57,33 +56,26 @@ public class AWSRedirectionRetryHandler extends RedirectionRetryHandler {
public boolean shouldRetryRequest(HttpCommand command, HttpResponse response) {
if (response.getFirstHeaderOrNull(HttpHeaders.LOCATION) == null
&& (response.getStatusCode() == 301 || response.getStatusCode() == 307)) {
byte[] content = closeClientButKeepContentStream(response);
if (command.getRequest().getMethod() == HttpMethod.HEAD) {
changeToGETRequest(command.getRequest());
return true;
} else {
command.incrementRedirectCount();
try {
AWSError error = utils.parseAWSErrorFromContent(command.getRequest(), response,
new String(content));
String host = error.getDetails().get("Endpoint");
if (host != null) {
if (host.equals(command.getRequest().getEndpoint().getHost())) {
// must be an amazon error related to
// http://developer.amazonwebservices.com/connect/thread.jspa?messageID=72287&#72287
return backoffHandler.shouldRetryRequest(command, response);
} else {
changeSchemeHostAndPortTo(command.getRequest(), command.getRequest()
.getEndpoint().getScheme(), host, command.getRequest().getEndpoint()
.getPort(), uriBuilderProvider.get());
}
return true;
closeClientButKeepContentStream(response);
AWSError error = utils.parseAWSErrorFromContent(command.getRequest(), response);
String host = error.getDetails().get("Endpoint");
if (host != null) {
if (host.equals(command.getRequest().getEndpoint().getHost())) {
// must be an amazon error related to
// http://developer.amazonwebservices.com/connect/thread.jspa?messageID=72287&#72287
return backoffHandler.shouldRetryRequest(command, response);
} else {
return false;
changeSchemeHostAndPortTo(command.getRequest(), command.getRequest()
.getEndpoint().getScheme(), host, command.getRequest().getEndpoint()
.getPort(), uriBuilderProvider.get());
}
} catch (HttpException e) {
logger.error(e, "error on redirect for command %s; response %s; retrying...",
command, response);
return true;
} else {
return false;
}
}

View File

@ -20,8 +20,6 @@ package org.jclouds.aws.handlers;
import static org.jclouds.http.HttpUtils.releasePayload;
import java.io.IOException;
import javax.annotation.Resource;
import javax.inject.Inject;
import javax.inject.Singleton;
@ -39,7 +37,6 @@ import org.jclouds.http.HttpResponseException;
import org.jclouds.logging.Logger;
import org.jclouds.rest.AuthorizationException;
import org.jclouds.rest.ResourceNotFoundException;
import org.jclouds.util.Utils;
import com.google.common.annotations.VisibleForTesting;
@ -67,7 +64,7 @@ public class ParseAWSErrorFromXmlContent implements HttpErrorHandler {
HttpRequest request = command.getRequest();
Exception exception = new HttpResponseException(command, response);
try {
AWSError error = parseErrorFromContentOrNull(request, response);
AWSError error = utils.parseAWSErrorFromContent(request, response);
exception = error != null ? new AWSResponseException(command, response, error) : exception;
switch (response.getStatusCode()) {
case 400:
@ -103,16 +100,4 @@ public class ParseAWSErrorFromXmlContent implements HttpErrorHandler {
}
}
AWSError parseErrorFromContentOrNull(HttpRequest request, HttpResponse response) {
if (response.getPayload() != null) {
try {
String content = Utils.toStringAndClose(response.getPayload().getInput());
if (content != null && content.indexOf('<') >= 0)
return utils.parseAWSErrorFromContent(request, response, content);
} catch (IOException e) {
logger.warn(e, "exception reading error from response", response);
}
}
return null;
}
}

View File

@ -26,5 +26,6 @@ package org.jclouds.aws.reference;
public interface AWSConstants {
public static final String PROPERTY_REGIONS = "jclouds.aws.regions";
public static final String PROPERTY_DEFAULT_REGIONS = "jclouds.aws.default_regions";
public static final String PROPERTY_AUTH_TAG = "jclouds.aws.auth.tag";
public static final String PROPERTY_HEADER_TAG = "jclouds.aws.header.tag";
}

View File

@ -16,34 +16,18 @@
* limitations under the License.
* ====================================================================
*/
package org.jclouds.rest.annotations;
package org.jclouds.aws.s3;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Prefixes the hostname of the endpoint with the contents of a method parameter,
*
* @author Adrian Cole
* Annotates the parameter that this is a bucket.
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface HostPrefixParam {
/**
* Defines the characters that will be inserted in-between the value of the annotated method
* argument and the existing hostname.
*
* <p />
* <ul>
* <li>hostname was {@code mydomain.com}</li>
* <li>method argument is {@code myhost}</li>
* <li>if {@code joinOn} is not set, result is {@code myhostmydomain.com}</li>
* <li>if {@code joinOn} is set to {@code .}, result is {@code myhost.mydomain.com}</li>
* </ul>
*
*/
String value() default ".";
@Target(PARAMETER)
@Retention(RUNTIME)
public @interface Bucket {
}

View File

@ -1,16 +1,33 @@
/**
*
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed 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.Constants.PROPERTY_ENDPOINT;
import static org.jclouds.aws.reference.AWSConstants.PROPERTY_AUTH_TAG;
import static org.jclouds.aws.reference.AWSConstants.PROPERTY_DEFAULT_REGIONS;
import static org.jclouds.aws.reference.AWSConstants.PROPERTY_HEADER_TAG;
import static org.jclouds.aws.reference.AWSConstants.PROPERTY_REGIONS;
import static org.jclouds.aws.s3.reference.S3Constants.PROPERTY_S3_AUTH_TAG;
import static org.jclouds.aws.s3.reference.S3Constants.PROPERTY_S3_HEADER_TAG;
import static org.jclouds.aws.s3.reference.S3Constants.PROPERTY_S3_SERVICE_EXPR;
import java.util.Properties;
/**
* Builds properties used in Walrus Clients
* Builds properties used in Google Storage
*
* @author Adrian Cole
*/
@ -18,9 +35,8 @@ public class GoogleStoragePropertiesBuilder extends S3PropertiesBuilder {
@Override
protected Properties defaultProperties() {
Properties properties = super.defaultProperties();
properties.setProperty(PROPERTY_S3_AUTH_TAG, "GOOG1");
properties.setProperty(PROPERTY_S3_HEADER_TAG, "goog");
properties.setProperty(PROPERTY_S3_SERVICE_EXPR, "\\.commondatastorage\\.googleapis\\.com");
properties.setProperty(PROPERTY_AUTH_TAG, "GOOG1");
properties.setProperty(PROPERTY_HEADER_TAG, "goog");
return properties;
}

View File

@ -30,8 +30,11 @@ import javax.ws.rs.HEAD;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.jclouds.aws.s3.binders.BindACLToXMLPayload;
import org.jclouds.aws.s3.binders.BindAsHostPrefixIfConfigured;
import org.jclouds.aws.s3.binders.BindBucketLoggingToXmlPayload;
import org.jclouds.aws.s3.binders.BindNoBucketLoggingToXmlPayload;
import org.jclouds.aws.s3.binders.BindPayerToXmlPayload;
@ -73,7 +76,6 @@ import org.jclouds.http.options.GetOptions;
import org.jclouds.rest.annotations.BinderParam;
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;
@ -98,7 +100,6 @@ import com.google.common.util.concurrent.ListenableFuture;
* @see S3Client
* @see <a href="http://docs.amazonwebservices.com/AmazonS3/2006-03-01/RESTAPI.html" />
*/
@VirtualHost
@SkipEncoding('/')
@RequestFilters(RequestAuthorizeSignature.class)
@BlobScope(CONTAINER)
@ -118,7 +119,7 @@ public interface S3AsyncClient {
@ExceptionParser(ReturnNullOnKeyNotFound.class)
@ResponseParser(ParseObjectFromHeadersAndHttpContent.class)
ListenableFuture<S3Object> getObject(
@HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName,
@Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators( { BucketNameValidator.class }) String bucketName,
@PathParam("key") String key, GetOptions... options);
/**
@ -129,7 +130,7 @@ public interface S3AsyncClient {
@ExceptionParser(ReturnNullOnKeyNotFound.class)
@ResponseParser(ParseObjectMetadataFromHeaders.class)
ListenableFuture<ObjectMetadata> headObject(
@HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName,
@Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators( { BucketNameValidator.class }) String bucketName,
@PathParam("key") String key);
/**
@ -139,7 +140,7 @@ public interface S3AsyncClient {
@Path("{key}")
@ExceptionParser(ReturnFalseOnKeyNotFound.class)
ListenableFuture<Boolean> objectExists(
@HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName,
@Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators( { BucketNameValidator.class }) String bucketName,
@PathParam("key") String key);
/**
@ -149,7 +150,7 @@ public interface S3AsyncClient {
@Path("{key}")
@ExceptionParser(ReturnVoidOnNotFoundOr404.class)
ListenableFuture<Void> deleteObject(
@HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName,
@Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators( { BucketNameValidator.class }) String bucketName,
@PathParam("key") String key);
/**
@ -159,7 +160,7 @@ public interface S3AsyncClient {
@Path("{key}")
@ResponseParser(ParseETagHeader.class)
ListenableFuture<String> putObject(
@HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName,
@Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators( { BucketNameValidator.class }) String bucketName,
@PathParam("key") @ParamParser(ObjectKey.class) @BinderParam(BindS3ObjectToPayload.class) S3Object object,
PutObjectOptions... options);
@ -172,7 +173,7 @@ public interface S3AsyncClient {
ListenableFuture<Boolean> putBucketInRegion(
// TODO endpoint based on region
@BinderParam(BindRegionToXmlPayload.class) @Nullable String region,
@HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName,
@Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators( { BucketNameValidator.class }) String bucketName,
PutBucketOptions... options);
/**
@ -182,7 +183,7 @@ public interface S3AsyncClient {
@Path("/")
@ExceptionParser(ReturnTrueOn404OrNotFoundFalseIfNotEmpty.class)
ListenableFuture<Boolean> deleteBucketIfEmpty(
@HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName);
@Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators( { BucketNameValidator.class }) String bucketName);
/**
* @see S3Client#bucketExists
@ -192,7 +193,7 @@ public interface S3AsyncClient {
@QueryParams(keys = "max-keys", values = "0")
@ExceptionParser(ReturnFalseOnContainerNotFound.class)
ListenableFuture<Boolean> bucketExists(
@HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName);
@Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators( { BucketNameValidator.class }) String bucketName);
/**
* @see S3Client#getBucketLocation
@ -202,7 +203,7 @@ public interface S3AsyncClient {
@Path("/")
@XMLResponseParser(LocationConstraintHandler.class)
ListenableFuture<String> getBucketLocation(
@HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName);
@Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators( { BucketNameValidator.class }) String bucketName);
/**
* @see S3Client#getBucketPayer
@ -212,7 +213,7 @@ public interface S3AsyncClient {
@Path("/")
@XMLResponseParser(PayerHandler.class)
ListenableFuture<Payer> getBucketPayer(
@HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName);
@Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators( { BucketNameValidator.class }) String bucketName);
/**
* @see S3Client#setBucketPayer
@ -221,7 +222,7 @@ public interface S3AsyncClient {
@QueryParams(keys = "requestPayment")
@Path("/")
ListenableFuture<Void> setBucketPayer(
@HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName,
@Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators( { BucketNameValidator.class }) String bucketName,
@BinderParam(BindPayerToXmlPayload.class) Payer payer);
/**
@ -231,7 +232,7 @@ public interface S3AsyncClient {
@Path("/")
@XMLResponseParser(ListBucketHandler.class)
ListenableFuture<ListBucketResponse> listBucket(
@HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName,
@Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators( { BucketNameValidator.class }) String bucketName,
ListBucketOptions... options);
/**
@ -240,6 +241,7 @@ public interface S3AsyncClient {
@GET
@XMLResponseParser(ListAllMyBucketsHandler.class)
@Path("/")
@VirtualHost
ListenableFuture<? extends SortedSet<BucketMetadata>> listOwnedBuckets();
/**
@ -252,7 +254,7 @@ public interface S3AsyncClient {
ListenableFuture<ObjectMetadata> copyObject(
@PathParam("sourceBucket") String sourceBucket,
@PathParam("sourceObject") String sourceObject,
@HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String destinationBucket,
@Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators( { BucketNameValidator.class }) String destinationBucket,
@PathParam("destinationObject") String destinationObject, CopyObjectOptions... options);
/**
@ -264,7 +266,7 @@ public interface S3AsyncClient {
@ExceptionParser(ThrowContainerNotFoundOn404.class)
@Path("/")
ListenableFuture<AccessControlList> getBucketACL(
@HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName);
@Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators( { BucketNameValidator.class }) String bucketName);
/**
* @see S3Client#putBucketACL
@ -273,7 +275,7 @@ public interface S3AsyncClient {
@Path("/")
@QueryParams(keys = "acl")
ListenableFuture<Boolean> putBucketACL(
@HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName,
@Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators( { BucketNameValidator.class }) String bucketName,
@BinderParam(BindACLToXMLPayload.class) AccessControlList acl);
/**
@ -285,7 +287,7 @@ public interface S3AsyncClient {
@XMLResponseParser(AccessControlListHandler.class)
@ExceptionParser(ThrowKeyNotFoundOn404.class)
ListenableFuture<AccessControlList> getObjectACL(
@HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName,
@Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators( { BucketNameValidator.class }) String bucketName,
@PathParam("key") String key);
/**
@ -295,7 +297,7 @@ public interface S3AsyncClient {
@QueryParams(keys = "acl")
@Path("{key}")
ListenableFuture<Boolean> putObjectACL(
@HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName,
@Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators( { BucketNameValidator.class }) String bucketName,
@PathParam("key") String key,
@BinderParam(BindACLToXMLPayload.class) AccessControlList acl);
@ -308,7 +310,7 @@ public interface S3AsyncClient {
@ExceptionParser(ThrowContainerNotFoundOn404.class)
@Path("/")
ListenableFuture<BucketLogging> getBucketLogging(
@HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName);
@Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators( { BucketNameValidator.class }) String bucketName);
/**
* @see S3Client#enableBucketLogging
@ -317,7 +319,7 @@ public interface S3AsyncClient {
@Path("/")
@QueryParams(keys = "logging")
ListenableFuture<Void> enableBucketLogging(
@HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName,
@Bucket @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators( { BucketNameValidator.class }) String bucketName,
@BinderParam(BindBucketLoggingToXmlPayload.class) BucketLogging logging);
/**
@ -326,7 +328,8 @@ public interface S3AsyncClient {
@PUT
@Path("/")
@QueryParams(keys = "logging")
@Produces(MediaType.TEXT_XML)
ListenableFuture<Void> disableBucketLogging(
@BinderParam(BindNoBucketLoggingToXmlPayload.class) @HostPrefixParam @ParamValidators( { BucketNameValidator.class }) String bucketName);
@Bucket @BinderParam(BindNoBucketLoggingToXmlPayload.class) @ParamValidators( { BucketNameValidator.class }) String bucketName);
}

View File

@ -21,11 +21,12 @@ package org.jclouds.aws.s3;
import static org.jclouds.Constants.PROPERTY_API_VERSION;
import static org.jclouds.Constants.PROPERTY_ENDPOINT;
import static org.jclouds.Constants.PROPERTY_RELAX_HOSTNAME;
import static org.jclouds.aws.reference.AWSConstants.PROPERTY_AUTH_TAG;
import static org.jclouds.aws.reference.AWSConstants.PROPERTY_DEFAULT_REGIONS;
import static org.jclouds.aws.reference.AWSConstants.PROPERTY_HEADER_TAG;
import static org.jclouds.aws.reference.AWSConstants.PROPERTY_REGIONS;
import static org.jclouds.aws.s3.reference.S3Constants.PROPERTY_S3_AUTH_TAG;
import static org.jclouds.aws.s3.reference.S3Constants.PROPERTY_S3_HEADER_TAG;
import static org.jclouds.aws.s3.reference.S3Constants.PROPERTY_S3_SERVICE_EXPR;
import static org.jclouds.aws.s3.reference.S3Constants.PROPERTY_S3_SERVICE_PATH;
import static org.jclouds.aws.s3.reference.S3Constants.PROPERTY_S3_VIRTUAL_HOST_BUCKETS;
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_USER_METADATA_PREFIX;
@ -47,9 +48,10 @@ public class S3PropertiesBuilder extends PropertiesBuilder {
protected Properties defaultProperties() {
Properties properties = super.defaultProperties();
properties.setProperty(PROPERTY_API_VERSION, S3AsyncClient.VERSION);
properties.setProperty(PROPERTY_S3_AUTH_TAG, "AWS");
properties.setProperty(PROPERTY_S3_HEADER_TAG, "amz");
properties.setProperty(PROPERTY_S3_SERVICE_EXPR, "\\.s3[^.]*\\.amazonaws\\.com");
properties.setProperty(PROPERTY_AUTH_TAG, "AWS");
properties.setProperty(PROPERTY_HEADER_TAG, "amz");
properties.setProperty(PROPERTY_S3_SERVICE_PATH, "/");
properties.setProperty(PROPERTY_S3_VIRTUAL_HOST_BUCKETS, "true");
properties.setProperty(PROPERTY_RELAX_HOSTNAME, "true");
addEndpoints(properties);
properties.setProperty(PROPERTY_BLOBSTORE_DIRECTORY_SUFFIX, DIRECTORY_SUFFIX_FOLDER);
@ -90,7 +92,7 @@ public class S3PropertiesBuilder extends PropertiesBuilder {
protected void setMetaPrefix() {
if (properties.getProperty(PROPERTY_USER_METADATA_PREFIX) == null) {
properties.setProperty(PROPERTY_USER_METADATA_PREFIX, String.format("x-%s-meta-",
properties.getProperty(PROPERTY_S3_HEADER_TAG)));
properties.getProperty(PROPERTY_HEADER_TAG)));
}
}

View File

@ -1,8 +1,29 @@
/**
*
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed 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.Constants.PROPERTY_API_VERSION;
import static org.jclouds.Constants.PROPERTY_ENDPOINT;
import static org.jclouds.aws.reference.AWSConstants.PROPERTY_DEFAULT_REGIONS;
import static org.jclouds.aws.reference.AWSConstants.PROPERTY_REGIONS;
import static org.jclouds.aws.s3.reference.S3Constants.PROPERTY_S3_SERVICE_PATH;
import static org.jclouds.aws.s3.reference.S3Constants.PROPERTY_S3_VIRTUAL_HOST_BUCKETS;
import java.util.Properties;
@ -16,9 +37,12 @@ public class WalrusPropertiesBuilder extends S3PropertiesBuilder {
protected Properties addEndpoints(Properties properties) {
properties.setProperty(PROPERTY_REGIONS, "Walrus");
properties.setProperty(PROPERTY_DEFAULT_REGIONS, "Walrus");
properties.setProperty(PROPERTY_API_VERSION, "Walrus-1.6");
properties.setProperty(PROPERTY_ENDPOINT, "http://ecc.eucalyptus.com:8773/services/Walrus");
properties.setProperty(PROPERTY_ENDPOINT + ".Walrus",
"http://ecc.eucalyptus.com:8773/services/Walrus");
properties.setProperty(PROPERTY_S3_SERVICE_PATH, "/services/Walrus");
properties.setProperty(PROPERTY_S3_VIRTUAL_HOST_BUCKETS, "false");
return properties;
}

View File

@ -0,0 +1,81 @@
/**
*
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed 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 org.jclouds.aws.s3.reference.S3Constants.PROPERTY_S3_SERVICE_PATH;
import static org.jclouds.aws.s3.reference.S3Constants.PROPERTY_S3_VIRTUAL_HOST_BUCKETS;
import static org.jclouds.http.HttpUtils.urlEncode;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Provider;
import javax.inject.Singleton;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.UriBuilder;
import org.jclouds.aws.s3.S3AsyncClient;
import org.jclouds.http.HttpRequest;
import org.jclouds.rest.Binder;
import org.jclouds.rest.annotations.SkipEncoding;
import org.jclouds.rest.binders.BindAsHostPrefix;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
/**
*
* @author Adrian Cole
*/
@Singleton
public class BindAsHostPrefixIfConfigured implements Binder {
private final Provider<UriBuilder> uriBuilderProvider;
private final BindAsHostPrefix bindAsHostPrefix;
private final boolean isVhostStyle;
private final String servicePath;
@Inject
public BindAsHostPrefixIfConfigured(BindAsHostPrefix bindAsHostPrefix,
@Named(PROPERTY_S3_VIRTUAL_HOST_BUCKETS) boolean isVhostStyle,
@Named(PROPERTY_S3_SERVICE_PATH) String servicePath,
Provider<UriBuilder> uriBuilderProvider) {
this.bindAsHostPrefix = bindAsHostPrefix;
this.isVhostStyle = isVhostStyle;
this.servicePath = servicePath;
this.uriBuilderProvider = uriBuilderProvider;
}
public void bindToRequest(HttpRequest request, Object payload) {
if (isVhostStyle) {
bindAsHostPrefix.bindToRequest(request, payload);
request.getHeaders().replaceValues(HttpHeaders.HOST,
ImmutableSet.of(request.getEndpoint().getHost()));
} else {
UriBuilder builder = uriBuilderProvider.get().uri(request.getEndpoint());
StringBuilder path = new StringBuilder(urlEncode(request.getEndpoint().getPath(),
S3AsyncClient.class.getAnnotation(SkipEncoding.class).value()));
int indexToInsert = path.indexOf(servicePath);
indexToInsert = indexToInsert == -1 ? 0 : indexToInsert;
indexToInsert += servicePath.length();
path.insert(indexToInsert, "/" + payload.toString());
builder.replacePath(path.toString());
request.setEndpoint(builder.buildFromEncodedMap(Maps.<String, Object> newHashMap()));
}
}
}

View File

@ -18,8 +18,8 @@
*/
package org.jclouds.aws.s3.binders;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.ws.rs.core.MediaType;
import org.jclouds.http.HttpRequest;
import org.jclouds.rest.Binder;
@ -30,11 +30,17 @@ import org.jclouds.rest.Binder;
*/
@Singleton
public class BindNoBucketLoggingToXmlPayload implements Binder {
private final BindAsHostPrefixIfConfigured bindAsHostPrefixIfConfigured;
@Inject
BindNoBucketLoggingToXmlPayload(BindAsHostPrefixIfConfigured bindAsHostPrefixIfConfigured) {
this.bindAsHostPrefixIfConfigured = bindAsHostPrefixIfConfigured;
}
public void bindToRequest(HttpRequest request, Object payload) {
bindAsHostPrefixIfConfigured.bindToRequest(request, payload);
String stringPayload = "<BucketLoggingStatus xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\"/>";
request.setPayload(stringPayload);
request.getPayload().setContentType(MediaType.TEXT_XML);
}
}

View File

@ -69,7 +69,7 @@ public class S3RestClientModule extends AWSRestClientModule<S3Client, S3AsyncCli
@Provides
@Singleton
RequestSigner provideRequestSigner(RequestAuthorizeSignature in) {
protected RequestSigner provideRequestSigner(RequestAuthorizeSignature in) {
return in;
}
@ -79,7 +79,7 @@ public class S3RestClientModule extends AWSRestClientModule<S3Client, S3AsyncCli
@Provides
@TimeStamp
@Singleton
Supplier<String> provideTimeStampCache(@Named(Constants.PROPERTY_SESSION_INTERVAL) long seconds,
protected Supplier<String> provideTimeStampCache(@Named(Constants.PROPERTY_SESSION_INTERVAL) long seconds,
final DateService dateService) {
return new ExpirableSupplier<String>(new Supplier<String>() {
public String get() {

View File

@ -18,9 +18,17 @@
*/
package org.jclouds.aws.s3.filters;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkArgument;
import static org.jclouds.Constants.PROPERTY_CREDENTIAL;
import static org.jclouds.Constants.PROPERTY_IDENTITY;
import static org.jclouds.aws.reference.AWSConstants.PROPERTY_AUTH_TAG;
import static org.jclouds.aws.reference.AWSConstants.PROPERTY_HEADER_TAG;
import static org.jclouds.aws.s3.reference.S3Constants.PROPERTY_S3_SERVICE_PATH;
import static org.jclouds.aws.s3.reference.S3Constants.PROPERTY_S3_VIRTUAL_HOST_BUCKETS;
import static org.jclouds.util.Patterns.NEWLINE_PATTERN;
import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
@ -34,7 +42,7 @@ import javax.inject.Singleton;
import javax.ws.rs.core.HttpHeaders;
import org.jclouds.Constants;
import org.jclouds.aws.s3.reference.S3Constants;
import org.jclouds.aws.s3.Bucket;
import org.jclouds.date.TimeStamp;
import org.jclouds.encryption.EncryptionService;
import org.jclouds.http.HttpException;
@ -44,10 +52,13 @@ import org.jclouds.http.HttpUtils;
import org.jclouds.http.internal.SignatureWire;
import org.jclouds.logging.Logger;
import org.jclouds.rest.RequestSigner;
import org.jclouds.rest.internal.GeneratedHttpRequest;
import org.jclouds.util.Utils;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
/**
* Signs the S3 request.
@ -75,18 +86,21 @@ public class RequestAuthorizeSignature implements HttpRequestFilter, RequestSign
private final String authTag;
private final String headerTag;
private final String srvExpr;
private final String servicePath;
private final boolean isVhostStyle;
@Inject
public RequestAuthorizeSignature(SignatureWire signatureWire,
@Named(S3Constants.PROPERTY_S3_AUTH_TAG) String authTag,
@Named(S3Constants.PROPERTY_S3_SERVICE_EXPR) String srvExpr,
@Named(S3Constants.PROPERTY_S3_HEADER_TAG) String headerTag,
@Named(Constants.PROPERTY_IDENTITY) String accessKey,
@Named(Constants.PROPERTY_CREDENTIAL) String secretKey,
@Named(PROPERTY_AUTH_TAG) String authTag,
@Named(PROPERTY_S3_VIRTUAL_HOST_BUCKETS) boolean isVhostStyle,
@Named(PROPERTY_S3_SERVICE_PATH) String servicePath,
@Named(PROPERTY_HEADER_TAG) String headerTag,
@Named(PROPERTY_IDENTITY) String accessKey,
@Named(PROPERTY_CREDENTIAL) String secretKey,
@TimeStamp Provider<String> timeStampProvider, EncryptionService encryptionService,
HttpUtils utils) {
this.srvExpr = srvExpr;
this.isVhostStyle = isVhostStyle;
this.servicePath = servicePath;
this.headerTag = headerTag;
this.authTag = authTag;
this.signatureWire = signatureWire;
@ -112,15 +126,15 @@ public class RequestAuthorizeSignature implements HttpRequestFilter, RequestSign
appendPayloadMetadata(request, buffer);
appendHttpHeaders(request, buffer);
appendAmzHeaders(request, buffer);
appendBucketName(request, buffer);
if (isVhostStyle)
appendBucketName(request, buffer);
appendUriPath(request, buffer);
if (signatureWire.enabled())
signatureWire.output(buffer.toString());
return buffer.toString();
}
private void calculateAndReplaceAuthHeader(HttpRequest request, String toSign)
throws HttpException {
void calculateAndReplaceAuthHeader(HttpRequest request, String toSign) throws HttpException {
String signature = sign(toSign);
if (signatureWire.enabled())
signatureWire.input(Utils.toInputStream(signature));
@ -139,16 +153,16 @@ public class RequestAuthorizeSignature implements HttpRequestFilter, RequestSign
return signature;
}
private void appendMethod(HttpRequest request, StringBuilder toSign) {
void appendMethod(HttpRequest request, StringBuilder toSign) {
toSign.append(request.getMethod()).append("\n");
}
private void replaceDateHeader(HttpRequest request) {
void replaceDateHeader(HttpRequest request) {
request.getHeaders().replaceValues(HttpHeaders.DATE,
Collections.singletonList(timeStampProvider.get()));
}
private void appendAmzHeaders(HttpRequest request, StringBuilder toSign) {
void appendAmzHeaders(HttpRequest request, StringBuilder toSign) {
Set<String> headers = new TreeSet<String>(request.getHeaders().keySet());
for (String header : headers) {
if (header.startsWith("x-" + headerTag + "-")) {
@ -162,7 +176,7 @@ public class RequestAuthorizeSignature implements HttpRequestFilter, RequestSign
}
}
private void appendPayloadMetadata(HttpRequest request, StringBuilder buffer) {
void appendPayloadMetadata(HttpRequest request, StringBuilder buffer) {
buffer.append(
utils.valueOrEmpty(request.getPayload() == null ? null : request.getPayload()
.getContentMD5())).append("\n");
@ -171,19 +185,33 @@ public class RequestAuthorizeSignature implements HttpRequestFilter, RequestSign
.getContentType())).append("\n");
}
private void appendHttpHeaders(HttpRequest request, StringBuilder toSign) {
void appendHttpHeaders(HttpRequest request, StringBuilder toSign) {
for (String header : firstHeadersToSign)
toSign.append(valueOrEmpty(request.getHeaders().get(header))).append("\n");
}
@VisibleForTesting
void appendBucketName(HttpRequest request, StringBuilder toSign) {
String hostHeader = request.getFirstHeaderOrNull(HttpHeaders.HOST);
if (hostHeader == null)
hostHeader = checkNotNull(request.getEndpoint().getHost(),
"request.getEndPoint().getHost()");
if (hostHeader.matches(".*" + srvExpr))
toSign.append("/").append(hostHeader.replaceAll(srvExpr, ""));
void appendBucketName(HttpRequest req, StringBuilder toSign) {
checkArgument(req instanceof GeneratedHttpRequest<?>,
"this should be a generated http request");
GeneratedHttpRequest<?> request = GeneratedHttpRequest.class.cast(req);
String bucketName = null;
for (int i = 0; i < request.getJavaMethod().getParameterAnnotations().length; i++) {
if (Iterables.any(Arrays.asList(request.getJavaMethod().getParameterAnnotations()[i]),
new Predicate<Annotation>() {
public boolean apply(Annotation input) {
return input.annotationType().equals(Bucket.class);
}
})) {
bucketName = (String) request.getArgs()[i];
break;
}
}
if (bucketName != null)
toSign.append(servicePath).append(bucketName);
}
@VisibleForTesting

View File

@ -22,9 +22,9 @@ import javax.inject.Inject;
import org.jclouds.aws.s3.domain.MutableObjectMetadata;
import org.jclouds.aws.s3.domain.S3Object;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.rest.InvocationContext;
import org.jclouds.rest.internal.GeneratedHttpRequest;
import com.google.common.base.Function;
@ -57,8 +57,10 @@ public class ParseObjectFromHeadersAndHttpContent implements Function<HttpRespon
return object;
}
public void setContext(GeneratedHttpRequest<?> request) {
@Override
public ParseObjectFromHeadersAndHttpContent setContext(HttpRequest request) {
metadataParser.setContext(request);
return this;
}
}

View File

@ -18,20 +18,21 @@
*/
package org.jclouds.aws.s3.functions;
import static org.jclouds.blobstore.reference.BlobStoreConstants.PROPERTY_USER_METADATA_PREFIX;
import static org.jclouds.http.HttpUtils.attemptToParseSizeAndRangeFromHeaders;
import javax.inject.Inject;
import javax.inject.Named;
import javax.ws.rs.core.HttpHeaders;
import org.jclouds.aws.s3.blobstore.functions.BlobToObjectMetadata;
import org.jclouds.aws.s3.domain.MutableObjectMetadata;
import org.jclouds.aws.s3.reference.S3Headers;
import org.jclouds.blobstore.domain.BlobMetadata;
import org.jclouds.blobstore.functions.ParseSystemAndUserMetadataFromHeaders;
import org.jclouds.encryption.EncryptionService;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.rest.InvocationContext;
import org.jclouds.rest.internal.GeneratedHttpRequest;
import org.jclouds.util.Utils;
import com.google.common.annotations.VisibleForTesting;
@ -49,13 +50,16 @@ public class ParseObjectMetadataFromHeaders implements
private final ParseSystemAndUserMetadataFromHeaders blobMetadataParser;
private final BlobToObjectMetadata blobToObjectMetadata;
private final EncryptionService encryptionService;
private final String userMdPrefix;
@Inject
public ParseObjectMetadataFromHeaders(ParseSystemAndUserMetadataFromHeaders blobMetadataParser,
BlobToObjectMetadata blobToObjectMetadata, EncryptionService encryptionService) {
BlobToObjectMetadata blobToObjectMetadata, EncryptionService encryptionService,
@Named(PROPERTY_USER_METADATA_PREFIX) String userMdPrefix) {
this.blobMetadataParser = blobMetadataParser;
this.blobToObjectMetadata = blobToObjectMetadata;
this.encryptionService = encryptionService;
this.userMdPrefix = userMdPrefix;
}
/**
@ -80,15 +84,17 @@ public class ParseObjectMetadataFromHeaders implements
@VisibleForTesting
protected void addETagTo(HttpResponse from, MutableObjectMetadata metadata) {
if (metadata.getETag() == null) {
String eTagHeader = from.getFirstHeaderOrNull(S3Headers.AMZ_MD5);
String eTagHeader = from.getFirstHeaderOrNull(userMdPrefix + "object-eTag");
if (eTagHeader != null) {
metadata.setETag(eTagHeader);
}
}
}
public void setContext(GeneratedHttpRequest<?> request) {
@Override
public ParseObjectMetadataFromHeaders setContext(HttpRequest request) {
blobMetadataParser.setContext(request);
return this;
}
}

View File

@ -20,11 +20,13 @@ package org.jclouds.aws.s3.options;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static org.jclouds.aws.reference.AWSConstants.PROPERTY_HEADER_TAG;
import static org.jclouds.blobstore.reference.BlobStoreConstants.PROPERTY_USER_METADATA_PREFIX;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.Map;
import java.util.Map.Entry;
import javax.inject.Inject;
import javax.inject.Named;
@ -70,11 +72,8 @@ import com.google.common.collect.Multimap;
*/
public class CopyObjectOptions extends BaseHttpRequestOptions {
private final static DateService dateService = new SimpleDateFormatDateService();
public static final CopyObjectOptions NONE = new CopyObjectOptions();
private Map<String, String> metadata;
private CannedAccessPolicy acl = CannedAccessPolicy.PRIVATE;
private String metadataPrefix;
@ -84,6 +83,13 @@ public class CopyObjectOptions extends BaseHttpRequestOptions {
this.metadataPrefix = metadataPrefix;
}
private String headerTag;
@Inject
public void setHeaderTag(@Named(PROPERTY_HEADER_TAG) String headerTag) {
this.headerTag = headerTag;
}
/**
* Override the default ACL (private) with the specified one.
*
@ -251,15 +257,18 @@ public class CopyObjectOptions extends BaseHttpRequestOptions {
@Override
public Multimap<String, String> buildRequestHeaders() {
checkState(headerTag != null, "headerTag should have been injected!");
checkState(metadataPrefix != null, "metadataPrefix should have been injected!");
Multimap<String, String> returnVal = LinkedHashMultimap.create();
returnVal.putAll(headers);
for (Entry<String, String> entry : headers.entries()) {
returnVal.put(entry.getKey().replace("aws", headerTag), entry.getValue());
}
if (metadata != null) {
for (String key : metadata.keySet()) {
returnVal.put(key.startsWith(metadataPrefix) ? key : metadataPrefix + key, metadata
.get(key));
}
returnVal.put("x-amz-metadata-directive", "REPLACE");
returnVal.put("x-" + headerTag + "-metadata-directive", "REPLACE");
}
return returnVal;
}

View File

@ -19,11 +19,21 @@
package org.jclouds.aws.s3.options;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static org.jclouds.aws.reference.AWSConstants.PROPERTY_HEADER_TAG;
import java.util.Map.Entry;
import javax.inject.Inject;
import javax.inject.Named;
import org.jclouds.aws.s3.domain.CannedAccessPolicy;
import org.jclouds.aws.s3.reference.S3Headers;
import org.jclouds.http.options.BaseHttpRequestOptions;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Multimap;
/**
* Contains options supported in the REST API for the PUT bucket operation. <h2>
* Usage</h2> The recommended way to instantiate a PutBucketOptions object is to statically import
@ -47,6 +57,23 @@ import org.jclouds.http.options.BaseHttpRequestOptions;
public class PutBucketOptions extends BaseHttpRequestOptions {
private CannedAccessPolicy acl = CannedAccessPolicy.PRIVATE;
private String headerTag;
@Inject
public void setHeaderTag(@Named(PROPERTY_HEADER_TAG) String headerTag) {
this.headerTag = headerTag;
}
@Override
public Multimap<String, String> buildRequestHeaders() {
checkState(headerTag != null, "headerTag should have been injected!");
Multimap<String, String> returnVal = LinkedHashMultimap.create();
for (Entry<String, String> entry : headers.entries()) {
returnVal.put(entry.getKey().replace("aws", headerTag), entry.getValue());
}
return returnVal;
}
/**
* Override the default ACL (private) with the specified one.
*

View File

@ -18,11 +18,20 @@
*/
package org.jclouds.aws.s3.options;
import java.util.Map.Entry;
import javax.inject.Inject;
import javax.inject.Named;
import org.jclouds.aws.s3.domain.CannedAccessPolicy;
import org.jclouds.aws.s3.reference.S3Headers;
import org.jclouds.http.options.BaseHttpRequestOptions;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Multimap;
import static com.google.common.base.Preconditions.*;
import static org.jclouds.aws.reference.AWSConstants.PROPERTY_HEADER_TAG;
/**
* Contains options supported in the REST API for the PUT object operation.
@ -51,6 +60,22 @@ public class PutObjectOptions extends BaseHttpRequestOptions {
private CannedAccessPolicy acl = CannedAccessPolicy.PRIVATE;
private String headerTag;
@Inject
public void setHeaderTag(@Named(PROPERTY_HEADER_TAG) String headerTag) {
this.headerTag = headerTag;
}
@Override
public Multimap<String, String> buildRequestHeaders() {
checkState(headerTag != null, "headerTag should have been injected!");
Multimap<String, String> returnVal = LinkedHashMultimap.create();
for (Entry<String, String> entry : headers.entries()) {
returnVal.put(entry.getKey().replace("aws", headerTag), entry.getValue());
}
return returnVal;
}
/**
* Override the default ACL (private) with the specified one.
*

View File

@ -33,10 +33,7 @@ public interface S3Constants {
public static final String MARKER = "marker";
public static final String MAX_KEYS = "max-keys";
public static final String DELIMITER = "delimiter";
public static final String PROPERTY_S3_SERVICE_EXPR = "jclouds.service.expr";
public static final String PROPERTY_S3_AUTH_TAG = "jclouds.s3.auth.tag";
public static final String PROPERTY_S3_HEADER_TAG = "jclouds.s3.header.tag";
public static final String PROPERTY_S3_SERVICE_PATH = "jclouds.s3.service-path";
public static final String PROPERTY_S3_VIRTUAL_HOST_BUCKETS = "jclouds.s3.virtual-host-buckets";
}

View File

@ -36,7 +36,5 @@ public interface S3Headers {
*/
public static final String CANNED_ACL = "x-amz-acl";
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

@ -21,20 +21,9 @@ package org.jclouds.aws.s3.util;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.regex.Pattern;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.jclouds.aws.domain.AWSError;
import org.jclouds.aws.s3.S3Client;
import org.jclouds.aws.s3.reference.S3Headers;
import org.jclouds.aws.util.AWSUtils;
import org.jclouds.http.HttpCommand;
import org.jclouds.http.HttpException;
import org.jclouds.http.HttpResponse;
import org.jclouds.util.Patterns;
/**
@ -42,27 +31,8 @@ import org.jclouds.util.Patterns;
*
* @author Adrian Cole
*/
@Singleton
public class S3Utils {
@Inject
AWSUtils util;
public AWSError parseAWSErrorFromContent(HttpCommand command, HttpResponse response,
InputStream content) throws HttpException {
AWSError error = util.parseAWSErrorFromContent(command.getRequest(), response, content);
if (error.getRequestId() == null)
error.setRequestId(response.getFirstHeaderOrNull(S3Headers.REQUEST_ID));
error.setRequestToken(response.getFirstHeaderOrNull(S3Headers.REQUEST_TOKEN));
return error;
}
public AWSError parseAWSErrorFromContent(HttpCommand command, HttpResponse response,
String content) throws HttpException {
return parseAWSErrorFromContent(command, response, new ByteArrayInputStream(content
.getBytes()));
}
public static final Pattern BUCKET_NAME_PATTERN = Pattern.compile("^[a-z0-9][-_.a-z0-9]+");
// TODO add validatorparam so that this is actually used

View File

@ -20,6 +20,8 @@ package org.jclouds.aws.sqs;
import static org.jclouds.Constants.PROPERTY_API_VERSION;
import static org.jclouds.Constants.PROPERTY_ENDPOINT;
import static org.jclouds.aws.reference.AWSConstants.PROPERTY_AUTH_TAG;
import static org.jclouds.aws.reference.AWSConstants.PROPERTY_HEADER_TAG;
import static org.jclouds.aws.reference.AWSConstants.PROPERTY_REGIONS;
import java.util.Properties;
@ -38,6 +40,8 @@ public class SQSPropertiesBuilder extends PropertiesBuilder {
@Override
protected Properties defaultProperties() {
Properties properties = super.defaultProperties();
properties.setProperty(PROPERTY_AUTH_TAG, "AWS");
properties.setProperty(PROPERTY_HEADER_TAG, "amz");
properties.setProperty(PROPERTY_API_VERSION, SQSAsyncClient.VERSION);
properties.setProperty(PROPERTY_REGIONS, Joiner.on(',').join(Region.US_EAST_1,
Region.US_WEST_1, Region.EU_WEST_1, Region.AP_SOUTHEAST_1));

View File

@ -18,19 +18,22 @@
*/
package org.jclouds.aws.util;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import static org.jclouds.aws.reference.AWSConstants.PROPERTY_HEADER_TAG;
import javax.annotation.Resource;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Provider;
import javax.inject.Singleton;
import org.jclouds.aws.domain.AWSError;
import org.jclouds.aws.xml.ErrorHandler;
import org.jclouds.http.HttpException;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.http.functions.ParseSax;
import org.jclouds.http.functions.ParseSax.Factory;
import org.jclouds.logging.Logger;
import org.jclouds.rest.RequestSigner;
/**
@ -43,27 +46,43 @@ public class AWSUtils {
private final RequestSigner signer;
private final ParseSax.Factory factory;
private final Provider<ErrorHandler> errorHandlerProvider;
private final String requestId;
private final String requestToken;
@Resource
protected Logger logger = Logger.NULL;
@Inject
AWSUtils(RequestSigner signer, Factory factory, Provider<ErrorHandler> errorHandlerProvider) {
AWSUtils(@Named(PROPERTY_HEADER_TAG) String headerTag, RequestSigner signer, Factory factory,
Provider<ErrorHandler> errorHandlerProvider) {
this.signer = signer;
this.factory = factory;
this.errorHandlerProvider = errorHandlerProvider;
this.requestId = String.format("x-%s-request-id", headerTag);
this.requestToken = String.format("x-%s-id-2", headerTag);
}
public AWSError parseAWSErrorFromContent(HttpRequest request, HttpResponse response,
InputStream content) {
AWSError error = (AWSError) factory.create(errorHandlerProvider.get()).parse(content);
if ("SignatureDoesNotMatch".equals(error.getCode())) {
error.setStringSigned(signer.createStringToSign(request));
error.setSignature(signer.sign(error.getStringSigned()));
public AWSError parseAWSErrorFromContent(HttpRequest request, HttpResponse response) {
// HEAD has no content
if (response.getPayload() == null)
return null;
// Eucalyptus and Walrus occasionally return text/plain
if (response.getPayload().getContentType() != null
&& response.getPayload().getContentType().indexOf("text") != -1)
return null;
try {
AWSError error = (AWSError) factory.create(errorHandlerProvider.get()).setContext(request)
.apply(response);
if (error.getRequestId() == null)
error.setRequestId(response.getFirstHeaderOrNull(requestId));
error.setRequestToken(response.getFirstHeaderOrNull(requestToken));
if ("SignatureDoesNotMatch".equals(error.getCode())) {
error.setStringSigned(signer.createStringToSign(request));
error.setSignature(signer.sign(error.getStringSigned()));
}
return error;
} catch (HttpException e) {
logger.warn(e, "error parsing error");
return null;
}
return error;
}
public AWSError parseAWSErrorFromContent(HttpRequest request, HttpResponse response,
String content) {
return parseAWSErrorFromContent(request, response, new ByteArrayInputStream(content
.getBytes()));
}
}

View File

@ -5,6 +5,7 @@ import static org.easymock.EasyMock.reportMatcher;
import static org.easymock.classextension.EasyMock.createMock;
import static org.easymock.classextension.EasyMock.replay;
import static org.easymock.classextension.EasyMock.verify;
import static org.jclouds.aws.reference.AWSConstants.PROPERTY_HEADER_TAG;
import java.net.URI;
@ -22,6 +23,7 @@ import org.testng.annotations.Test;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.name.Names;
/**
*
@ -57,7 +59,7 @@ public class ParseAWSErrorFromXmlContentTest {
@Override
protected void configure() {
bind(RequestSigner.class).toInstance(createMock(RequestSigner.class));
bindConstant().annotatedWith(Names.named(PROPERTY_HEADER_TAG)).to("amz");
}
}).getInstance(ParseAWSErrorFromXmlContent.class);

View File

@ -0,0 +1,74 @@
/**
*
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed 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.testng.Assert.assertEquals;
import java.io.IOException;
import java.util.Properties;
import org.jclouds.aws.s3.blobstore.functions.BlobToObject;
import org.jclouds.aws.s3.filters.RequestAuthorizeSignature;
import org.jclouds.http.HttpRequest;
import org.jclouds.rest.RestClientTest;
import org.jclouds.rest.RestContextFactory;
import org.jclouds.rest.RestContextFactory.ContextSpec;
import org.jclouds.rest.internal.RestAnnotationProcessor;
import org.testng.annotations.BeforeClass;
import com.google.inject.TypeLiteral;
public abstract class BaseS3AsyncClientTest extends RestClientTest<S3AsyncClient> {
protected BlobToObject blobToS3Object;
protected RequestAuthorizeSignature filter;
@Override
protected void checkFilters(HttpRequest request) {
assertEquals(request.getFilters().size(), 1);
assertEquals(request.getFilters().get(0).getClass(), RequestAuthorizeSignature.class);
}
@Override
protected TypeLiteral<RestAnnotationProcessor<S3AsyncClient>> createTypeLiteral() {
return new TypeLiteral<RestAnnotationProcessor<S3AsyncClient>>() {
};
}
@BeforeClass
@Override
protected void setupFactory() throws IOException {
super.setupFactory();
blobToS3Object = injector.getInstance(BlobToObject.class);
filter = injector.getInstance(RequestAuthorizeSignature.class);
}
public BaseS3AsyncClientTest() {
super();
}
@Override
public ContextSpec<?, ?> createContextSpec() {
return new RestContextFactory().createContextSpec("s3", "identity", "credential",
new Properties());
}
}

View File

@ -23,10 +23,8 @@ import static org.testng.Assert.assertEquals;
import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.Properties;
import org.jclouds.aws.domain.Region;
import org.jclouds.aws.s3.blobstore.functions.BlobToObject;
import org.jclouds.aws.s3.config.S3RestClientModule;
import org.jclouds.aws.s3.domain.AccessControlList;
import org.jclouds.aws.s3.domain.BucketLogging;
@ -36,7 +34,6 @@ import org.jclouds.aws.s3.domain.S3Object;
import org.jclouds.aws.s3.domain.AccessControlList.EmailAddressGrantee;
import org.jclouds.aws.s3.domain.AccessControlList.Grant;
import org.jclouds.aws.s3.domain.AccessControlList.Permission;
import org.jclouds.aws.s3.filters.RequestAuthorizeSignature;
import org.jclouds.aws.s3.functions.ParseObjectFromHeadersAndHttpContent;
import org.jclouds.aws.s3.functions.ParseObjectMetadataFromHeaders;
import org.jclouds.aws.s3.functions.ReturnFalseIfBucketAlreadyOwnedByYou;
@ -61,25 +58,19 @@ import org.jclouds.blobstore.functions.ThrowKeyNotFoundOn404;
import org.jclouds.date.TimeStamp;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.RequiresHttp;
import org.jclouds.http.functions.ReleasePayloadAndReturn;
import org.jclouds.http.functions.ParseETagHeader;
import org.jclouds.http.functions.ParseSax;
import org.jclouds.http.functions.ReleasePayloadAndReturn;
import org.jclouds.http.functions.ReturnTrueIf2xx;
import org.jclouds.http.options.GetOptions;
import org.jclouds.rest.ConfiguresRestClient;
import org.jclouds.rest.RestClientTest;
import org.jclouds.rest.RestContextFactory;
import org.jclouds.rest.RestContextFactory.ContextSpec;
import org.jclouds.rest.functions.ReturnVoidOnNotFoundOr404;
import org.jclouds.rest.internal.RestAnnotationProcessor;
import org.jclouds.util.Utils;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableSet;
import com.google.inject.Module;
import com.google.inject.TypeLiteral;
/**
* Tests behavior of {@code S3Client}
@ -87,7 +78,7 @@ import com.google.inject.TypeLiteral;
* @author Adrian Cole
*/
@Test(groups = "unit", testName = "s3.S3ClientTest")
public class S3AsyncClientTest extends RestClientTest<S3AsyncClient> {
public class S3AsyncClientTest extends BaseS3AsyncClientTest {
public void testAllRegions() throws SecurityException, NoSuchMethodException, IOException {
Method method = S3AsyncClient.class.getMethod("putBucketInRegion", String.class,
@ -228,8 +219,7 @@ public class S3AsyncClientTest extends RestClientTest<S3AsyncClient> {
assertRequestLineEquals(request,
"PUT https://destinationbucket.s3.amazonaws.com/destinationObject HTTP/1.1");
assertNonPayloadHeadersEqual(
request,
assertNonPayloadHeadersEqual(request,
"Host: destinationbucket.s3.amazonaws.com\nx-amz-copy-source: /sourceBucket/sourceObject\n");
assertPayloadEquals(request, null, null, false);
@ -416,8 +406,7 @@ public class S3AsyncClientTest extends RestClientTest<S3AsyncClient> {
HttpRequest request = processor.createRequest(method, "EU", "bucket");
assertRequestLineEquals(request, "PUT https://bucket.s3.amazonaws.com/ HTTP/1.1");
assertNonPayloadHeadersEqual(request,
"Host: bucket.s3.amazonaws.com\n");
assertNonPayloadHeadersEqual(request, "Host: bucket.s3.amazonaws.com\n");
assertPayloadEquals(
request,
"<CreateBucketConfiguration><LocationConstraint>EU</LocationConstraint></CreateBucketConfiguration>",
@ -434,7 +423,7 @@ public class S3AsyncClientTest extends RestClientTest<S3AsyncClient> {
IllegalArgumentException, NoSuchMethodException, IOException {
Method method = S3AsyncClient.class.getMethod("putObject", String.class, S3Object.class,
Array.newInstance(PutObjectOptions.class, 0).getClass());
PutObjectOptions[].class);
HttpRequest request = processor.createRequest(method, "bucket", blobToS3Object
.apply(BindBlobToMultipartFormTest.TEST_BLOB));
@ -522,37 +511,9 @@ public class S3AsyncClientTest extends RestClientTest<S3AsyncClient> {
checkFilters(request);
}
BlobToObject blobToS3Object;
RequestAuthorizeSignature filter;
@Override
protected void checkFilters(HttpRequest request) {
assertEquals(request.getFilters().size(), 1);
assertEquals(request.getFilters().get(0).getClass(), RequestAuthorizeSignature.class);
}
@Override
protected TypeLiteral<RestAnnotationProcessor<S3AsyncClient>> createTypeLiteral() {
return new TypeLiteral<RestAnnotationProcessor<S3AsyncClient>>() {
};
}
@BeforeClass
@Override
protected void setupFactory() throws IOException {
super.setupFactory();
blobToS3Object = injector.getInstance(BlobToObject.class);
filter = injector.getInstance(RequestAuthorizeSignature.class);
}
@Override
protected Module createModule() {
return new TestS3RestClientModule();
}
@RequiresHttp
@ConfiguresRestClient
private static final class TestS3RestClientModule extends S3RestClientModule {
protected static final class TestS3RestClientModule extends S3RestClientModule {
@Override
protected void configure() {
super.configure();
@ -565,9 +526,8 @@ public class S3AsyncClientTest extends RestClientTest<S3AsyncClient> {
}
@Override
public ContextSpec<?, ?> createContextSpec() {
return new RestContextFactory().createContextSpec("s3", "identity", "credential",
new Properties());
protected Module createModule() {
return new TestS3RestClientModule();
}
}

View File

@ -0,0 +1,81 @@
/**
*
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed 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 org.jclouds.aws.s3.reference.S3Constants.PROPERTY_S3_SERVICE_PATH;
import static org.jclouds.aws.s3.reference.S3Constants.PROPERTY_S3_VIRTUAL_HOST_BUCKETS;
import static org.testng.Assert.assertEquals;
import java.io.IOException;
import java.net.URI;
import java.util.Properties;
import org.jclouds.aws.s3.BaseS3AsyncClientTest;
import org.jclouds.http.HttpRequest;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
/**
* Tests behavior of {@code BindAsHostPrefixIfConfigured}
*
* @author Adrian Cole
*/
@Test(groups = "unit", testName = "s3.BindAsHostPrefixIfConfiguredTest")
public class BindAsHostPrefixIfConfiguredTest extends BaseS3AsyncClientTest {
public void testBucket() throws IOException {
HttpRequest request = new HttpRequest("GET", URI.create("http://euc/services/Walrus"));
BindAsHostPrefixIfConfigured binder = injector
.getInstance(BindAsHostPrefixIfConfigured.class);
binder.bindToRequest(request, "bucket");
assertEquals(request.getRequestLine(), "GET http://euc/services/Walrus/bucket HTTP/1.1");
}
@Test(dataProvider = "objects")
public void testObject(String key) throws InterruptedException {
HttpRequest request = new HttpRequest("GET", URI.create("http://euc/services/Walrus/object"));
BindAsHostPrefixIfConfigured binder = injector
.getInstance(BindAsHostPrefixIfConfigured.class);
binder.bindToRequest(request, "bucket");
assertEquals(request.getRequestLine(),
"GET http://euc/services/Walrus/bucket/object HTTP/1.1");
}
@DataProvider(name = "objects")
public Object[][] createData() {
return new Object[][] { { "normal" }, { "sp ace" }, { "qu?stion" }, { "unic₪de" },
{ "path/foo" }, { "colon:" }, { "asteri*k" }, { "quote\"" }, { "{great<r}" },
{ "lesst>en" }, { "p|pe" } };
}
@Override
protected Properties getProperties() {
Properties properties = new Properties();
properties.setProperty(PROPERTY_S3_SERVICE_PATH, "/services/Walrus");
properties.setProperty(PROPERTY_S3_VIRTUAL_HOST_BUCKETS, "false");
return properties;
}
}

View File

@ -23,8 +23,8 @@ import static org.testng.Assert.assertEquals;
import java.io.IOException;
import java.net.URI;
import org.jclouds.aws.s3.BaseS3AsyncClientTest;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.functions.BaseHandlerTest;
import org.testng.annotations.Test;
/**
@ -33,7 +33,7 @@ import org.testng.annotations.Test;
* @author Adrian Cole
*/
@Test(groups = "unit", testName = "s3.BindNoBucketLoggingToXmlPayloadTest")
public class BindNoBucketLoggingToXmlPayloadTest extends BaseHandlerTest {
public class BindNoBucketLoggingToXmlPayloadTest extends BaseS3AsyncClientTest {
public void testApplyInputStream() throws IOException {
@ -41,8 +41,7 @@ public class BindNoBucketLoggingToXmlPayloadTest extends BaseHandlerTest {
BindNoBucketLoggingToXmlPayload binder = injector
.getInstance(BindNoBucketLoggingToXmlPayload.class);
binder.bindToRequest(request, null);
assertEquals(request.getPayload().getContentType(), "text/xml");
binder.bindToRequest(request, "bucket");
assertEquals(request.getPayload().getRawContent(),
"<BucketLoggingStatus xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\"/>");

View File

@ -18,43 +18,33 @@
*/
package org.jclouds.aws.s3.filters;
import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
import static org.testng.Assert.assertEquals;
import java.io.IOException;
import java.net.URI;
import java.util.Properties;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.core.HttpHeaders;
import org.jclouds.Constants;
import org.jclouds.aws.s3.BaseS3AsyncClientTest;
import org.jclouds.aws.s3.S3AsyncClient;
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.options.PutObjectOptions;
import org.jclouds.blobstore.binders.BindBlobToMultipartFormTest;
import org.jclouds.http.HttpRequest;
import org.jclouds.logging.config.NullLoggingModule;
import org.jclouds.rest.RestContextFactory;
import org.jclouds.rest.BaseRestClientTest.MockModule;
import org.testng.annotations.BeforeClass;
import org.jclouds.rest.internal.GeneratedHttpRequest;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableSet;
import com.google.inject.Injector;
@Test(groups = "unit", testName = "s3.RequestAuthorizeSignatureTest")
public class RequestAuthorizeSignatureTest {
private RequestAuthorizeSignature filter;
public class RequestAuthorizeSignatureTest extends BaseS3AsyncClientTest {
@DataProvider(parallel = true)
public Object[][] dataProvider() {
return new Object[][] {
{ new HttpRequest(HttpMethod.GET, URI.create("http://s3.amazonaws.com:80")) },
{ new HttpRequest(
HttpMethod.GET,
URI
.create("http://adriancole.s3int5.s3-external-3.amazonaws.com:80/testObject")) },
{ new HttpRequest(HttpMethod.GET, URI.create("http://s3.amazonaws.com:80/?acl"))
public Object[][] dataProvider() throws NoSuchMethodException {
return new Object[][] { { listOwnedBuckets() }, { putObject() }, { putBucketAcl() }
} };
};
}
/**
@ -62,7 +52,7 @@ public class RequestAuthorizeSignatureTest {
* this was once per second. If this timestamp update interval is increased, it could make this
* test appear to hang for a long time.
*/
@Test(threadPoolSize = 3, dataProvider = "dataProvider", timeOut = 3000)
@Test(threadPoolSize = 3, dataProvider = "dataProvider", timeOut = 10000)
void testIdempotent(HttpRequest request) {
filter.filter(request);
String signature = request.getFirstHeaderOrNull(HttpHeaders.AUTHORIZATION);
@ -84,70 +74,69 @@ public class RequestAuthorizeSignatureTest {
}
@Test
void testAppendBucketNameHostHeader() {
URI host = URI.create("http://s3.amazonaws.com:80");
HttpRequest request = new HttpRequest(HttpMethod.GET, host);
request.getHeaders().put(HttpHeaders.HOST, "adriancole.s3int5.s3.amazonaws.com");
void testAppendBucketNameHostHeader() throws SecurityException, NoSuchMethodException {
HttpRequest request = processor.createRequest(S3AsyncClient.class.getMethod(
"getBucketLocation", String.class), "bucket");
StringBuilder builder = new StringBuilder();
filter.appendBucketName(request, builder);
assertEquals(builder.toString(), "/adriancole.s3int5");
assertEquals(builder.toString(), "/bucket");
}
@Test
void testAclQueryString() {
URI host = URI.create("http://s3.amazonaws.com:80/?acl");
HttpRequest request = new HttpRequest(HttpMethod.GET, host);
void testAclQueryString() throws SecurityException, NoSuchMethodException {
HttpRequest request = putBucketAcl();
StringBuilder builder = new StringBuilder();
filter.appendUriPath(request, builder);
assertEquals(builder.toString(), "/?acl");
}
private GeneratedHttpRequest<S3AsyncClient> putBucketAcl() throws NoSuchMethodException {
return processor.createRequest(S3AsyncClient.class.getMethod("putBucketACL", String.class,
AccessControlList.class), "bucket", AccessControlList.fromCannedAccessPolicy(
CannedAccessPolicy.PRIVATE, "1234"));
}
// "?acl", "?location", "?logging", or "?torrent"
@Test
void testAppendBucketNameHostHeaderService() {
URI host = URI.create("http://s3.amazonaws.com:80");
HttpRequest request = new HttpRequest(HttpMethod.GET, host);
request.getHeaders().put(HttpHeaders.HOST, "s3.amazonaws.com");
void testAppendBucketNameHostHeaderService() throws SecurityException, NoSuchMethodException {
HttpRequest request = listOwnedBuckets();
StringBuilder builder = new StringBuilder();
filter.appendBucketName(request, builder);
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(), "");
private GeneratedHttpRequest<S3AsyncClient> listOwnedBuckets() throws NoSuchMethodException {
return processor.createRequest(S3AsyncClient.class.getMethod("listOwnedBuckets"));
}
@Test
void testAppendBucketNameURIHost() {
URI host = URI.create("http://adriancole.s3int5.s3-external-3.amazonaws.com:80");
HttpRequest request = new HttpRequest(HttpMethod.GET, host);
void testHeadersGoLowercase() throws SecurityException, NoSuchMethodException {
HttpRequest request = putObject();
StringBuilder builder = new StringBuilder();
filter.appendBucketName(request, builder);
assertEquals(builder.toString(), "/adriancole.s3int5");
filter.appendAmzHeaders(request, builder);
assertEquals(builder.toString(), "x-amz-meta-x-amz-adrian:foo\n");
}
/**
* before class, as we need to ensure that the filter is threadsafe.
*
* @throws IOException
*
*/
@BeforeClass
protected void createFilter() throws IOException {
Properties overrides = new Properties();
overrides.setProperty(Constants.PROPERTY_SESSION_INTERVAL, "1");
Injector injector = new RestContextFactory().createContextBuilder("s3", "foo", "bar",
ImmutableSet.of(new MockModule(), new NullLoggingModule()), overrides)
.buildInjector();
private HttpRequest putObject() throws NoSuchMethodException {
S3Object object = blobToS3Object.apply(BindBlobToMultipartFormTest.TEST_BLOB);
filter = injector.getInstance(RequestAuthorizeSignature.class);
object.getMetadata().getUserMetadata().put("x-amz-Adrian", "foo");
HttpRequest request = processor.createRequest(S3AsyncClient.class.getMethod("putObject",
String.class, S3Object.class, PutObjectOptions[].class), "bucket", object);
return request;
}
@Test
void testAppendBucketNameURIHost() throws SecurityException, NoSuchMethodException {
HttpRequest request = processor.createRequest(S3AsyncClient.class.getMethod(
"getBucketLocation", String.class), "bucket");
assertEquals(request.getEndpoint().getHost(), "bucket.s3.amazonaws.com");
}
protected Properties getProperties() {
Properties overrides= new Properties();
overrides.setProperty(PROPERTY_SESSION_INTERVAL, 1 + "");
return overrides;
}
}

View File

@ -32,7 +32,6 @@ import org.jclouds.aws.s3.blobstore.functions.BlobToObjectMetadata;
import org.jclouds.aws.s3.domain.MutableObjectMetadata;
import org.jclouds.aws.s3.domain.ObjectMetadata.StorageClass;
import org.jclouds.aws.s3.domain.internal.MutableObjectMetadataImpl;
import org.jclouds.aws.s3.reference.S3Headers;
import org.jclouds.blobstore.domain.MutableBlobMetadata;
import org.jclouds.blobstore.domain.internal.MutableBlobMetadataImpl;
import org.jclouds.blobstore.functions.ParseSystemAndUserMetadataFromHeaders;
@ -61,7 +60,7 @@ public class ParseObjectMetadataFromHeadersTest {
http.getHeaders().put("Content-Disposition", "contentDisposition");
http.getHeaders().put(HttpHeaders.CONTENT_ENCODING, "encoding");
ParseObjectMetadataFromHeaders parser = new ParseObjectMetadataFromHeaders(blobParser(http,
"\"abc\""), blobToObjectMetadata, encryptionService);
"\"abc\""), blobToObjectMetadata, encryptionService, "x-amz-meta-");
MutableObjectMetadata response = parser.apply(http);
assertEquals(response, expects);
}
@ -75,9 +74,9 @@ public class ParseObjectMetadataFromHeadersTest {
http.getHeaders().put(HttpHeaders.CACHE_CONTROL, "cacheControl");
http.getHeaders().put("Content-Disposition", "contentDisposition");
http.getHeaders().put(HttpHeaders.CONTENT_ENCODING, "encoding");
http.getHeaders().put(S3Headers.AMZ_MD5, "\"abc\"");
http.getHeaders().put("x-amz-meta-object-eTag", "\"abc\"");
ParseObjectMetadataFromHeaders parser = new ParseObjectMetadataFromHeaders(blobParser(http,
null), blobToObjectMetadata, encryptionService);
null), blobToObjectMetadata, encryptionService, "x-amz-meta-");
MutableObjectMetadata response = parser.apply(http);
assertEquals(response, expects);
}

View File

@ -70,6 +70,7 @@ public class CopyObjectOptionsTest {
void testGoodMetaStatic() {
CopyObjectOptions options = overrideMetadataWith(goodMeta);
options.setMetadataPrefix("x-amz-meta-");
options.setHeaderTag("amz");
assertGoodMeta(options);
}
@ -92,6 +93,7 @@ public class CopyObjectOptionsTest {
@Test
void testGoodMeta() {
CopyObjectOptions options = new CopyObjectOptions();
options.setHeaderTag("amz");
options.setMetadataPrefix("x-amz-meta-");
options.overrideMetadataWith(goodMeta);
assertGoodMeta(options);
@ -269,6 +271,8 @@ public class CopyObjectOptionsTest {
@Test
void testBuildRequestHeadersWhenMetadataNull() throws UnsupportedEncodingException {
CopyObjectOptions options = new CopyObjectOptions();
options.setHeaderTag("amz");
options.setMetadataPrefix("x-amz-meta-");
assert options.buildRequestHeaders() != null;
}
@ -277,6 +281,8 @@ public class CopyObjectOptionsTest {
void testBuildRequestHeaders() throws UnsupportedEncodingException {
CopyObjectOptions options = ifSourceModifiedSince(now).ifSourceETagDoesntMatch(etag)
.overrideMetadataWith(goodMeta);
options.setHeaderTag("amz");
options.setMetadataPrefix("x-amz-meta-");
Multimap<String, String> headers = options.buildRequestHeaders();
@ -304,6 +310,8 @@ public class CopyObjectOptionsTest {
@Test
void testBuildRequestHeadersACL() throws UnsupportedEncodingException {
CopyObjectOptions options = overrideAcl(CannedAccessPolicy.AUTHENTICATED_READ);
options.setHeaderTag("amz");
options.setMetadataPrefix("x-amz-meta-");
Multimap<String, String> headers = options.buildRequestHeaders();

View File

@ -52,8 +52,10 @@ public class PutBucketOptionsTest {
@Test
void testBuildRequestHeaders() throws UnsupportedEncodingException {
Multimap<String, String> headers = withBucketAcl(CannedAccessPolicy.AUTHENTICATED_READ)
.buildRequestHeaders();
PutBucketOptions options = withBucketAcl(CannedAccessPolicy.AUTHENTICATED_READ);
options.setHeaderTag("amz");
Multimap<String, String> headers = options.buildRequestHeaders();
assertEquals(headers.get(S3Headers.CANNED_ACL).iterator().next(),
CannedAccessPolicy.AUTHENTICATED_READ.toString());
}

View File

@ -18,18 +18,17 @@
*/
package org.jclouds.aws.s3.options;
import com.google.common.collect.Multimap;
import static org.jclouds.aws.s3.options.PutObjectOptions.Builder.withAcl;
import org.jclouds.aws.s3.domain.CannedAccessPolicy;
import org.jclouds.aws.s3.options.PutObjectOptions;
import org.jclouds.aws.s3.reference.S3Headers;
import static org.testng.Assert.assertEquals;
import org.testng.annotations.Test;
import java.io.UnsupportedEncodingException;
import org.jclouds.aws.s3.domain.CannedAccessPolicy;
import org.jclouds.aws.s3.reference.S3Headers;
import org.testng.annotations.Test;
import com.google.common.collect.Multimap;
/**
* Tests possible uses of PutObjectOptions and PutObjectOptions.Builder.*
*
@ -38,24 +37,26 @@ import java.io.UnsupportedEncodingException;
@Test(groups = "unit", testName = "s3.PutObjectOptionsTest")
public class PutObjectOptionsTest {
@Test
public void testAclDefault() {
PutObjectOptions options = new PutObjectOptions();
assertEquals(options.getAcl(), CannedAccessPolicy.PRIVATE);
}
@Test
public void testAclDefault() {
PutObjectOptions options = new PutObjectOptions();
assertEquals(options.getAcl(), CannedAccessPolicy.PRIVATE);
}
@Test
public void testAclStatic() {
PutObjectOptions options = withAcl(CannedAccessPolicy.AUTHENTICATED_READ);
assertEquals(options.getAcl(), CannedAccessPolicy.AUTHENTICATED_READ);
}
@Test
public void testAclStatic() {
PutObjectOptions options = withAcl(CannedAccessPolicy.AUTHENTICATED_READ);
assertEquals(options.getAcl(), CannedAccessPolicy.AUTHENTICATED_READ);
}
@Test
void testBuildRequestHeaders() throws UnsupportedEncodingException {
@Test
void testBuildRequestHeaders() throws UnsupportedEncodingException {
Multimap<String, String> headers = withAcl(
CannedAccessPolicy.AUTHENTICATED_READ).buildRequestHeaders();
assertEquals(headers.get(S3Headers.CANNED_ACL).iterator().next(),
CannedAccessPolicy.AUTHENTICATED_READ.toString());
}
PutObjectOptions options = withAcl(CannedAccessPolicy.AUTHENTICATED_READ);
options.setHeaderTag("amz");
Multimap<String, String> headers = options.buildRequestHeaders();
assertEquals(headers.get(S3Headers.CANNED_ACL).iterator().next(),
CannedAccessPolicy.AUTHENTICATED_READ.toString());
}
}

View File

@ -16,7 +16,7 @@
* limitations under the License.
* ====================================================================
*/
package org.jclouds.aws.s3.util;
package org.jclouds.aws.util;
import static org.easymock.EasyMock.expect;
import static org.easymock.classextension.EasyMock.createMock;
@ -28,16 +28,13 @@ import java.io.InputStream;
import java.util.Properties;
import org.jclouds.aws.domain.AWSError;
import org.jclouds.aws.s3.reference.S3Headers;
import org.jclouds.http.HttpCommand;
import org.jclouds.http.HttpException;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.http.Payloads;
import org.jclouds.logging.config.NullLoggingModule;
import org.jclouds.rest.RestContextFactory;
import org.jclouds.rest.BaseRestClientTest.MockModule;
import org.jclouds.util.Utils;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
@ -50,10 +47,9 @@ import com.google.inject.Injector;
*
* @author Adrian Cole
*/
@Test(sequential = true, groups = { "unit" }, testName = "s3.S3UtilsTest")
public class S3UtilsTest {
S3Utils utils = null;
private HttpResponse response;
@Test(sequential = true, groups = { "unit" }, testName = "aws.AWSUtilsTest")
public class AWSUtilsTest {
AWSUtils utils = null;
private HttpCommand command;
@BeforeTest
@ -63,10 +59,8 @@ public class S3UtilsTest {
ImmutableSet.of(new MockModule(), new NullLoggingModule()), new Properties())
.buildInjector();
utils = injector.getInstance(S3Utils.class);
response = new HttpResponse(400, "boa", Payloads.newStringPayload(""));
response.getHeaders().put(S3Headers.REQUEST_ID, "requestid");
response.getHeaders().put(S3Headers.REQUEST_TOKEN, "requesttoken");
utils = injector.getInstance(AWSUtils.class);
command = createMock(HttpCommand.class);
expect(command.getRequest()).andReturn(createMock(HttpRequest.class)).atLeastOnce();
replay(command);
@ -77,14 +71,17 @@ public class S3UtilsTest {
utils = null;
}
@Test
public void testParseAWSErrorFromContentHttpCommandHttpResponseInputStream() {
InputStream content = getClass().getResourceAsStream("/error.xml");
AWSError error = utils.parseAWSErrorFromContent(command, response, content);
validate(error);
HttpResponse response(InputStream content) {
HttpResponse response = new HttpResponse(400, "boa", Payloads.newInputStreamPayload(content));
response.getHeaders().put("x-amz-request-id", "requestid");
response.getHeaders().put("x-amz-id-2", "requesttoken");
return response;
}
private void validate(AWSError error) {
@Test
public void testParseAWSErrorFromContentHttpCommandHttpResponseInputStream() {
AWSError error = utils.parseAWSErrorFromContent(command.getRequest(), response(getClass()
.getResourceAsStream("/error.xml")));
assertEquals(error.getCode(), "NoSuchKey");
assertEquals(error.getMessage(), "The resource you requested does not exist");
assertEquals(error.getRequestToken(), "requesttoken");
@ -92,15 +89,6 @@ public class S3UtilsTest {
assertEquals(error.getDetails().get("Resource"), "/mybucket/myfoto.jpg");
}
@Test
public void testParseAWSErrorFromContentHttpCommandHttpResponseString() throws HttpException,
IOException {
InputStream content = getClass().getResourceAsStream("/error.xml");
AWSError error = utils.parseAWSErrorFromContent(command, response, Utils
.toStringAndClose(content));
validate(error);
}
@Test
public void testValidateBucketName() {
// TODO

View File

@ -144,7 +144,7 @@
<category name="jclouds.wire">
<priority value="DEBUG" />
<appender-ref ref="ASYNCWIRE" />
</category>
</category><!--
<category name="jclouds.signature">
<priority value="DEBUG" />
@ -152,7 +152,7 @@
</category>
<!-- ======================= -->
--><!-- ======================= -->
<!-- Setup the Root category -->
<!-- ======================= -->

View File

@ -23,9 +23,9 @@ import javax.inject.Singleton;
import org.jclouds.azure.storage.blob.domain.AzureBlob;
import org.jclouds.azure.storage.blob.domain.MutableBlobProperties;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.rest.InvocationContext;
import org.jclouds.rest.internal.GeneratedHttpRequest;
import com.google.common.base.Function;
@ -57,8 +57,10 @@ public class ParseBlobFromHeadersAndHttpContent implements Function<HttpResponse
return blob;
}
public void setContext(GeneratedHttpRequest<?> request) {
@Override
public ParseBlobFromHeadersAndHttpContent setContext(HttpRequest request) {
metadataParser.setContext(request);
return this;
}
}

View File

@ -27,9 +27,9 @@ import org.jclouds.azure.storage.blob.blobstore.functions.BlobMetadataToBlobProp
import org.jclouds.azure.storage.blob.domain.MutableBlobProperties;
import org.jclouds.blobstore.domain.BlobMetadata;
import org.jclouds.blobstore.functions.ParseSystemAndUserMetadataFromHeaders;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.rest.InvocationContext;
import org.jclouds.rest.internal.GeneratedHttpRequest;
import com.google.common.base.Function;
@ -64,8 +64,10 @@ public class ParseBlobPropertiesFromHeaders implements
return to;
}
public void setContext(GeneratedHttpRequest<?> request) {
@Override
public ParseBlobPropertiesFromHeaders setContext(HttpRequest request) {
blobMetadataParser.setContext(request);
return this;
}
}

View File

@ -18,6 +18,8 @@
*/
package org.jclouds.azure.storage.blob.functions;
import static com.google.common.base.Preconditions.checkArgument;
import java.util.Map.Entry;
import javax.inject.Inject;
@ -30,6 +32,7 @@ import org.jclouds.azure.storage.blob.domain.internal.MutableContainerProperties
import org.jclouds.blobstore.reference.BlobStoreConstants;
import org.jclouds.date.DateService;
import org.jclouds.http.HttpException;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.rest.InvocationContext;
import org.jclouds.rest.internal.GeneratedHttpRequest;
@ -99,8 +102,12 @@ public class ParseContainerPropertiesFromHeaders implements
}
}
public void setContext(GeneratedHttpRequest<?> request) {
this.request = request;
@Override
public ParseContainerPropertiesFromHeaders setContext(HttpRequest request) {
checkArgument(request instanceof GeneratedHttpRequest<?>,
"note this handler requires a GeneratedHttpRequest");
this.request = (GeneratedHttpRequest<?>) request;
return this;
}
}

View File

@ -18,11 +18,13 @@
*/
package org.jclouds.blobstore.functions;
import static com.google.common.base.Preconditions.checkArgument;
import static org.jclouds.util.Utils.propagateOrNull;
import org.jclouds.blobstore.BlobStore;
import org.jclouds.blobstore.internal.BlobRuntimeException;
import org.jclouds.blobstore.strategy.ClearContainerStrategy;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponseException;
import org.jclouds.rest.InvocationContext;
import org.jclouds.rest.internal.GeneratedHttpRequest;
@ -64,8 +66,12 @@ public class ClearAndDeleteIfNotEmpty implements Function<Exception, Void>, Invo
return Void.class.cast(propagateOrNull(from));
}
public void setContext(GeneratedHttpRequest<?> request) {
this.request = request;
@Override
public ClearAndDeleteIfNotEmpty setContext(HttpRequest request) {
checkArgument(request instanceof GeneratedHttpRequest<?>,
"note this handler requires a GeneratedHttpRequest");
this.request = (GeneratedHttpRequest<?>) request;
return this;
}
}

View File

@ -22,9 +22,9 @@ import javax.inject.Inject;
import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.domain.MutableBlobMetadata;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.rest.InvocationContext;
import org.jclouds.rest.internal.GeneratedHttpRequest;
import com.google.common.base.Function;
@ -54,8 +54,10 @@ public class ParseBlobFromHeadersAndHttpContent implements Function<HttpResponse
return object;
}
public void setContext(GeneratedHttpRequest<?> request) {
@Override
public ParseBlobFromHeadersAndHttpContent setContext(HttpRequest request) {
metadataParser.setContext(request);
return this;
}
}

View File

@ -18,6 +18,8 @@
*/
package org.jclouds.blobstore.functions;
import static com.google.common.base.Preconditions.checkArgument;
import static org.jclouds.Constants.PROPERTY_API_VERSION;
import static org.jclouds.blobstore.reference.BlobStoreConstants.PROPERTY_USER_METADATA_PREFIX;
import static org.jclouds.blobstore.util.BlobStoreUtils.getKeyFor;
import static org.jclouds.http.HttpUtils.attemptToParseSizeAndRangeFromHeaders;
@ -32,6 +34,7 @@ import javax.ws.rs.core.HttpHeaders;
import org.jclouds.blobstore.domain.MutableBlobMetadata;
import org.jclouds.date.DateService;
import org.jclouds.http.HttpException;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.rest.InvocationContext;
import org.jclouds.rest.internal.GeneratedHttpRequest;
@ -47,14 +50,18 @@ public class ParseSystemAndUserMetadataFromHeaders implements
private final String metadataPrefix;
private final DateService dateParser;
private final Provider<MutableBlobMetadata> metadataFactory;
private final String apiVersion;
private GeneratedHttpRequest<?> request;
@Inject
public ParseSystemAndUserMetadataFromHeaders(Provider<MutableBlobMetadata> metadataFactory,
DateService dateParser, @Named(PROPERTY_USER_METADATA_PREFIX) String metadataPrefix) {
DateService dateParser, @Named(PROPERTY_USER_METADATA_PREFIX) String metadataPrefix,
@Named(PROPERTY_API_VERSION) String apiVersion) {
this.metadataFactory = metadataFactory;
this.dateParser = dateParser;
this.metadataPrefix = metadataPrefix;
this.apiVersion = apiVersion;
}
public MutableBlobMetadata apply(HttpResponse from) {
@ -92,7 +99,13 @@ public class ParseSystemAndUserMetadataFromHeaders implements
if (lastModified == null)
throw new HttpException(HttpHeaders.LAST_MODIFIED + " header not present in response: "
+ from.getStatusLine());
metadata.setLastModified(dateParser.rfc822DateParse(lastModified));
// Eucalyptus 1.6 returns iso8601 dates
if (apiVersion.indexOf("Walrus-1.6") != -1) {
metadata.setLastModified(dateParser.iso8601DateParse(lastModified.replace("+0000", "Z")));
} else {
metadata.setLastModified(dateParser.rfc822DateParse(lastModified));
}
if (metadata.getLastModified() == null)
throw new HttpException("could not parse: " + HttpHeaders.LAST_MODIFIED + ": "
+ lastModified);
@ -122,7 +135,10 @@ public class ParseSystemAndUserMetadataFromHeaders implements
throw new HttpException(HttpHeaders.CONTENT_TYPE + " not found in headers");
}
public void setContext(GeneratedHttpRequest<?> request) {
this.request = request;
public ParseSystemAndUserMetadataFromHeaders setContext(HttpRequest request) {
checkArgument(request instanceof GeneratedHttpRequest<?>,
"note this handler requires a GeneratedHttpRequest");
this.request = (GeneratedHttpRequest<?>) request;
return this;
}
}

View File

@ -58,7 +58,7 @@ public class ParseBlobMetadataFromHeadersTest {
void setUp() {
parser = new ParseSystemAndUserMetadataFromHeaders(blobMetadataProvider,
new SimpleDateFormatDateService(), "prefix");
new SimpleDateFormatDateService(), "prefix", "default");
GeneratedHttpRequest<?> request = createMock(GeneratedHttpRequest.class);
expect(request.getEndpoint()).andReturn(URI.create("http://localhost/key")).anyTimes();

View File

@ -487,8 +487,8 @@ public class HttpUtils {
return formBuilder.toString();
}
public void setPayloadPropertiesFromHeaders(Multimap<String, String> headers, HttpMessage request) {
Payload payload = request.getPayload();
public void setPayloadPropertiesFromHeaders(Multimap<String, String> headers, HttpMessage message) {
Payload payload = message.getPayload();
boolean chunked = any(headers.entries(), new Predicate<Entry<String, String>>() {
@Override
public boolean apply(Entry<String, String> input) {
@ -508,32 +508,32 @@ public class HttpUtils {
if (payload != null)
payload.setContentType(header.getValue());
} else {
request.getHeaders().put(header.getKey(), header.getValue());
message.getHeaders().put(header.getKey(), header.getValue());
}
}
String contentRange = request.getFirstHeaderOrNull("Content-Range");
if (contentRange != null) {
payload.setContentLength(Long.parseLong(contentRange.substring(0, contentRange
.lastIndexOf('/'))));
if (message instanceof HttpRequest) {
checkArgument(
message.getPayload() == null
|| message.getFirstHeaderOrNull(CONTENT_TYPE) == null,
"configuration error please use request.getPayload().setContentType(value) as opposed to adding a content type header: "
+ message);
checkArgument(
message.getPayload() == null
|| message.getFirstHeaderOrNull(CONTENT_LENGTH) == null,
"configuration error please use request.getPayload().setContentLength(value) as opposed to adding a content length header: "
+ message);
checkArgument(message.getPayload() == null
|| message.getPayload().getContentLength() != null
|| "chunked".equalsIgnoreCase(message.getFirstHeaderOrNull("Transfer-Encoding")),
"either chunked encoding must be set on the http request or contentlength set on the payload: "
+ message);
checkArgument(
message.getPayload() == null
|| message.getFirstHeaderOrNull("Content-MD5") == null,
"configuration error please use request.getPayload().setContentMD5(value) as opposed to adding a content md5 header: "
+ message);
}
checkArgument(
request.getPayload() == null || request.getFirstHeaderOrNull(CONTENT_TYPE) == null,
"configuration error please use request.getPayload().setContentType(value) as opposed to adding a content type header: "
+ request);
checkArgument(
request.getPayload() == null || request.getFirstHeaderOrNull(CONTENT_LENGTH) == null,
"configuration error please use request.getPayload().setContentLength(value) as opposed to adding a content length header: "
+ request);
checkArgument(request.getPayload() == null || request.getPayload().getContentLength() != null
|| "chunked".equalsIgnoreCase(request.getFirstHeaderOrNull("Transfer-Encoding")),
"either chunked encoding must be set on the http request or contentlength set on the payload: "
+ request);
checkArgument(
request.getPayload() == null || request.getFirstHeaderOrNull("Content-MD5") == null,
"configuration error please use request.getPayload().setContentMD5(value) as opposed to adding a content md5 header: "
+ request);
}
public static void releasePayload(HttpResponse from) {

View File

@ -22,10 +22,10 @@ import static org.jclouds.http.HttpUtils.releasePayload;
import javax.annotation.Resource;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.logging.Logger;
import org.jclouds.rest.InvocationContext;
import org.jclouds.rest.internal.GeneratedHttpRequest;
import com.google.common.base.Function;
@ -38,16 +38,16 @@ public class ParseContentMD5FromHeaders implements Function<HttpResponse, byte[]
public static class NoContentMD5Exception extends RuntimeException {
private static final long serialVersionUID = 1L;
private final GeneratedHttpRequest<?> request;
private final HttpRequest request;
private final HttpResponse response;
public NoContentMD5Exception(GeneratedHttpRequest<?> request, HttpResponse response) {
public NoContentMD5Exception(HttpRequest request, HttpResponse response) {
super(String.format("no MD5 returned from request: %s; response %s", request, response));
this.request = request;
this.response = response;
}
public GeneratedHttpRequest<?> getRequest() {
public HttpRequest getRequest() {
return request;
}
@ -59,7 +59,7 @@ public class ParseContentMD5FromHeaders implements Function<HttpResponse, byte[]
@Resource
protected Logger logger = Logger.NULL;
private GeneratedHttpRequest<?> request;
private HttpRequest request;
public byte[] apply(HttpResponse from) {
releasePayload(from);
@ -69,8 +69,10 @@ public class ParseContentMD5FromHeaders implements Function<HttpResponse, byte[]
throw new NoContentMD5Exception(request, from);
}
public void setContext(GeneratedHttpRequest<?> request) {
@Override
public ParseContentMD5FromHeaders setContext(HttpRequest request) {
this.request = request;
return this;
}
}

View File

@ -18,40 +18,43 @@
*/
package org.jclouds.http.functions;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Throwables.propagate;
import static com.google.common.io.Closeables.closeQuietly;
import java.io.InputStream;
import java.io.StringReader;
import javax.annotation.Resource;
import javax.inject.Inject;
import org.jclouds.http.HttpException;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.logging.Logger;
import org.jclouds.rest.InvocationContext;
import org.jclouds.rest.internal.GeneratedHttpRequest;
import org.jclouds.util.Utils;
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import com.google.common.base.Function;
import com.google.common.base.Throwables;
import com.google.common.io.Closeables;
/**
* This object will parse the body of an HttpResponse and return the result of
* type <T> back to the caller.
* This object will parse the body of an HttpResponse and return the result of type <T> back to the
* caller.
*
* @author Adrian Cole
*/
public class ParseSax<T> implements Function<HttpResponse, T>,
InvocationContext {
public class ParseSax<T> implements Function<HttpResponse, T>, InvocationContext {
private final XMLReader parser;
private final HandlerWithResult<T> handler;
@Resource
protected Logger logger = Logger.NULL;
private GeneratedHttpRequest<?> request;
private HttpRequest request;
public static interface Factory {
<T> ParseSax<T> create(HandlerWithResult<T> handler);
@ -63,34 +66,67 @@ public class ParseSax<T> implements Function<HttpResponse, T>,
this.handler = checkNotNull(handler, "handler");
}
public T apply(HttpResponse from) throws HttpException {
public T apply(HttpResponse from) {
try {
checkNotNull(from, "http response");
checkNotNull(from.getPayload(), "payload in " + from);
} catch (NullPointerException e) {
return addRequestDetailsToException(e);
}
if (from.getStatusCode() >= 300)
return convertStreamToStringAndParse(from);
return parse(from.getPayload().getInput());
}
public T parse(InputStream from) throws HttpException {
if (from == null)
throw new HttpException("No input to parse");
private T convertStreamToStringAndParse(HttpResponse from) {
try {
return parse(Utils.toStringAndClose(from.getPayload().getInput()));
} catch (Exception e) {
return addRequestDetailsToException(e);
}
}
public T parse(String from) {
try {
checkNotNull(from, "xml string");
checkArgument(from.indexOf('<') >= 0, String.format("not an xml document [%s] ",from));
} catch (RuntimeException e) {
return addRequestDetailsToException(e);
}
return parse(new InputSource(new StringReader(from)));
}
public T parse(InputStream from) {
try {
return parse(new InputSource(from));
} finally {
closeQuietly(from);
}
}
public T parse(InputSource from) {
try {
checkNotNull(from, "xml inputsource");
parser.setContentHandler(getHandler());
// This method should accept documents with a BOM (Byte-order mark)
parser.parse(new InputSource(from));
parser.parse(from);
return getHandler().getResult();
} catch (Exception e) {
if (request != null) {
return addRequestDetailsToException(e);
}
}
StringBuilder message = new StringBuilder();
message.append("Error parsing input for ").append(
request.getRequestLine()).append(": ");
message.append(e.getMessage());
logger.error(e, message.toString());
throw new HttpException(message.toString(), e);
} else {
Throwables.propagate(e);
assert false : "should have propagated: " + e;
return null;
}
} finally {
Closeables.closeQuietly(from);
private T addRequestDetailsToException(Exception e) {
if (request != null) {
StringBuilder message = new StringBuilder();
message.append("Error parsing input for ").append(request.getRequestLine()).append(": ");
message.append(e.getMessage());
logger.error(e, message.toString());
throw new HttpException(message.toString(), e);
} else {
propagate(e);
assert false : "should have propagated: " + e;
return null;
}
}
@ -99,26 +135,38 @@ public class ParseSax<T> implements Function<HttpResponse, T>,
}
/**
* Handler that produces a useable domain object accessible after parsing
* completes.
* Handler that produces a useable domain object accessible after parsing completes.
*
* @author Adrian Cole
*/
public abstract static class HandlerWithResult<T> extends DefaultHandler
implements InvocationContext {
protected GeneratedHttpRequest<?> request;
public abstract static class HandlerWithResult<T> extends DefaultHandler implements
InvocationContext {
protected HttpRequest request;
public abstract T getResult();
@Override
public void setContext(GeneratedHttpRequest<?> request) {
public HandlerWithResult<T> setContext(HttpRequest request) {
this.request = request;
return this;
}
}
public abstract static class HandlerForGeneratedRequestWithResult<T> extends
HandlerWithResult<T> {
@Override
public HandlerForGeneratedRequestWithResult<T> setContext(HttpRequest request) {
checkArgument(request instanceof GeneratedHttpRequest<?>,
"note this handler requires a GeneratedHttpRequest");
super.setContext(request);
return this;
}
}
@Override
public void setContext(GeneratedHttpRequest<?> request) {
public ParseSax<T> setContext(HttpRequest request) {
handler.setContext(request);
this.request = request;
return this;
}
}

View File

@ -30,10 +30,10 @@ import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.UriBuilder;
import org.jclouds.http.HttpException;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.http.HttpResponseException;
import org.jclouds.rest.InvocationContext;
import org.jclouds.rest.internal.GeneratedHttpRequest;
import org.jclouds.util.Utils;
import com.google.common.base.Function;
@ -52,7 +52,7 @@ public class ParseURIFromListOrLocationHeaderIf20x implements Function<HttpRespo
this.uriBuilderProvider = uriBuilderProvider;
}
private GeneratedHttpRequest<?> request;
private HttpRequest request;
public URI apply(HttpResponse from) {
if (from.getStatusCode() > 206)
@ -93,7 +93,9 @@ public class ParseURIFromListOrLocationHeaderIf20x implements Function<HttpRespo
}
}
public void setContext(GeneratedHttpRequest<?> request) {
@Override
public ParseURIFromListOrLocationHeaderIf20x setContext(HttpRequest request) {
this.request = request;
return this;
}
}

View File

@ -20,7 +20,7 @@ package org.jclouds.rest;
import javax.ws.rs.PathParam;
import org.jclouds.rest.internal.GeneratedHttpRequest;
import org.jclouds.http.HttpRequest;
/**
* Passes generated Http request into this object;
@ -29,5 +29,5 @@ import org.jclouds.rest.internal.GeneratedHttpRequest;
* @author Adrian Cole
*/
public interface InvocationContext {
void setContext(GeneratedHttpRequest<?> request);
Object setContext(HttpRequest request);
}

View File

@ -0,0 +1,59 @@
/**
*
* Copyright (C) 2009 Cloud Conscious, LLC. <info@cloudconscious.com>
*
* ====================================================================
* Licensed 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.rest.binders;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.net.InternetDomainName.isValid;
import javax.inject.Inject;
import javax.inject.Provider;
import javax.inject.Singleton;
import javax.ws.rs.core.UriBuilder;
import org.jclouds.http.HttpRequest;
import org.jclouds.rest.Binder;
import com.google.common.net.InternetDomainName;
/**
*
* @author Adrian Cole
*/
@Singleton
public class BindAsHostPrefix implements Binder {
private final Provider<UriBuilder> uriBuilderProvider;
@Inject
public BindAsHostPrefix(Provider<UriBuilder> uriBuilderProvider) {
this.uriBuilderProvider = uriBuilderProvider;
}
public void bindToRequest(HttpRequest request, Object payload) {
checkNotNull(payload, "hostprefix");
checkArgument(isValid(request.getEndpoint().getHost()), "this is only valid for hostnames: "
+ request);
UriBuilder builder = uriBuilderProvider.get().uri(request.getEndpoint());
InternetDomainName name = InternetDomainName.from(request.getEndpoint().getHost()).child(
payload.toString());
builder.host(name.name());
request.setEndpoint(builder.build());
}
}

View File

@ -19,7 +19,6 @@
package org.jclouds.rest.internal;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.Collections2.filter;
import static com.google.common.collect.Collections2.transform;
import static com.google.common.collect.Iterables.concat;
@ -100,7 +99,6 @@ import org.jclouds.rest.annotations.EndpointParam;
import org.jclouds.rest.annotations.ExceptionParser;
import org.jclouds.rest.annotations.FormParams;
import org.jclouds.rest.annotations.Headers;
import org.jclouds.rest.annotations.HostPrefixParam;
import org.jclouds.rest.annotations.MapBinder;
import org.jclouds.rest.annotations.MapPayloadParam;
import org.jclouds.rest.annotations.MatrixParams;
@ -150,7 +148,6 @@ public class RestAnnotationProcessor<T> {
private final Map<Method, Map<Integer, Set<Annotation>>> methodToIndexOfParamToDecoratorParamAnnotation = createMethodToIndexOfParamToAnnotation(BinderParam.class);
private final Map<Method, Map<Integer, Set<Annotation>>> methodToIndexOfParamToHeaderParamAnnotations = createMethodToIndexOfParamToAnnotation(HeaderParam.class);
private final Map<Method, Map<Integer, Set<Annotation>>> methodToIndexOfParamToHostPrefixParamAnnotations = createMethodToIndexOfParamToAnnotation(HostPrefixParam.class);
private final Map<Method, Map<Integer, Set<Annotation>>> methodToIndexOfParamToEndpointAnnotations = createMethodToIndexOfParamToAnnotation(Endpoint.class);
private final Map<Method, Map<Integer, Set<Annotation>>> methodToIndexOfParamToEndpointParamAnnotations = createMethodToIndexOfParamToAnnotation(EndpointParam.class);
private final Map<Method, Map<Integer, Set<Annotation>>> methodToIndexOfParamToMatrixParamAnnotations = createMethodToIndexOfParamToAnnotation(MatrixParam.class);
@ -299,7 +296,6 @@ public class RestAnnotationProcessor<T> {
for (int index = 0; index < method.getParameterTypes().length; index++) {
methodToIndexOfParamToDecoratorParamAnnotation.get(method).get(index);
methodToIndexOfParamToHeaderParamAnnotations.get(method).get(index);
methodToIndexOfParamToHostPrefixParamAnnotations.get(method).get(index);
methodToIndexOfParamToMatrixParamAnnotations.get(method).get(index);
methodToIndexOfParamToFormParamAnnotations.get(method).get(index);
methodToIndexOfParamToQueryParamAnnotations.get(method).get(index);
@ -385,7 +381,7 @@ public class RestAnnotationProcessor<T> {
}
String httpMethod = getHttpMethodOrConstantOrThrowException(method);
UriBuilder builder = addHostPrefixIfPresent(endpoint, method, args);
UriBuilder builder = uriBuilderProvider.get().uri(endpoint);
Multimap<String, String> tokenValues = LinkedHashMultimap.create();
@ -631,26 +627,6 @@ public class RestAnnotationProcessor<T> {
return null;
}
private UriBuilder addHostPrefixIfPresent(URI endpoint, Method method, Object... args) {
Map<Integer, Set<Annotation>> map = indexWithOnlyOneAnnotation(method, "@HostPrefixParam",
methodToIndexOfParamToHostPrefixParamAnnotations);
UriBuilder builder = uriBuilderProvider.get().uri(endpoint);
if (map.size() == 1) {
HostPrefixParam param = (HostPrefixParam) map.values().iterator().next().iterator().next();
int index = map.keySet().iterator().next();
String prefix = checkNotNull(args[index],
String.format("argument at index %d on method %s", index, method)).toString();
checkArgument(!prefix.equals(""), String.format(
"argument at index %d must be a valid hostname for method %s", index, method));
String joinOn = param.value();
String host = endpoint.getHost();
builder.host(prefix + joinOn + host);
}
return builder;
}
public static final TypeLiteral<ListenableFuture<Boolean>> futureBooleanLiteral = new TypeLiteral<ListenableFuture<Boolean>>() {
};

View File

@ -21,6 +21,7 @@ package org.jclouds.rest;
import static org.jclouds.rest.RestContextFactory.createContextBuilder;
import java.io.IOException;
import java.util.Properties;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.functions.ParseSax;
@ -60,10 +61,13 @@ public abstract class RestClientTest<T> extends BaseRestClientTest {
protected void setupFactory() throws IOException {
ContextSpec<?, ?> contextSpec = createContextSpec();
injector = createContextBuilder(contextSpec,
ImmutableSet.of(new MockModule(), new NullLoggingModule(), createModule()))
.buildInjector();
ImmutableSet.of(new MockModule(), new NullLoggingModule(), createModule()),
getProperties()).buildInjector();
parserFactory = injector.getInstance(ParseSax.Factory.class);
processor = injector.getInstance(Key.get(createTypeLiteral()));
}
protected Properties getProperties() {
return new Properties();
}
}

View File

@ -109,7 +109,6 @@ import org.jclouds.rest.annotations.Endpoint;
import org.jclouds.rest.annotations.EndpointParam;
import org.jclouds.rest.annotations.FormParams;
import org.jclouds.rest.annotations.Headers;
import org.jclouds.rest.annotations.HostPrefixParam;
import org.jclouds.rest.annotations.MapBinder;
import org.jclouds.rest.annotations.MapPayloadParam;
import org.jclouds.rest.annotations.MatrixParams;
@ -121,6 +120,7 @@ import org.jclouds.rest.annotations.RequestFilters;
import org.jclouds.rest.annotations.ResponseParser;
import org.jclouds.rest.annotations.SkipEncoding;
import org.jclouds.rest.annotations.VirtualHost;
import org.jclouds.rest.binders.BindAsHostPrefix;
import org.jclouds.rest.binders.BindMapToMatrixParams;
import org.jclouds.rest.binders.BindToJsonPayload;
import org.jclouds.rest.binders.BindToStringPayload;
@ -1494,8 +1494,9 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest {
public HttpRequest request;
public void setContext(GeneratedHttpRequest<?> request) {
public ReturnStringIf200Context setContext(HttpRequest request) {
this.request = request;
return this;
}
}
@ -1723,27 +1724,17 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest {
.singletonList("localhost"));
}
public class TestVirtualHost {
public interface TestVirtualHost {
@GET
@Path("/{id}")
@VirtualHost
public ListenableFuture<String> get(@PathParam("id") String id, String foo) {
return null;
}
ListenableFuture<String> get(@PathParam("id") String id, String foo);
@GET
@Path("/{id}")
public ListenableFuture<String> getPrefix(@PathParam("id") String id,
@HostPrefixParam("") String foo) {
return null;
}
ListenableFuture<String> getPrefix(@PathParam("id") String id,
@BinderParam(BindAsHostPrefix.class) String foo);
@GET
@Path("/{id}")
public ListenableFuture<String> getPrefixDot(@PathParam("id") String id,
@HostPrefixParam String foo) {
return null;
}
}
@Test
@ -1762,17 +1753,6 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest {
@Test
public void testHostPrefix() throws SecurityException, NoSuchMethodException {
Method method = TestVirtualHost.class.getMethod("getPrefix", String.class, String.class);
HttpRequest request = factory(TestVirtualHost.class).createRequest(method,
new Object[] { "1", "holy" });
assertEquals(request.getEndpoint().getHost(), "holylocalhost");
assertEquals(request.getEndpoint().getPath(), "/1");
assertEquals(request.getMethod(), HttpMethod.GET);
assertEquals(request.getHeaders().size(), 0);
}
@Test
public void testHostPrefixDot() throws SecurityException, NoSuchMethodException {
Method method = TestVirtualHost.class.getMethod("getPrefixDot", String.class, String.class);
HttpRequest request = factory(TestVirtualHost.class).createRequest(method,
new Object[] { "1", "holy" });
assertEquals(request.getEndpoint().getHost(), "holy.localhost");
@ -1782,15 +1762,9 @@ public class RestAnnotationProcessorTest extends BaseRestClientTest {
}
@Test(expectedExceptions = IllegalArgumentException.class)
public void testHostPrefixDotEmpty() throws SecurityException, NoSuchMethodException {
Method method = TestVirtualHost.class.getMethod("getPrefixDot", String.class, String.class);
factory(TestVirtualHost.class).createRequest(method, new Object[] { "1", "" });
}
@Test(expectedExceptions = NullPointerException.class)
public void testHostPrefixDotNull() throws SecurityException, NoSuchMethodException {
Method method = TestVirtualHost.class.getMethod("getPrefixDot", String.class, String.class);
factory(TestVirtualHost.class).createRequest(method, new Object[] { "1", null });
public void testHostPrefixEmpty() throws SecurityException, NoSuchMethodException {
Method method = TestVirtualHost.class.getMethod("getPrefix", String.class, String.class);
factory(TestVirtualHost.class).createRequest(method, "1", "");
}
public class TestHeaders {

View File

@ -18,12 +18,14 @@
*/
package org.jclouds.mezeo.pcs2.functions;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import java.util.Map;
import javax.inject.Inject;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.http.functions.ReturnStringIf200;
import org.jclouds.rest.InvocationContext;
@ -64,8 +66,12 @@ public class AddMetadataItemIntoMap implements Function<HttpResponse, Void>, Inv
return null;
}
public void setContext(GeneratedHttpRequest<?> request) {
this.request = request;
@Override
public AddMetadataItemIntoMap setContext(HttpRequest request) {
checkArgument(request instanceof GeneratedHttpRequest<?>,
"note this handler requires a GeneratedHttpRequest");
this.request = (GeneratedHttpRequest<?>) request;
return this;
}
}

View File

@ -22,12 +22,12 @@ import static com.google.common.base.Preconditions.checkNotNull;
import java.net.URI;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.rackspace.cloudfiles.domain.AccountMetadata;
import org.jclouds.rackspace.cloudfiles.domain.ContainerCDNMetadata;
import org.jclouds.rackspace.cloudfiles.reference.CloudFilesHeaders;
import org.jclouds.rest.InvocationContext;
import org.jclouds.rest.internal.GeneratedHttpRequest;
import com.google.common.base.Function;
@ -39,7 +39,7 @@ import com.google.common.base.Function;
public class ParseContainerCDNMetadataFromHeaders implements
Function<HttpResponse, ContainerCDNMetadata>, InvocationContext {
private GeneratedHttpRequest<?> request;
private HttpRequest request;
/**
* parses the http response headers to create a new {@link ContainerCDNMetadata} object.
@ -60,7 +60,9 @@ public class ParseContainerCDNMetadataFromHeaders implements
}
}
public void setContext(GeneratedHttpRequest<?> request) {
@Override
public ParseContainerCDNMetadataFromHeaders setContext(HttpRequest request) {
this.request = request;
return this;
}
}

View File

@ -20,11 +20,11 @@ package org.jclouds.rackspace.cloudfiles.functions;
import javax.inject.Inject;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.rackspace.cloudfiles.domain.CFObject;
import org.jclouds.rackspace.cloudfiles.domain.MutableObjectInfoWithMetadata;
import org.jclouds.rest.InvocationContext;
import org.jclouds.rest.internal.GeneratedHttpRequest;
import com.google.common.base.Function;
@ -57,8 +57,9 @@ public class ParseObjectFromHeadersAndHttpContent implements Function<HttpRespon
return object;
}
public void setContext(GeneratedHttpRequest<?> request) {
@Override
public ParseObjectFromHeadersAndHttpContent setContext(HttpRequest request) {
infoParser.setContext(request);
return this;
}
}

View File

@ -25,11 +25,11 @@ import javax.inject.Inject;
import org.jclouds.blobstore.domain.BlobMetadata;
import org.jclouds.blobstore.functions.ParseSystemAndUserMetadataFromHeaders;
import org.jclouds.encryption.EncryptionService;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.rackspace.cloudfiles.blobstore.functions.ResourceToObjectInfo;
import org.jclouds.rackspace.cloudfiles.domain.MutableObjectInfoWithMetadata;
import org.jclouds.rest.InvocationContext;
import org.jclouds.rest.internal.GeneratedHttpRequest;
import org.jclouds.util.Utils;
import com.google.common.base.Function;
@ -68,8 +68,10 @@ public class ParseObjectInfoFromHeaders implements
return to;
}
public void setContext(GeneratedHttpRequest<?> request) {
@Override
public ParseObjectInfoFromHeaders setContext(HttpRequest request) {
blobMetadataParser.setContext(request);
return this;
}
}

View File

@ -33,6 +33,7 @@ import javax.inject.Inject;
import org.jclouds.blobstore.domain.PageSet;
import org.jclouds.blobstore.domain.internal.PageSetImpl;
import org.jclouds.encryption.EncryptionService;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.functions.ParseJson;
import org.jclouds.rackspace.cloudfiles.domain.ObjectInfo;
import org.jclouds.rackspace.cloudfiles.options.ListContainerOptions;
@ -180,7 +181,11 @@ public class ParseObjectInfoListFromJsonResponse extends ParseJson<PageSet<Objec
}
}
public void setContext(GeneratedHttpRequest<?> request) {
this.request = request;
@Override
public ParseObjectInfoListFromJsonResponse setContext(HttpRequest request) {
checkArgument(request instanceof GeneratedHttpRequest<?>,
"note this handler requires a GeneratedHttpRequest");
this.request = (GeneratedHttpRequest<?>) request;
return this;
}
}

View File

@ -25,6 +25,7 @@ import static org.testng.Assert.assertNotNull;
import java.net.URI;
import org.jclouds.Constants;
import org.jclouds.blobstore.reference.BlobStoreConstants;
import org.jclouds.http.HttpResponse;
import org.jclouds.http.Payloads;
@ -52,6 +53,7 @@ public class ParseObjectInfoFromHeadersTest {
bindConstant()
.annotatedWith(Names.named(BlobStoreConstants.PROPERTY_USER_METADATA_PREFIX)).to(
"sdf");
bindConstant().annotatedWith(Names.named(Constants.PROPERTY_API_VERSION)).to("1");
}
});