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,19 +207,18 @@ 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;
}
@ -236,11 +230,10 @@ public abstract class BaseReservationHandler<T> extends HandlerWithResult<T> {
}
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,
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;
@ -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,21 +59,17 @@ 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())) {
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;
}
} catch (HttpException e) {
logger.warn(e, "error parsing response: %s", new String(content));
}
}
}
return false;

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,15 +56,13 @@ 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));
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())) {
@ -81,11 +78,6 @@ public class AWSRedirectionRetryHandler extends RedirectionRetryHandler {
} else {
return false;
}
} catch (HttpException e) {
logger.error(e, "error on redirect for command %s; response %s; retrying...",
command, response);
return false;
}
}
} else {
return super.shouldRetryRequest(command, response);

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,6 +126,7 @@ public class RequestAuthorizeSignature implements HttpRequestFilter, RequestSign
appendPayloadMetadata(request, buffer);
appendHttpHeaders(request, buffer);
appendAmzHeaders(request, buffer);
if (isVhostStyle)
appendBucketName(request, buffer);
appendUriPath(request, buffer);
if (signatureWire.enabled())
@ -119,8 +134,7 @@ public class RequestAuthorizeSignature implements HttpRequestFilter, RequestSign
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);
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;
}
public AWSError parseAWSErrorFromContent(HttpRequest request, HttpResponse response,
String content) {
return parseAWSErrorFromContent(request, response, new ByteArrayInputStream(content
.getBytes()));
} catch (HttpException e) {
logger.warn(e, "error parsing error");
return null;
}
}
}

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 {
private HttpRequest putObject() throws NoSuchMethodException {
S3Object object = blobToS3Object.apply(BindBlobToMultipartFormTest.TEST_BLOB);
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(Constants.PROPERTY_SESSION_INTERVAL, "1");
Injector injector = new RestContextFactory().createContextBuilder("s3", "foo", "bar",
ImmutableSet.of(new MockModule(), new NullLoggingModule()), overrides)
.buildInjector();
filter = injector.getInstance(RequestAuthorizeSignature.class);
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.*
*
@ -53,8 +52,10 @@ public class PutObjectOptionsTest {
@Test
void testBuildRequestHeaders() throws UnsupportedEncodingException {
Multimap<String, String> headers = withAcl(
CannedAccessPolicy.AUTHENTICATED_READ).buildRequestHeaders();
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());
// 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(
request.getPayload() == null || request.getFirstHeaderOrNull(CONTENT_TYPE) == null,
message.getPayload() == null
|| message.getFirstHeaderOrNull(CONTENT_TYPE) == null,
"configuration error please use request.getPayload().setContentType(value) as opposed to adding a content type header: "
+ request);
+ message);
checkArgument(
request.getPayload() == null || request.getFirstHeaderOrNull(CONTENT_LENGTH) == null,
message.getPayload() == null
|| message.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")),
+ 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: "
+ request);
+ message);
checkArgument(
request.getPayload() == null || request.getFirstHeaderOrNull("Content-MD5") == null,
message.getPayload() == null
|| message.getFirstHeaderOrNull("Content-MD5") == null,
"configuration error please use request.getPayload().setContentMD5(value) as opposed to adding a content md5 header: "
+ request);
+ message);
}
}
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,35 +66,68 @@ 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);
}
}
private T addRequestDetailsToException(Exception e) {
if (request != null) {
StringBuilder message = new StringBuilder();
message.append("Error parsing input for ").append(
request.getRequestLine()).append(": ");
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);
propagate(e);
assert false : "should have propagated: " + e;
return null;
}
} finally {
Closeables.closeQuietly(from);
}
}
public HandlerWithResult<T> getHandler() {
@ -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");
}
});