Issue 73: changed eTag to String, as at least two clouds don't have parsable etags; changed user metadata to Map from Multimap as there are no dupes

git-svn-id: http://jclouds.googlecode.com/svn/trunk@1973 3d8758e0-26b5-11de-8745-db77d3ebf521
This commit is contained in:
adrian.f.cole 2009-10-12 06:10:15 +00:00
parent 04f70ce8f7
commit 33d11fe07f
63 changed files with 379 additions and 392 deletions

View File

@ -179,7 +179,7 @@ public interface S3BlobStore extends BlobStore<BucketMetadata, ObjectMetadata, S
@PUT @PUT
@Path("{key}") @Path("{key}")
@ResponseParser(ParseETagHeader.class) @ResponseParser(ParseETagHeader.class)
Future<byte[]> putBlob( Future<String> putBlob(
@HostPrefixParam String bucketName, @HostPrefixParam String bucketName,
@PathParam("key") @ParamParser(BlobKey.class) @BinderParam(BindS3ObjectToEntity.class) S3Object object); @PathParam("key") @ParamParser(BlobKey.class) @BinderParam(BindS3ObjectToEntity.class) S3Object object);

View File

@ -204,7 +204,7 @@ public interface S3Connection {
@PUT @PUT
@Path("{key}") @Path("{key}")
@ResponseParser(ParseETagHeader.class) @ResponseParser(ParseETagHeader.class)
Future<byte[]> putObject( Future<String> putObject(
@HostPrefixParam String bucketName, @HostPrefixParam String bucketName,
@PathParam("key") @ParamParser(BlobKey.class) @BinderParam(BindS3ObjectToEntity.class) S3Object object, @PathParam("key") @ParamParser(BlobKey.class) @BinderParam(BindS3ObjectToEntity.class) S3Object object,
PutObjectOptions... options); PutObjectOptions... options);

View File

@ -24,7 +24,6 @@
package org.jclouds.aws.s3.domain; package org.jclouds.aws.s3.domain;
import java.io.Serializable; import java.io.Serializable;
import java.util.Arrays;
import org.jclouds.blobstore.domain.BlobMetadata; import org.jclouds.blobstore.domain.BlobMetadata;
@ -52,9 +51,9 @@ public class ObjectMetadata extends BlobMetadata implements Serializable {
dataDisposition).append(", owner=").append(owner).append(", storageClass=").append( dataDisposition).append(", owner=").append(owner).append(", storageClass=").append(
storageClass).append(", allHeaders=").append(allHeaders).append(", dataEncoding=") storageClass).append(", allHeaders=").append(allHeaders).append(", dataEncoding=")
.append(dataEncoding).append(", dataType=").append(dataType).append(", eTag=") .append(dataEncoding).append(", dataType=").append(dataType).append(", eTag=")
.append(Arrays.toString(eTag)).append(", key=").append(key) .append(eTag).append(", key=").append(key).append(", lastModified=").append(
.append(", lastModified=").append(lastModified).append(", size=").append(size) lastModified).append(", size=").append(size).append(", userMetadata=")
.append(", userMetadata=").append(userMetadata).append("]"); .append(userMetadata).append("]");
return builder.toString(); return builder.toString();
} }

View File

@ -77,10 +77,10 @@ public class ParseObjectMetadataFromHeaders extends
if (metadata.getETag() == null) { if (metadata.getETag() == null) {
String eTagHeader = from.getFirstHeaderOrNull(S3Headers.AMZ_MD5); String eTagHeader = from.getFirstHeaderOrNull(S3Headers.AMZ_MD5);
if (eTagHeader != null) { if (eTagHeader != null) {
metadata.setETag(HttpUtils.fromHexString(eTagHeader)); metadata.setETag(eTagHeader);
} }
} }
metadata.setContentMD5(metadata.getETag()); metadata.setContentMD5(HttpUtils.fromHexString(metadata.getETag().replaceAll("\"", "")));
} }
} }

View File

@ -28,10 +28,13 @@ import static com.google.common.base.Preconditions.checkState;
import static org.jclouds.blobstore.reference.BlobStoreConstants.PROPERTY_USER_METADATA_PREFIX; import static org.jclouds.blobstore.reference.BlobStoreConstants.PROPERTY_USER_METADATA_PREFIX;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Named;
import org.jclouds.aws.s3.domain.CannedAccessPolicy; import org.jclouds.aws.s3.domain.CannedAccessPolicy;
import org.jclouds.aws.s3.reference.S3Headers; import org.jclouds.aws.s3.reference.S3Headers;
import org.jclouds.http.HttpUtils;
import org.jclouds.http.options.BaseHttpRequestOptions; import org.jclouds.http.options.BaseHttpRequestOptions;
import org.jclouds.util.DateService; import org.jclouds.util.DateService;
import org.joda.time.DateTime; import org.joda.time.DateTime;
@ -39,8 +42,6 @@ import org.joda.time.DateTime;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.google.common.collect.HashMultimap; import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
import javax.inject.Inject;
import javax.inject.Named;
/** /**
* Contains options supported in the REST API for the COPY object operation. * Contains options supported in the REST API for the COPY object operation.
@ -76,7 +77,7 @@ public class CopyObjectOptions extends BaseHttpRequestOptions {
public static final CopyObjectOptions NONE = new CopyObjectOptions(); public static final CopyObjectOptions NONE = new CopyObjectOptions();
private Multimap<String, String> metadata; private Map<String, String> metadata;
private CannedAccessPolicy acl = CannedAccessPolicy.PRIVATE; private CannedAccessPolicy acl = CannedAccessPolicy.PRIVATE;
@ -149,7 +150,7 @@ public class CopyObjectOptions extends BaseHttpRequestOptions {
* This header can be used with x-amz-copy-source-if-unmodified-since, but cannot be used with * This header can be used with x-amz-copy-source-if-unmodified-since, but cannot be used with
* other conditional copy headers. * other conditional copy headers.
* *
* @see CopyObjectOptions#ifSourceETagMatches(byte[]) * @see CopyObjectOptions#ifSourceETagMatches(String)
*/ */
public String getIfMatch() { public String getIfMatch() {
return getFirstHeaderOrNull("x-amz-copy-source-if-match"); return getFirstHeaderOrNull("x-amz-copy-source-if-match");
@ -164,7 +165,7 @@ public class CopyObjectOptions extends BaseHttpRequestOptions {
* This header can be used with x-amz-copy-source-if-modified-since, but cannot be used with * This header can be used with x-amz-copy-source-if-modified-since, but cannot be used with
* other conditional copy headers. * other conditional copy headers.
* *
* @see CopyObjectOptions#ifSourceETagDoesntMatch(byte[]) * @see CopyObjectOptions#ifSourceETagDoesntMatch(String)
*/ */
public String getIfNoneMatch() { public String getIfNoneMatch() {
return getFirstHeaderOrNull("x-amz-copy-source-if-none-match"); return getFirstHeaderOrNull("x-amz-copy-source-if-none-match");
@ -176,14 +177,14 @@ public class CopyObjectOptions extends BaseHttpRequestOptions {
* *
* @see #overrideMetadataWith(Multimap) * @see #overrideMetadataWith(Multimap)
*/ */
public Multimap<String, String> getMetadata() { public Map<String, String> getMetadata() {
return metadata; return metadata;
} }
/** /**
* Only return the object if it has changed since this time. * Only return the object if it has changed since this time.
* <p/> * <p/>
* Not compatible with {@link #ifSourceETagMatches(byte[])} or * Not compatible with {@link #ifSourceETagMatches(String)} or
* {@link #ifSourceUnmodifiedSince(DateTime)} * {@link #ifSourceUnmodifiedSince(DateTime)}
*/ */
public CopyObjectOptions ifSourceModifiedSince(DateTime ifModifiedSince) { public CopyObjectOptions ifSourceModifiedSince(DateTime ifModifiedSince) {
@ -198,7 +199,7 @@ public class CopyObjectOptions extends BaseHttpRequestOptions {
/** /**
* Only return the object if it hasn't changed since this time. * Only return the object if it hasn't changed since this time.
* <p/> * <p/>
* Not compatible with {@link #ifSourceETagDoesntMatch(byte[])} or * Not compatible with {@link #ifSourceETagDoesntMatch(String)} or
* {@link #ifSourceModifiedSince(DateTime)} * {@link #ifSourceModifiedSince(DateTime)}
*/ */
public CopyObjectOptions ifSourceUnmodifiedSince(DateTime ifUnmodifiedSince) { public CopyObjectOptions ifSourceUnmodifiedSince(DateTime ifUnmodifiedSince) {
@ -215,26 +216,26 @@ public class CopyObjectOptions extends BaseHttpRequestOptions {
* The object's eTag hash should match the parameter <code>eTag</code>. * The object's eTag hash should match the parameter <code>eTag</code>.
* <p/> * <p/>
* <p/> * <p/>
* Not compatible with {@link #ifSourceETagDoesntMatch(byte[])} or * Not compatible with {@link #ifSourceETagDoesntMatch(String)} or
* {@link #ifSourceModifiedSince(DateTime)} * {@link #ifSourceModifiedSince(DateTime)}
* *
* @param eTag * @param eTag
* hash representing the entity * hash representing the entity
*/ */
public CopyObjectOptions ifSourceETagMatches(byte[] eTag) throws UnsupportedEncodingException { public CopyObjectOptions ifSourceETagMatches(String eTag) throws UnsupportedEncodingException {
checkState(getIfNoneMatch() == null, checkState(getIfNoneMatch() == null,
"ifETagDoesntMatch() is not compatible with ifETagMatches()"); "ifETagDoesntMatch() is not compatible with ifETagMatches()");
checkState(getIfModifiedSince() == null, checkState(getIfModifiedSince() == null,
"ifModifiedSince() is not compatible with ifETagMatches()"); "ifModifiedSince() is not compatible with ifETagMatches()");
replaceHeader("x-amz-copy-source-if-match", String.format("\"%1$s\"", HttpUtils replaceHeader("x-amz-copy-source-if-match", String.format("\"%1$s\"", checkNotNull(eTag,
.toHexString(checkNotNull(eTag, "eTag")))); "eTag")));
return this; return this;
} }
/** /**
* The object should not have a eTag hash corresponding with the parameter <code>eTag</code>. * The object should not have a eTag hash corresponding with the parameter <code>eTag</code>.
* <p/> * <p/>
* Not compatible with {@link #ifSourceETagMatches(byte[])} or * Not compatible with {@link #ifSourceETagMatches(String)} or
* {@link #ifSourceUnmodifiedSince(DateTime)} * {@link #ifSourceUnmodifiedSince(DateTime)}
* *
* @param eTag * @param eTag
@ -242,13 +243,13 @@ public class CopyObjectOptions extends BaseHttpRequestOptions {
* @throws UnsupportedEncodingException * @throws UnsupportedEncodingException
* if there was a problem converting this into an S3 eTag string * if there was a problem converting this into an S3 eTag string
*/ */
public CopyObjectOptions ifSourceETagDoesntMatch(byte[] eTag) public CopyObjectOptions ifSourceETagDoesntMatch(String eTag)
throws UnsupportedEncodingException { throws UnsupportedEncodingException {
checkState(getIfMatch() == null, "ifETagMatches() is not compatible with ifETagDoesntMatch()"); checkState(getIfMatch() == null, "ifETagMatches() is not compatible with ifETagDoesntMatch()");
Preconditions.checkState(getIfUnmodifiedSince() == null, Preconditions.checkState(getIfUnmodifiedSince() == null,
"ifUnmodifiedSince() is not compatible with ifETagDoesntMatch()"); "ifUnmodifiedSince() is not compatible with ifETagDoesntMatch()");
replaceHeader("x-amz-copy-source-if-none-match", String.format("\"%1$s\"", HttpUtils replaceHeader("x-amz-copy-source-if-none-match", String.format("\"%1$s\"", checkNotNull(eTag,
.toHexString(checkNotNull(eTag, "ifETagDoesntMatch")))); "ifETagDoesntMatch")));
return this; return this;
} }
@ -259,7 +260,7 @@ public class CopyObjectOptions extends BaseHttpRequestOptions {
returnVal.putAll(headers); returnVal.putAll(headers);
if (metadata != null) { if (metadata != null) {
for (String key : metadata.keySet()) { for (String key : metadata.keySet()) {
returnVal.putAll(key.startsWith(metadataPrefix) ? key : metadataPrefix + key, metadata returnVal.put(key.startsWith(metadataPrefix) ? key : metadataPrefix + key, metadata
.get(key)); .get(key));
} }
returnVal.put("x-amz-metadata-directive", "REPLACE"); returnVal.put("x-amz-metadata-directive", "REPLACE");
@ -270,7 +271,7 @@ public class CopyObjectOptions extends BaseHttpRequestOptions {
/** /**
* Use the provided metadata instead of what is on the source object. * Use the provided metadata instead of what is on the source object.
*/ */
public CopyObjectOptions overrideMetadataWith(Multimap<String, String> metadata) { public CopyObjectOptions overrideMetadataWith(Map<String, String> metadata) {
checkNotNull(metadata, "metadata"); checkNotNull(metadata, "metadata");
this.metadata = metadata; this.metadata = metadata;
return this; return this;
@ -302,18 +303,18 @@ public class CopyObjectOptions extends BaseHttpRequestOptions {
} }
/** /**
* @see CopyObjectOptions#ifSourceETagMatches(byte[]) * @see CopyObjectOptions#ifSourceETagMatches(String)
*/ */
public static CopyObjectOptions ifSourceETagMatches(byte[] eTag) public static CopyObjectOptions ifSourceETagMatches(String eTag)
throws UnsupportedEncodingException { throws UnsupportedEncodingException {
CopyObjectOptions options = new CopyObjectOptions(); CopyObjectOptions options = new CopyObjectOptions();
return options.ifSourceETagMatches(eTag); return options.ifSourceETagMatches(eTag);
} }
/** /**
* @see CopyObjectOptions#ifSourceETagDoesntMatch(byte[]) * @see CopyObjectOptions#ifSourceETagDoesntMatch(String)
*/ */
public static CopyObjectOptions ifSourceETagDoesntMatch(byte[] eTag) public static CopyObjectOptions ifSourceETagDoesntMatch(String eTag)
throws UnsupportedEncodingException { throws UnsupportedEncodingException {
CopyObjectOptions options = new CopyObjectOptions(); CopyObjectOptions options = new CopyObjectOptions();
return options.ifSourceETagDoesntMatch(eTag); return options.ifSourceETagDoesntMatch(eTag);
@ -322,7 +323,7 @@ public class CopyObjectOptions extends BaseHttpRequestOptions {
/** /**
* @see #overrideMetadataWith(Multimap) * @see #overrideMetadataWith(Multimap)
*/ */
public static CopyObjectOptions overrideMetadataWith(Multimap<String, String> metadata) { public static CopyObjectOptions overrideMetadataWith(Map<String, String> metadata) {
CopyObjectOptions options = new CopyObjectOptions(); CopyObjectOptions options = new CopyObjectOptions();
return options.overrideMetadataWith(metadata); return options.overrideMetadataWith(metadata);
} }

View File

@ -23,13 +23,12 @@
*/ */
package org.jclouds.aws.s3.xml; package org.jclouds.aws.s3.xml;
import javax.inject.Inject;
import org.jclouds.aws.s3.domain.ObjectMetadata; import org.jclouds.aws.s3.domain.ObjectMetadata;
import org.jclouds.http.HttpUtils;
import org.jclouds.http.functions.ParseSax; import org.jclouds.http.functions.ParseSax;
import org.jclouds.util.DateService; import org.jclouds.util.DateService;
import javax.inject.Inject;
/** /**
* Parses the response from Amazon S3 COPY Object command. * Parses the response from Amazon S3 COPY Object command.
* <p/> * <p/>
@ -51,7 +50,7 @@ public class CopyObjectHandler extends ParseSax.HandlerWithResult<ObjectMetadata
public void endElement(String uri, String name, String qName) { public void endElement(String uri, String name, String qName) {
if (qName.equals("ETag")) { if (qName.equals("ETag")) {
metadata.setETag(HttpUtils.fromHexString(currentText.toString().replaceAll("\"", ""))); metadata.setETag(currentText.toString());
} else if (qName.equals("LastModified")) { } else if (qName.equals("LastModified")) {
metadata.setLastModified(dateParser.iso8601DateParse(currentText.toString())); metadata.setLastModified(dateParser.iso8601DateParse(currentText.toString()));
} }

View File

@ -93,9 +93,9 @@ public class ListBucketHandler extends ParseSax.HandlerWithResult<ListBucketResp
} else if (qName.equals("LastModified")) { } else if (qName.equals("LastModified")) {
currentObjectMetadata.setLastModified(dateParser.iso8601DateParse(currentText.toString())); currentObjectMetadata.setLastModified(dateParser.iso8601DateParse(currentText.toString()));
} else if (qName.equals("ETag")) { } else if (qName.equals("ETag")) {
currentObjectMetadata.setETag(HttpUtils.fromHexString(currentText.toString().replaceAll( currentObjectMetadata.setETag(currentText.toString());
"\"", ""))); currentObjectMetadata.setContentMD5(HttpUtils.fromHexString(currentObjectMetadata
currentObjectMetadata.setContentMD5(currentObjectMetadata.getETag()); .getETag().replaceAll("\"", "")));
} else if (qName.equals("Size")) { } else if (qName.equals("Size")) {
currentObjectMetadata.setSize(Long.parseLong(currentText.toString())); currentObjectMetadata.setSize(Long.parseLong(currentText.toString()));
} else if (qName.equals("Owner")) { } else if (qName.equals("Owner")) {

View File

@ -45,6 +45,7 @@ import static org.testng.Assert.assertTrue;
import java.io.IOException; import java.io.IOException;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.net.URL; import java.net.URL;
import java.util.Map;
import java.util.SortedSet; import java.util.SortedSet;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -68,8 +69,7 @@ import org.jclouds.util.Utils;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import com.google.common.collect.HashMultimap; import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
/** /**
* *
@ -337,10 +337,11 @@ public class S3ConnectionLiveTest extends
} }
} }
private void addToContainerAndValidate(String containerName, String sourceKey) protected String addToContainerAndValidate(String containerName, String sourceKey)
throws InterruptedException, ExecutionException, TimeoutException, IOException { throws InterruptedException, ExecutionException, TimeoutException, IOException {
addBlobToContainer(containerName, sourceKey); String etag = addBlobToContainer(containerName, sourceKey);
validateContent(containerName, sourceKey); validateContent(containerName, sourceKey);
return etag;
} }
// TODO: fails on linux and windows // TODO: fails on linux and windows
@ -407,7 +408,7 @@ public class S3ConnectionLiveTest extends
String containerName = getContainerName(); String containerName = getContainerName();
String destinationContainer = getContainerName(); String destinationContainer = getContainerName();
try { try {
addToContainerAndValidate(containerName, sourceKey); String goodETag = addToContainerAndValidate(containerName, sourceKey);
context.getApi().copyObject(containerName, sourceKey, destinationContainer, context.getApi().copyObject(containerName, sourceKey, destinationContainer,
destinationKey, ifSourceETagMatches(goodETag)).get(10, TimeUnit.SECONDS); destinationKey, ifSourceETagMatches(goodETag)).get(10, TimeUnit.SECONDS);
@ -415,7 +416,7 @@ public class S3ConnectionLiveTest extends
try { try {
context.getApi().copyObject(containerName, sourceKey, destinationContainer, context.getApi().copyObject(containerName, sourceKey, destinationContainer,
destinationKey, ifSourceETagMatches(badETag)).get(10, TimeUnit.SECONDS); destinationKey, ifSourceETagMatches("setsds")).get(10, TimeUnit.SECONDS);
} catch (ExecutionException e) { } catch (ExecutionException e) {
HttpResponseException ex = (HttpResponseException) e.getCause(); HttpResponseException ex = (HttpResponseException) e.getCause();
assertEquals(ex.getResponse().getStatusCode(), 412); assertEquals(ex.getResponse().getStatusCode(), 412);
@ -431,10 +432,10 @@ public class S3ConnectionLiveTest extends
String containerName = getContainerName(); String containerName = getContainerName();
String destinationContainer = getContainerName(); String destinationContainer = getContainerName();
try { try {
addToContainerAndValidate(containerName, sourceKey); String goodETag = addToContainerAndValidate(containerName, sourceKey);
context.getApi().copyObject(containerName, sourceKey, destinationContainer, context.getApi().copyObject(containerName, sourceKey, destinationContainer,
destinationKey, ifSourceETagDoesntMatch(badETag)).get(10, TimeUnit.SECONDS); destinationKey, ifSourceETagDoesntMatch("asfasdf")).get(10, TimeUnit.SECONDS);
validateContent(destinationContainer, destinationKey); validateContent(destinationContainer, destinationKey);
try { try {
@ -458,7 +459,7 @@ public class S3ConnectionLiveTest extends
try { try {
addToContainerAndValidate(containerName, sourceKey); addToContainerAndValidate(containerName, sourceKey);
Multimap<String, String> metadata = HashMultimap.create(); Map<String, String> metadata = Maps.newHashMap();
metadata.put("adrian", "cole"); metadata.put("adrian", "cole");
context.getApi().copyObject(containerName, sourceKey, destinationContainer, context.getApi().copyObject(containerName, sourceKey, destinationContainer,

View File

@ -23,7 +23,6 @@
*/ */
package org.jclouds.aws.s3.internal; package org.jclouds.aws.s3.internal;
import java.util.Arrays;
import java.util.Map; import java.util.Map;
import java.util.SortedSet; import java.util.SortedSet;
import java.util.TreeSet; import java.util.TreeSet;
@ -54,7 +53,6 @@ import org.jclouds.aws.s3.reference.S3Constants;
import org.jclouds.blobstore.ContainerNotFoundException; import org.jclouds.blobstore.ContainerNotFoundException;
import org.jclouds.blobstore.KeyNotFoundException; import org.jclouds.blobstore.KeyNotFoundException;
import org.jclouds.blobstore.integration.internal.StubBlobStore; import org.jclouds.blobstore.integration.internal.StubBlobStore;
import org.jclouds.http.HttpUtils;
import org.jclouds.http.options.GetOptions; import org.jclouds.http.options.GetOptions;
import org.jclouds.util.DateService; import org.jclouds.util.DateService;
import org.joda.time.DateTime; import org.joda.time.DateTime;
@ -187,14 +185,14 @@ public class StubS3Connection extends StubBlobStore<BucketMetadata, ObjectMetada
if (source.containsKey(sourceObject)) { if (source.containsKey(sourceObject)) {
S3Object object = source.get(sourceObject); S3Object object = source.get(sourceObject);
if (options.getIfMatch() != null) { if (options.getIfMatch() != null) {
if (!Arrays.equals(object.getMetadata().getETag(), HttpUtils if (!object.getMetadata().getETag().equals(
.fromHexString(options.getIfMatch().replaceAll("\"", "")))) options.getIfMatch()))
throwResponseException(412); throwResponseException(412);
} }
if (options.getIfNoneMatch() != null) { if (options.getIfNoneMatch() != null) {
if (Arrays.equals(object.getMetadata().getETag(), HttpUtils.fromHexString(options if (object.getMetadata().getETag().equals(
.getIfNoneMatch().replaceAll("\"", "")))) options.getIfNoneMatch()))
throwResponseException(412); throwResponseException(412);
} }
if (options.getIfModifiedSince() != null) { if (options.getIfModifiedSince() != null) {
@ -226,7 +224,7 @@ public class StubS3Connection extends StubBlobStore<BucketMetadata, ObjectMetada
}; };
} }
public Future<byte[]> putObject(final String bucketName, final S3Object object, public Future<String> putObject(final String bucketName, final S3Object object,
@Nullable PutObjectOptions nullableOptions) { @Nullable PutObjectOptions nullableOptions) {
final PutObjectOptions options = (nullableOptions == null) ? new PutObjectOptions() final PutObjectOptions options = (nullableOptions == null) ? new PutObjectOptions()
: nullableOptions; : nullableOptions;
@ -313,7 +311,7 @@ public class StubS3Connection extends StubBlobStore<BucketMetadata, ObjectMetada
return super.getBlob(bucketName, key, optionsList.length != 0 ? optionsList[0] : null); return super.getBlob(bucketName, key, optionsList.length != 0 ? optionsList[0] : null);
} }
public Future<byte[]> putObject(String bucketName, S3Object object, public Future<String> putObject(String bucketName, S3Object object,
PutObjectOptions... optionsList) { PutObjectOptions... optionsList) {
return putObject(bucketName, object, optionsList.length != 0 ? optionsList[0] : null); return putObject(bucketName, object, optionsList.length != 0 ? optionsList[0] : null);
} }

View File

@ -34,16 +34,16 @@ import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertTrue; import static org.testng.Assert.assertTrue;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.util.Map;
import org.jclouds.aws.s3.domain.CannedAccessPolicy; import org.jclouds.aws.s3.domain.CannedAccessPolicy;
import org.jclouds.aws.s3.reference.S3Headers; import org.jclouds.aws.s3.reference.S3Headers;
import org.jclouds.http.HttpUtils;
import org.jclouds.util.DateService; import org.jclouds.util.DateService;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.testng.annotations.BeforeMethod; import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import com.google.common.collect.HashMultimap; import com.google.common.collect.Maps;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
/** /**
@ -54,22 +54,22 @@ import com.google.common.collect.Multimap;
@Test(groups = "unit", testName = "s3.CopyObjectOptionsTest") @Test(groups = "unit", testName = "s3.CopyObjectOptionsTest")
public class CopyObjectOptionsTest { public class CopyObjectOptionsTest {
private byte[] testBytes; private String etag;
private DateTime now; private DateTime now;
private String nowExpected; private String nowExpected;
private Multimap<String, String> goodMeta; private Map<String, String> goodMeta;
private Multimap<String, String> badMeta; private Map<String, String> badMeta;
@BeforeMethod @BeforeMethod
void setUp() { void setUp() {
goodMeta = HashMultimap.create(); goodMeta = Maps.newHashMap();
goodMeta.put("x-amz-meta-adrian", "foo"); goodMeta.put("x-amz-meta-adrian", "foo");
badMeta = HashMultimap.create(); badMeta = Maps.newHashMap();
badMeta.put("x-google-meta-adrian", "foo"); badMeta.put("x-google-meta-adrian", "foo");
now = new DateTime(); now = new DateTime();
nowExpected = new DateService().rfc822DateFormat(now); nowExpected = new DateService().rfc822DateFormat(now);
testBytes = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7 }; etag = "mama";
} }
@Test @Test
@ -93,7 +93,7 @@ public class CopyObjectOptionsTest {
assertEquals(headers.get("x-amz-metadata-directive").iterator().next(), "REPLACE"); assertEquals(headers.get("x-amz-metadata-directive").iterator().next(), "REPLACE");
assertEquals(options.getMetadata().size(), 1); assertEquals(options.getMetadata().size(), 1);
assertEquals(headers.get("x-amz-meta-adrian").iterator().next(), "foo"); assertEquals(headers.get("x-amz-meta-adrian").iterator().next(), "foo");
assertEquals(options.getMetadata().get("x-amz-meta-adrian").iterator().next(), "foo"); assertEquals(options.getMetadata().get("x-amz-meta-adrian"), "foo");
} }
@Test @Test
@ -159,7 +159,7 @@ public class CopyObjectOptionsTest {
@Test @Test
public void testIfETagMatches() throws UnsupportedEncodingException { public void testIfETagMatches() throws UnsupportedEncodingException {
CopyObjectOptions options = new CopyObjectOptions(); CopyObjectOptions options = new CopyObjectOptions();
options.ifSourceETagMatches(testBytes); options.ifSourceETagMatches(etag);
matchesHex(options.getIfMatch()); matchesHex(options.getIfMatch());
} }
@ -171,7 +171,7 @@ public class CopyObjectOptionsTest {
@Test @Test
public void testIfETagMatchesStatic() throws UnsupportedEncodingException { public void testIfETagMatchesStatic() throws UnsupportedEncodingException {
CopyObjectOptions options = ifSourceETagMatches(testBytes); CopyObjectOptions options = ifSourceETagMatches(etag);
matchesHex(options.getIfMatch()); matchesHex(options.getIfMatch());
} }
@ -183,7 +183,7 @@ public class CopyObjectOptionsTest {
@Test @Test
public void testIfETagDoesntMatch() throws UnsupportedEncodingException { public void testIfETagDoesntMatch() throws UnsupportedEncodingException {
CopyObjectOptions options = new CopyObjectOptions(); CopyObjectOptions options = new CopyObjectOptions();
options.ifSourceETagDoesntMatch(testBytes); options.ifSourceETagDoesntMatch(etag);
matchesHex(options.getIfNoneMatch()); matchesHex(options.getIfNoneMatch());
} }
@ -195,7 +195,7 @@ public class CopyObjectOptionsTest {
@Test @Test
public void testIfETagDoesntMatchStatic() throws UnsupportedEncodingException { public void testIfETagDoesntMatchStatic() throws UnsupportedEncodingException {
CopyObjectOptions options = ifSourceETagDoesntMatch(testBytes); CopyObjectOptions options = ifSourceETagDoesntMatch(etag);
matchesHex(options.getIfNoneMatch()); matchesHex(options.getIfNoneMatch());
} }
@ -205,7 +205,7 @@ public class CopyObjectOptionsTest {
} }
private void matchesHex(String match) throws UnsupportedEncodingException { private void matchesHex(String match) throws UnsupportedEncodingException {
String expected = "\"" + HttpUtils.toHexString(testBytes) + "\""; String expected = "\"" + etag + "\"";
assertEquals(match, expected); assertEquals(match, expected);
} }
@ -216,13 +216,13 @@ public class CopyObjectOptionsTest {
} }
public void testIfUnmodifiedAfterETagMatches() throws UnsupportedEncodingException { public void testIfUnmodifiedAfterETagMatches() throws UnsupportedEncodingException {
ifSourceETagMatches(testBytes).ifSourceUnmodifiedSince(now); ifSourceETagMatches(etag).ifSourceUnmodifiedSince(now);
} }
@Test(expectedExceptions = IllegalStateException.class) @Test(expectedExceptions = IllegalStateException.class)
public void testIfUnmodifiedAfterETagDoesntMatch() throws UnsupportedEncodingException { public void testIfUnmodifiedAfterETagDoesntMatch() throws UnsupportedEncodingException {
ifSourceETagDoesntMatch(testBytes).ifSourceUnmodifiedSince(now); ifSourceETagDoesntMatch(etag).ifSourceUnmodifiedSince(now);
} }
@Test(expectedExceptions = IllegalStateException.class) @Test(expectedExceptions = IllegalStateException.class)
@ -233,44 +233,44 @@ public class CopyObjectOptionsTest {
@Test(expectedExceptions = IllegalStateException.class) @Test(expectedExceptions = IllegalStateException.class)
public void testIfModifiedAfterETagMatches() throws UnsupportedEncodingException { public void testIfModifiedAfterETagMatches() throws UnsupportedEncodingException {
ifSourceETagMatches(testBytes).ifSourceModifiedSince(now); ifSourceETagMatches(etag).ifSourceModifiedSince(now);
} }
public void testIfModifiedAfterETagDoesntMatch() throws UnsupportedEncodingException { public void testIfModifiedAfterETagDoesntMatch() throws UnsupportedEncodingException {
ifSourceETagDoesntMatch(testBytes).ifSourceModifiedSince(now); ifSourceETagDoesntMatch(etag).ifSourceModifiedSince(now);
} }
@Test(expectedExceptions = IllegalStateException.class) @Test(expectedExceptions = IllegalStateException.class)
public void testETagMatchesAfterIfModified() throws UnsupportedEncodingException { public void testETagMatchesAfterIfModified() throws UnsupportedEncodingException {
ifSourceModifiedSince(now).ifSourceETagMatches(testBytes); ifSourceModifiedSince(now).ifSourceETagMatches(etag);
} }
public void testETagMatchesAfterIfUnmodified() throws UnsupportedEncodingException { public void testETagMatchesAfterIfUnmodified() throws UnsupportedEncodingException {
ifSourceUnmodifiedSince(now).ifSourceETagMatches(testBytes); ifSourceUnmodifiedSince(now).ifSourceETagMatches(etag);
} }
@Test(expectedExceptions = IllegalStateException.class) @Test(expectedExceptions = IllegalStateException.class)
public void testETagMatchesAfterETagDoesntMatch() throws UnsupportedEncodingException { public void testETagMatchesAfterETagDoesntMatch() throws UnsupportedEncodingException {
ifSourceETagDoesntMatch(testBytes).ifSourceETagMatches(testBytes); ifSourceETagDoesntMatch(etag).ifSourceETagMatches(etag);
} }
public void testETagDoesntMatchAfterIfModified() throws UnsupportedEncodingException { public void testETagDoesntMatchAfterIfModified() throws UnsupportedEncodingException {
ifSourceModifiedSince(now).ifSourceETagDoesntMatch(testBytes); ifSourceModifiedSince(now).ifSourceETagDoesntMatch(etag);
} }
@Test(expectedExceptions = IllegalStateException.class) @Test(expectedExceptions = IllegalStateException.class)
public void testETagDoesntMatchAfterIfUnmodified() throws UnsupportedEncodingException { public void testETagDoesntMatchAfterIfUnmodified() throws UnsupportedEncodingException {
ifSourceUnmodifiedSince(now).ifSourceETagDoesntMatch(testBytes); ifSourceUnmodifiedSince(now).ifSourceETagDoesntMatch(etag);
} }
@Test(expectedExceptions = IllegalStateException.class) @Test(expectedExceptions = IllegalStateException.class)
public void testETagDoesntMatchAfterETagMatches() throws UnsupportedEncodingException { public void testETagDoesntMatchAfterETagMatches() throws UnsupportedEncodingException {
ifSourceETagMatches(testBytes).ifSourceETagDoesntMatch(testBytes); ifSourceETagMatches(etag).ifSourceETagDoesntMatch(etag);
} }
@Test @Test
@ -282,15 +282,15 @@ public class CopyObjectOptionsTest {
@Test @Test
void testBuildRequestHeaders() throws UnsupportedEncodingException { void testBuildRequestHeaders() throws UnsupportedEncodingException {
CopyObjectOptions options = ifSourceModifiedSince(now).ifSourceETagDoesntMatch(testBytes) CopyObjectOptions options = ifSourceModifiedSince(now).ifSourceETagDoesntMatch(etag)
.overrideMetadataWith(goodMeta); .overrideMetadataWith(goodMeta);
options.setMetadataPrefix("x-amz-meta-"); options.setMetadataPrefix("x-amz-meta-");
Multimap<String, String> headers = options.buildRequestHeaders(); Multimap<String, String> headers = options.buildRequestHeaders();
assertEquals(headers.get("x-amz-copy-source-if-modified-since").iterator().next(), assertEquals(headers.get("x-amz-copy-source-if-modified-since").iterator().next(),
new DateService().rfc822DateFormat(now)); new DateService().rfc822DateFormat(now));
assertEquals(headers.get("x-amz-copy-source-if-none-match").iterator().next(), "\"" assertEquals(headers.get("x-amz-copy-source-if-none-match").iterator().next(), "\"" + etag
+ HttpUtils.toHexString(testBytes) + "\""); + "\"");
for (String value : goodMeta.values()) for (String value : goodMeta.values())
assertTrue(headers.containsValue(value)); assertTrue(headers.containsValue(value));

View File

@ -40,7 +40,6 @@ import org.jclouds.aws.s3.domain.CanonicalUser;
import org.jclouds.aws.s3.domain.ListBucketResponse; import org.jclouds.aws.s3.domain.ListBucketResponse;
import org.jclouds.aws.s3.domain.ObjectMetadata; import org.jclouds.aws.s3.domain.ObjectMetadata;
import org.jclouds.http.HttpException; import org.jclouds.http.HttpException;
import org.jclouds.http.HttpUtils;
import org.jclouds.http.functions.ParseSax; import org.jclouds.http.functions.ParseSax;
import org.jclouds.http.functions.config.ParserModule; import org.jclouds.http.functions.config.ParserModule;
import org.joda.time.DateTime; import org.joda.time.DateTime;
@ -137,7 +136,7 @@ public class S3ParserTest extends PerformanceTest {
DateTime expected = new DateTime("2009-03-12T02:00:13.000Z"); DateTime expected = new DateTime("2009-03-12T02:00:13.000Z");
assert object.getLastModified().equals(expected) : String.format( assert object.getLastModified().equals(expected) : String.format(
"expected %1$s, but got %1$s", expected, object.getLastModified()); "expected %1$s, but got %1$s", expected, object.getLastModified());
assertEquals(HttpUtils.toHexString(object.getETag()), "9d7bb64e8e18ee34eec06dd2cf37b766"); assertEquals(object.getETag(), "\"9d7bb64e8e18ee34eec06dd2cf37b766\"");
assert object.getSize() == 136; assert object.getSize() == 136;
CanonicalUser owner = new CanonicalUser( CanonicalUser owner = new CanonicalUser(
"e1a5f66a480ca99a4fdfe8e318c3020446c9989d7004e7778029fbcc5d990fa0"); "e1a5f66a480ca99a4fdfe8e318c3020446c9989d7004e7778029fbcc5d990fa0");
@ -162,7 +161,7 @@ public class S3ParserTest extends PerformanceTest {
ObjectMetadata metadata = runParseCopyObjectResult(); ObjectMetadata metadata = runParseCopyObjectResult();
DateTime expected = new DateTime("2009-03-19T13:23:27.000Z"); DateTime expected = new DateTime("2009-03-19T13:23:27.000Z");
assertEquals(metadata.getLastModified(), expected); assertEquals(metadata.getLastModified(), expected);
assertEquals(HttpUtils.toHexString(metadata.getETag()), "92836a3ea45a6984d1b4d23a747d46bb"); assertEquals(metadata.getETag(), "\"92836a3ea45a6984d1b4d23a747d46bb\"");
} }
@Test @Test

View File

@ -42,7 +42,6 @@ import org.jclouds.aws.s3.options.CopyObjectOptions;
import org.jclouds.aws.s3.options.ListBucketOptions; import org.jclouds.aws.s3.options.ListBucketOptions;
import org.jclouds.aws.s3.options.PutObjectOptions; import org.jclouds.aws.s3.options.PutObjectOptions;
import org.jclouds.blobstore.BlobStoreContext; import org.jclouds.blobstore.BlobStoreContext;
import org.jclouds.http.HttpUtils;
import org.jclouds.http.options.GetOptions; import org.jclouds.http.options.GetOptions;
import org.jclouds.util.Utils; import org.jclouds.util.Utils;
import org.jets3t.service.S3ObjectsChunk; import org.jets3t.service.S3ObjectsChunk;
@ -114,7 +113,7 @@ public class JCloudsS3Service extends S3Service {
Map map = new HashMap(); Map map = new HashMap();
// Result fields returned when copy is successful. // Result fields returned when copy is successful.
map.put("Last-Modified", jcObjectMetadata.getLastModified().toDate()); map.put("Last-Modified", jcObjectMetadata.getLastModified().toDate());
map.put("ETag", HttpUtils.toHexString(jcObjectMetadata.getETag())); map.put("ETag", jcObjectMetadata.getETag());
return map; return map;
} catch (Exception e) { } catch (Exception e) {
Utils.<S3ServiceException> rethrowIfRuntimeOrSameType(e); Utils.<S3ServiceException> rethrowIfRuntimeOrSameType(e);
@ -353,9 +352,9 @@ public class JCloudsS3Service extends S3Service {
try { try {
PutObjectOptions options = Util.convertPutObjectOptions(jsObject.getAcl()); PutObjectOptions options = Util.convertPutObjectOptions(jsObject.getAcl());
org.jclouds.aws.s3.domain.S3Object jcObject = Util.convertObject(jsObject); org.jclouds.aws.s3.domain.S3Object jcObject = Util.convertObject(jsObject);
byte eTag[] = connection.putObject(bucketName, jcObject, options).get( String eTag = connection.putObject(bucketName, jcObject, options).get(
requestTimeoutMilliseconds, TimeUnit.MILLISECONDS); requestTimeoutMilliseconds, TimeUnit.MILLISECONDS);
jsObject.setMd5Hash(eTag); jsObject.setETag(eTag);
return jsObject; return jsObject;
} catch (Exception e) { } catch (Exception e) {
Utils.<S3ServiceException> rethrowIfRuntimeOrSameType(e); Utils.<S3ServiceException> rethrowIfRuntimeOrSameType(e);

View File

@ -43,10 +43,8 @@ import org.jclouds.aws.s3.domain.ListBucketResponse;
import org.jclouds.aws.s3.options.CopyObjectOptions; import org.jclouds.aws.s3.options.CopyObjectOptions;
import org.jclouds.aws.s3.options.ListBucketOptions; import org.jclouds.aws.s3.options.ListBucketOptions;
import org.jclouds.aws.s3.options.PutObjectOptions; import org.jclouds.aws.s3.options.PutObjectOptions;
import org.jclouds.http.HttpUtils;
import org.jclouds.http.options.GetOptions; import org.jclouds.http.options.GetOptions;
import org.jclouds.util.DateService; import org.jclouds.util.DateService;
import org.jclouds.util.Utils;
import org.jets3t.service.S3ServiceException; import org.jets3t.service.S3ServiceException;
import org.jets3t.service.acl.AccessControlList; import org.jets3t.service.acl.AccessControlList;
import org.jets3t.service.acl.CanonicalGrantee; import org.jets3t.service.acl.CanonicalGrantee;
@ -61,9 +59,8 @@ import org.jets3t.service.model.S3Owner;
import org.jets3t.service.utils.RestUtils; import org.jets3t.service.utils.RestUtils;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Multimap; import com.google.common.collect.Maps;
/** /**
* Convert between jCloud and JetS3t objects. * Convert between jCloud and JetS3t objects.
@ -158,7 +155,7 @@ public class Util {
jcObject.getMetadata().setStorageClass(jsObject.getStorageClass()); jcObject.getMetadata().setStorageClass(jsObject.getStorageClass());
if (jsObject.getMd5HashAsHex() != null) { if (jsObject.getMd5HashAsHex() != null) {
jcObject.getMetadata().setETag(HttpUtils.fromHexString(jsObject.getMd5HashAsHex())); jcObject.getMetadata().setETag(jsObject.getMd5HashAsHex());
} }
if (jsObject.getOwner() != null) { if (jsObject.getOwner() != null) {
@ -207,11 +204,11 @@ public class Util {
} }
// TODO: options.ifETagMatches should accept multiple match tags // TODO: options.ifETagMatches should accept multiple match tags
if (ifMatchTags != null && ifMatchTags.length > 0) { if (ifMatchTags != null && ifMatchTags.length > 0) {
options.ifETagMatches(Utils.encodeString(ifMatchTags[0])); options.ifETagMatches(ifMatchTags[0]);
} }
// TODO: options.ifETagDoesntMatch should accept multiple match tags // TODO: options.ifETagDoesntMatch should accept multiple match tags
if (ifNoneMatchTags != null && ifNoneMatchTags.length > 0) { if (ifNoneMatchTags != null && ifNoneMatchTags.length > 0) {
options.ifETagDoesntMatch(Utils.encodeString(ifNoneMatchTags[0])); options.ifETagDoesntMatch(ifNoneMatchTags[0]);
} }
return options; return options;
} }
@ -355,14 +352,14 @@ public class Util {
} }
// TODO: options.ifETagMatches should accept multiple match tags // TODO: options.ifETagMatches should accept multiple match tags
if (ifMatchTags != null && ifMatchTags.length > 0) { if (ifMatchTags != null && ifMatchTags.length > 0) {
options.ifSourceETagMatches(Utils.encodeString(ifMatchTags[0])); options.ifSourceETagMatches(ifMatchTags[0]);
} }
// TODO: options.ifETagDoesntMatch should accept multiple match tags // TODO: options.ifETagDoesntMatch should accept multiple match tags
if (ifNoneMatchTags != null && ifNoneMatchTags.length > 0) { if (ifNoneMatchTags != null && ifNoneMatchTags.length > 0) {
options.ifSourceETagDoesntMatch(Utils.encodeString(ifNoneMatchTags[0])); options.ifSourceETagDoesntMatch(ifNoneMatchTags[0]);
} }
if (destinationMetadata != null) { if (destinationMetadata != null) {
Multimap<String, String> newMetadata = HashMultimap.create(); Map<String, String> newMetadata = Maps.newHashMap();
for (Object maybeUserMetadataObj : destinationMetadata.entrySet()) { for (Object maybeUserMetadataObj : destinationMetadata.entrySet()) {
String name = ((Entry<String, String>) maybeUserMetadataObj).getKey(); String name = ((Entry<String, String>) maybeUserMetadataObj).getKey();
String value = ((Entry<String, String>) maybeUserMetadataObj).getValue(); String value = ((Entry<String, String>) maybeUserMetadataObj).getValue();

View File

@ -51,7 +51,6 @@ import org.jclouds.aws.s3.domain.ObjectMetadata;
import org.jclouds.aws.s3.domain.AccessControlList.GroupGranteeURI; import org.jclouds.aws.s3.domain.AccessControlList.GroupGranteeURI;
import org.jclouds.aws.s3.domain.AccessControlList.Permission; import org.jclouds.aws.s3.domain.AccessControlList.Permission;
import org.jclouds.blobstore.integration.internal.BaseBlobStoreIntegrationTest; import org.jclouds.blobstore.integration.internal.BaseBlobStoreIntegrationTest;
import org.jclouds.http.HttpUtils;
import org.jets3t.service.S3ObjectsChunk; import org.jets3t.service.S3ObjectsChunk;
import org.jets3t.service.S3Service; import org.jets3t.service.S3Service;
import org.jets3t.service.S3ServiceException; import org.jets3t.service.S3ServiceException;
@ -71,7 +70,6 @@ import org.testng.annotations.Test;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;
/** /**
* Tests to cover JCloudsS3Service * Tests to cover JCloudsS3Service
@ -398,8 +396,7 @@ public class JCloudsS3ServiceLiveTest
requestObject.addMetadata("x-amz-meta-" + "my-metadata-1", "value-1"); requestObject.addMetadata("x-amz-meta-" + "my-metadata-1", "value-1");
jsResultObject = service.putObject(new S3Bucket(bucketName), requestObject); jsResultObject = service.putObject(new S3Bucket(bucketName), requestObject);
jcObject = context.getApi().getObject(bucketName, objectKey).get(10, TimeUnit.SECONDS); jcObject = context.getApi().getObject(bucketName, objectKey).get(10, TimeUnit.SECONDS);
assertEquals(Iterables.getLast(jcObject.getMetadata().getUserMetadata().get( assertEquals(jcObject.getMetadata().getUserMetadata().get("my-metadata-1"), "value-1");
"my-metadata-1")), "value-1");
assertEquals(jsResultObject.getMetadata("x-amz-meta-" + "my-metadata-1"), "value-1"); assertEquals(jsResultObject.getMetadata("x-amz-meta-" + "my-metadata-1"), "value-1");
// Upload object with canned public-read ACL // Upload object with canned public-read ACL
@ -424,8 +421,8 @@ public class JCloudsS3ServiceLiveTest
jsResultObject = service.putObject(new S3Bucket(bucketName), requestObject); jsResultObject = service.putObject(new S3Bucket(bucketName), requestObject);
jcObject = context.getApi().getObject(bucketName, objectKey).get(10, TimeUnit.SECONDS); jcObject = context.getApi().getObject(bucketName, objectKey).get(10, TimeUnit.SECONDS);
assertTrue(jsResultObject.verifyData(data.getBytes("UTF-8"))); assertTrue(jsResultObject.verifyData(data.getBytes("UTF-8")));
assertEquals(jsResultObject.getMd5HashAsHex(), HttpUtils.toHexString(jcObject assertEquals(jsResultObject.getETag(), jcObject.getMetadata().getETag().replaceAll("\"",
.getMetadata().getETag())); ""));
} finally { } finally {
returnContainer(bucketName); returnContainer(bucketName);
} }
@ -462,10 +459,9 @@ public class JCloudsS3ServiceLiveTest
.get(10, TimeUnit.SECONDS); .get(10, TimeUnit.SECONDS);
// TODO null keys from s3object! assertEquals(jcDestinationObject.getKey(), // TODO null keys from s3object! assertEquals(jcDestinationObject.getKey(),
// destinationObjectKey); // destinationObjectKey);
assertEquals(Iterators.getLast(jcDestinationObject.getMetadata().getUserMetadata().get( assertEquals(jcDestinationObject.getMetadata().getUserMetadata().get(metadataName),
metadataName).iterator()), sourceMetadataValue); sourceMetadataValue);
assertEquals(copyResult.get("ETag"), HttpUtils.toHexString(jcDestinationObject assertEquals(copyResult.get("ETag"), jcDestinationObject.getMetadata().getETag());
.getMetadata().getETag()));
// Test destination ACL is unchanged (ie private) // Test destination ACL is unchanged (ie private)
org.jclouds.aws.s3.domain.AccessControlList jcACL = context.getApi().getObjectACL( org.jclouds.aws.s3.domain.AccessControlList jcACL = context.getApi().getObjectACL(
bucketName, destinationObject.getKey()).get(10, TimeUnit.SECONDS); bucketName, destinationObject.getKey()).get(10, TimeUnit.SECONDS);
@ -479,8 +475,8 @@ public class JCloudsS3ServiceLiveTest
destinationObject, true); destinationObject, true);
jcDestinationObject = context.getApi().getObject(bucketName, destinationObject.getKey()) jcDestinationObject = context.getApi().getObject(bucketName, destinationObject.getKey())
.get(10, TimeUnit.SECONDS); .get(10, TimeUnit.SECONDS);
assertEquals(Iterators.getLast(jcDestinationObject.getMetadata().getUserMetadata().get( assertEquals(jcDestinationObject.getMetadata().getUserMetadata().get(metadataName),
metadataName).iterator()), destinationMetadataValue); destinationMetadataValue);
// Test destination ACL is unchanged (ie private) // Test destination ACL is unchanged (ie private)
jcACL = context.getApi().getObjectACL(bucketName, destinationObject.getKey()).get(10, jcACL = context.getApi().getObjectACL(bucketName, destinationObject.getKey()).get(10,
TimeUnit.SECONDS); TimeUnit.SECONDS);

View File

@ -271,7 +271,7 @@ public interface AzureBlobConnection {
@PUT @PUT
@Path("{container}/{key}") @Path("{container}/{key}")
@ResponseParser(ParseETagHeader.class) @ResponseParser(ParseETagHeader.class)
Future<byte[]> putBlob(@PathParam("container") String container, Future<String> putBlob(@PathParam("container") String container,
@PathParam("key") @ParamParser(BlobKey.class) @BinderParam(BindBlobToEntity.class) Blob object); @PathParam("key") @ParamParser(BlobKey.class) @BinderParam(BindBlobToEntity.class) Blob object);
/** /**

View File

@ -147,7 +147,7 @@ public interface AzureBlobStore extends BlobStore<ContainerMetadata, BlobMetadat
@PUT @PUT
@Path("{container}/{key}") @Path("{container}/{key}")
@ResponseParser(ParseETagHeader.class) @ResponseParser(ParseETagHeader.class)
Future<byte[]> putBlob( Future<String> putBlob(
@PathParam("container") String container, @PathParam("container") String container,
@PathParam("key") @ParamParser(BlobKey.class) @BinderParam(GenerateMD5AndBindBlobToEntity.class) Blob object); @PathParam("key") @ParamParser(BlobKey.class) @BinderParam(GenerateMD5AndBindBlobToEntity.class) Blob object);

View File

@ -53,7 +53,7 @@ public class BlobMetadata extends org.jclouds.blobstore.domain.BlobMetadata {
} }
public BlobMetadata(String currentName, URI currentUrl, DateTime currentLastModified, public BlobMetadata(String currentName, URI currentUrl, DateTime currentLastModified,
byte[] currentETag, long currentSize, String currentContentType, String currentETag, long currentSize, String currentContentType,
@Nullable byte[] contentMD5, @Nullable String currentContentEncoding, @Nullable byte[] contentMD5, @Nullable String currentContentEncoding,
@Nullable String currentContentLanguage) { @Nullable String currentContentLanguage) {
this(currentName); this(currentName);

View File

@ -99,7 +99,7 @@ public class ParseContainerMetadataFromHeaders implements
protected void addETagTo(HttpResponse from, ContainerMetadata metadata) { protected void addETagTo(HttpResponse from, ContainerMetadata metadata) {
String eTag = from.getFirstHeaderOrNull(HttpHeaders.ETAG); String eTag = from.getFirstHeaderOrNull(HttpHeaders.ETAG);
if (metadata.getETag() == null && eTag != null) { if (metadata.getETag() == null && eTag != null) {
metadata.setETag(HttpUtils.fromHexString(eTag.replaceAll("\"", ""))); metadata.setETag(HttpUtils.fromHexString(eTag));
} }
} }

View File

@ -31,7 +31,6 @@ import javax.inject.Inject;
import org.jclouds.azure.storage.blob.domain.BlobMetadata; import org.jclouds.azure.storage.blob.domain.BlobMetadata;
import org.jclouds.azure.storage.blob.domain.ListBlobsResponse; import org.jclouds.azure.storage.blob.domain.ListBlobsResponse;
import org.jclouds.azure.storage.blob.domain.TreeSetListBlobsResponse; import org.jclouds.azure.storage.blob.domain.TreeSetListBlobsResponse;
import org.jclouds.http.HttpUtils;
import org.jclouds.http.functions.ParseSax; import org.jclouds.http.functions.ParseSax;
import org.jclouds.util.DateService; import org.jclouds.util.DateService;
import org.joda.time.DateTime; import org.joda.time.DateTime;
@ -59,7 +58,7 @@ public class ContainerNameEnumerationResultsHandler extends
private URI currentUrl; private URI currentUrl;
private URI containerUrl; private URI containerUrl;
private DateTime currentLastModified; private DateTime currentLastModified;
private byte[] currentETag; private String currentETag;
private StringBuilder currentText = new StringBuilder(); private StringBuilder currentText = new StringBuilder();
@ -131,7 +130,7 @@ public class ContainerNameEnumerationResultsHandler extends
} else if (qName.equals("LastModified")) { } else if (qName.equals("LastModified")) {
currentLastModified = dateParser.rfc822DateParse(currentText.toString().trim()); currentLastModified = dateParser.rfc822DateParse(currentText.toString().trim());
} else if (qName.equals("Etag")) { } else if (qName.equals("Etag")) {
currentETag = HttpUtils.fromHexString(currentText.toString().trim()); currentETag = currentText.toString().trim();
} else if (qName.equals("Name")) { } else if (qName.equals("Name")) {
if (inBlob) if (inBlob)
currentName = currentText.toString().trim(); currentName = currentText.toString().trim();

View File

@ -54,7 +54,6 @@ import org.testng.annotations.BeforeGroups;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Iterables;
/** /**
* Tests behavior of {@code AzureBlobConnection} * Tests behavior of {@code AzureBlobConnection}
@ -239,7 +238,7 @@ public class AzureBlobConnectionLiveTest {
object.getMetadata().setContentType("text/plain"); object.getMetadata().setContentType("text/plain");
object.getMetadata().getUserMetadata().put("Metadata", "metadata-value"); object.getMetadata().getUserMetadata().put("Metadata", "metadata-value");
byte[] md5 = object.getMetadata().getContentMD5(); byte[] md5 = object.getMetadata().getContentMD5();
byte[] newEtag = connection.putBlob(privateContainer, object).get(10, TimeUnit.SECONDS); String newEtag = connection.putBlob(privateContainer, object).get(10, TimeUnit.SECONDS);
assertEquals(HttpUtils.toHexString(md5), HttpUtils.toHexString(object.getMetadata() assertEquals(HttpUtils.toHexString(md5), HttpUtils.toHexString(object.getMetadata()
.getContentMD5())); .getContentMD5()));
@ -265,8 +264,8 @@ public class AzureBlobConnectionLiveTest {
assertEquals(HttpUtils.toHexString(md5), HttpUtils.toHexString(object.getMetadata() assertEquals(HttpUtils.toHexString(md5), HttpUtils.toHexString(object.getMetadata()
.getContentMD5())); .getContentMD5()));
assertEquals(metadata.getETag(), newEtag); assertEquals(metadata.getETag(), newEtag);
assertEquals(metadata.getUserMetadata().entries().size(), 1); assertEquals(metadata.getUserMetadata().entrySet().size(), 1);
assertEquals(Iterables.getLast(metadata.getUserMetadata().get("metadata")), "metadata-value"); assertEquals(metadata.getUserMetadata().get("metadata"), "metadata-value");
// // Test POST to update object's metadata // // Test POST to update object's metadata
// Multimap<String, String> userMetadata = HashMultimap.create(); // Multimap<String, String> userMetadata = HashMultimap.create();
@ -299,13 +298,13 @@ public class AzureBlobConnectionLiveTest {
// assertEquals( // assertEquals(
// Iterables.getLast(getBlob.getMetadata().getUserMetadata().get("New-Metadata-2")), // Iterables.getLast(getBlob.getMetadata().getUserMetadata().get("New-Metadata-2")),
// "value-2"); // "value-2");
assertEquals(metadata.getUserMetadata().entries().size(), 1); assertEquals(metadata.getUserMetadata().entrySet().size(), 1);
assertEquals(Iterables.getLast(metadata.getUserMetadata().get("metadata")), "metadata-value"); assertEquals(metadata.getUserMetadata().get("metadata"), "metadata-value");
// Test PUT with invalid ETag (as if object's data was corrupted in transit) // Test PUT with invalid ETag (as if object's data was corrupted in transit)
String correctEtag = HttpUtils.toHexString(newEtag); String correctEtag = newEtag;
String incorrectEtag = "0" + correctEtag.substring(1); String incorrectEtag = "0" + correctEtag.substring(1);
object.getMetadata().setETag(HttpUtils.fromHexString(incorrectEtag)); object.getMetadata().setETag(incorrectEtag);
try { try {
connection.putBlob(privateContainer, object).get(10, TimeUnit.SECONDS); connection.putBlob(privateContainer, object).get(10, TimeUnit.SECONDS);
} catch (Throwable e) { } catch (Throwable e) {
@ -347,5 +346,4 @@ public class AzureBlobConnectionLiveTest {
connection.deleteBlob(privateContainer, "object").get(10, TimeUnit.SECONDS); connection.deleteBlob(privateContainer, "object").get(10, TimeUnit.SECONDS);
connection.deleteBlob(privateContainer, "chunked-object").get(10, TimeUnit.SECONDS); connection.deleteBlob(privateContainer, "chunked-object").get(10, TimeUnit.SECONDS);
} }
} }

View File

@ -26,7 +26,6 @@ package org.jclouds.azure.storage.blob.integration;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
import java.io.IOException; import java.io.IOException;
import java.util.Collections;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
@ -74,8 +73,7 @@ public class AzureBlobIntegrationTest extends
// inexpensive fashion // inexpensive fashion
// http://code.google.com/p/jclouds/issues/detail?id=92 // http://code.google.com/p/jclouds/issues/detail?id=92
// assertEquals(metadata.getSize(), TEST_STRING.length()); // assertEquals(metadata.getSize(), TEST_STRING.length());
assertEquals(metadata.getUserMetadata().get("adrian"), Collections assertEquals(metadata.getUserMetadata().get("adrian"), "powderpuff");
.singletonList("powderpuff"));
assertEquals(metadata.getContentMD5(), HttpUtils.md5(TEST_STRING.getBytes())); assertEquals(metadata.getContentMD5(), HttpUtils.md5(TEST_STRING.getBytes()));
} }

View File

@ -100,27 +100,24 @@ public class AddMD5ToListBlobsResponseTest extends BaseHandlerTest {
.create("http://myaccount.blob.core.windows.net/mycontainer/blob1.txt"), .create("http://myaccount.blob.core.windows.net/mycontainer/blob1.txt"),
dateService dateService
.rfc822DateParse("Thu, 18 Sep 2008 18:41:57 GMT"), .rfc822DateParse("Thu, 18 Sep 2008 18:41:57 GMT"),
HttpUtils.fromHexString("0x8CAE7D55D050B8B"), 8, "0x8CAE7D55D050B8B", 8, "text/plain; charset=UTF-8",
"text/plain; charset=UTF-8", HttpUtils HttpUtils.fromHexString("01"), null, null),
.fromHexString("01"), null, null),
new BlobMetadata( new BlobMetadata(
"blob2.txt", "blob2.txt",
URI URI
.create("http://myaccount.blob.core.windows.net/mycontainer/blob2.txt"), .create("http://myaccount.blob.core.windows.net/mycontainer/blob2.txt"),
dateService dateService
.rfc822DateParse("Thu, 18 Sep 2008 18:41:57 GMT"), .rfc822DateParse("Thu, 18 Sep 2008 18:41:57 GMT"),
HttpUtils.fromHexString("0x8CAE7D55CF6C339"), 14, "0x8CAE7D55CF6C339", 14, "text/plain; charset=UTF-8",
"text/plain; charset=UTF-8", HttpUtils HttpUtils.fromHexString("02"), null, null),
.fromHexString("02"), null, null),
new BlobMetadata( new BlobMetadata(
"newblob1.txt", "newblob1.txt",
URI URI
.create("http://myaccount.blob.core.windows.net/mycontainer/newblob1.txt"), .create("http://myaccount.blob.core.windows.net/mycontainer/newblob1.txt"),
dateService dateService
.rfc822DateParse("Thu, 18 Sep 2008 18:41:57 GMT"), .rfc822DateParse("Thu, 18 Sep 2008 18:41:57 GMT"),
HttpUtils.fromHexString("0x8CAE7D55CF6C339"), 25, "0x8CAE7D55CF6C339", 25, "text/plain; charset=UTF-8",
"text/plain; charset=UTF-8", HttpUtils HttpUtils.fromHexString("03"), null, null)
.fromHexString("03"), null, null)
), null, null, 4, "newblob2.txt", null, "myfolder/"); ), null, null, 4, "newblob2.txt", null, "myfolder/");

View File

@ -32,7 +32,6 @@ import org.jclouds.azure.storage.blob.domain.BlobMetadata;
import org.jclouds.azure.storage.blob.domain.ListBlobsResponse; import org.jclouds.azure.storage.blob.domain.ListBlobsResponse;
import org.jclouds.azure.storage.blob.domain.TreeSetListBlobsResponse; import org.jclouds.azure.storage.blob.domain.TreeSetListBlobsResponse;
import org.jclouds.azure.storage.domain.BoundedSortedSet; import org.jclouds.azure.storage.domain.BoundedSortedSet;
import org.jclouds.http.HttpUtils;
import org.jclouds.http.functions.BaseHandlerTest; import org.jclouds.http.functions.BaseHandlerTest;
import org.jclouds.util.DateService; import org.jclouds.util.DateService;
import org.testng.annotations.BeforeTest; import org.testng.annotations.BeforeTest;
@ -70,24 +69,24 @@ public class ContainerNameEnumerationResultsHandlerTest extends BaseHandlerTest
.create("http://myaccount.blob.core.windows.net/mycontainer/blob1.txt"), .create("http://myaccount.blob.core.windows.net/mycontainer/blob1.txt"),
dateService dateService
.rfc822DateParse("Thu, 18 Sep 2008 18:41:57 GMT"), .rfc822DateParse("Thu, 18 Sep 2008 18:41:57 GMT"),
HttpUtils.fromHexString("0x8CAE7D55D050B8B"), 8, "0x8CAE7D55D050B8B", 8, "text/plain; charset=UTF-8",
"text/plain; charset=UTF-8", null, null, null), null, null, null),
new BlobMetadata( new BlobMetadata(
"blob2.txt", "blob2.txt",
URI URI
.create("http://myaccount.blob.core.windows.net/mycontainer/blob2.txt"), .create("http://myaccount.blob.core.windows.net/mycontainer/blob2.txt"),
dateService dateService
.rfc822DateParse("Thu, 18 Sep 2008 18:41:57 GMT"), .rfc822DateParse("Thu, 18 Sep 2008 18:41:57 GMT"),
HttpUtils.fromHexString("0x8CAE7D55CF6C339"), 14, "0x8CAE7D55CF6C339", 14, "text/plain; charset=UTF-8",
"text/plain; charset=UTF-8", null, null, null), null, null, null),
new BlobMetadata( new BlobMetadata(
"newblob1.txt", "newblob1.txt",
URI URI
.create("http://myaccount.blob.core.windows.net/mycontainer/newblob1.txt"), .create("http://myaccount.blob.core.windows.net/mycontainer/newblob1.txt"),
dateService dateService
.rfc822DateParse("Thu, 18 Sep 2008 18:41:57 GMT"), .rfc822DateParse("Thu, 18 Sep 2008 18:41:57 GMT"),
HttpUtils.fromHexString("0x8CAE7D55CF6C339"), 25, "0x8CAE7D55CF6C339", 25, "text/plain; charset=UTF-8",
"text/plain; charset=UTF-8", null, null, null) null, null, null)
), null, null, 4, "newblob2.txt", null, "myfolder/"); ), null, null, 4, "newblob2.txt", null, "myfolder/");

View File

@ -50,7 +50,7 @@ public interface BlobStore<C extends ContainerMetadata, M extends BlobMetadata,
Future<? extends SortedSet<M>> listBlobs(String container); Future<? extends SortedSet<M>> listBlobs(String container);
Future<byte[]> putBlob(String container, B blob); Future<String> putBlob(String container, B blob);
Future<B> getBlob(String container, String key); Future<B> getBlob(String container, String key);

View File

@ -47,7 +47,7 @@ public class BindBlobToEntity implements Binder {
Blob<?> object = (Blob<?>) entity; Blob<?> object = (Blob<?>) entity;
for (String key : object.getMetadata().getUserMetadata().keySet()) { for (String key : object.getMetadata().getUserMetadata().keySet()) {
request.getHeaders().putAll(key.startsWith(metadataPrefix) ? key : metadataPrefix + key, request.getHeaders().put(key.startsWith(metadataPrefix) ? key : metadataPrefix + key,
object.getMetadata().getUserMetadata().get(key)); object.getMetadata().getUserMetadata().get(key));
} }
request.setEntity(checkNotNull(object.getData(), "object.getContent()")); request.setEntity(checkNotNull(object.getData(), "object.getContent()"));

View File

@ -27,7 +27,7 @@ import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import java.io.Serializable; import java.io.Serializable;
import java.util.Arrays; import java.util.Map;
import javax.inject.Inject; import javax.inject.Inject;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
@ -35,6 +35,7 @@ import javax.ws.rs.core.MediaType;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import com.google.common.collect.HashMultimap; import com.google.common.collect.HashMultimap;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
/** /**
@ -47,22 +48,22 @@ public class BlobMetadata implements Comparable<BlobMetadata>, Serializable {
private static final long serialVersionUID = -5932618957134612231L; private static final long serialVersionUID = -5932618957134612231L;
protected String key; protected String key;
protected byte[] eTag; protected String eTag;
protected volatile long size = -1; protected volatile long size = -1;
private byte[] contentMD5; private byte[] contentMD5;
protected Multimap<String, String> allHeaders = HashMultimap.create(); protected Multimap<String, String> allHeaders = HashMultimap.create();
protected Multimap<String, String> userMetadata = HashMultimap.create(); protected Map<String, String> userMetadata = Maps.newHashMap();
protected DateTime lastModified; protected DateTime lastModified;
protected String dataType = MediaType.APPLICATION_OCTET_STREAM; protected String dataType = MediaType.APPLICATION_OCTET_STREAM;
@Override @Override
public String toString() { public String toString() {
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
builder.append("BlobMetadata [key=").append(key).append(", eTag=").append( builder.append("BlobMetadata [key=").append(key).append(", eTag=").append(eTag).append(
Arrays.toString(eTag)).append(", lastModified=").append(lastModified).append( ", lastModified=").append(lastModified).append(", size=").append(size).append(
", size=").append(size).append(", dataType=").append(dataType).append( ", dataType=").append(dataType).append(", userMetadata=").append(userMetadata)
", userMetadata=").append(userMetadata).append("]"); .append("]");
return builder.toString(); return builder.toString();
} }
@ -71,7 +72,7 @@ public class BlobMetadata implements Comparable<BlobMetadata>, Serializable {
final int prime = 31; final int prime = 31;
int result = 1; int result = 1;
result = prime * result + ((dataType == null) ? 0 : dataType.hashCode()); result = prime * result + ((dataType == null) ? 0 : dataType.hashCode());
result = prime * result + Arrays.hashCode(eTag); result = prime * result + ((eTag == null) ? 0 : eTag.hashCode());
result = prime * result + ((key == null) ? 0 : key.hashCode()); result = prime * result + ((key == null) ? 0 : key.hashCode());
result = prime * result + ((lastModified == null) ? 0 : lastModified.hashCode()); result = prime * result + ((lastModified == null) ? 0 : lastModified.hashCode());
result = prime * result + (int) (size ^ (size >>> 32)); result = prime * result + (int) (size ^ (size >>> 32));
@ -93,7 +94,10 @@ public class BlobMetadata implements Comparable<BlobMetadata>, Serializable {
return false; return false;
} else if (!dataType.equals(other.dataType)) } else if (!dataType.equals(other.dataType))
return false; return false;
if (!Arrays.equals(eTag, other.eTag)) if (eTag == null) {
if (other.eTag != null)
return false;
} else if (!eTag.equals(other.eTag))
return false; return false;
if (key == null) { if (key == null) {
if (other.key != null) if (other.key != null)
@ -197,34 +201,25 @@ public class BlobMetadata implements Comparable<BlobMetadata>, Serializable {
} }
} }
public void setETag(byte[] eTag) { public void setETag(String eTag) {
if (eTag != null) { this.eTag = eTag;
this.eTag = new byte[eTag.length];
System.arraycopy(eTag, 0, this.eTag, 0, eTag.length);
}
} }
/** /**
* @return the eTag value stored in the Etag header returned by HTTP. * @return the eTag value stored in the Etag header returned by HTTP.
*/ */
public byte[] getETag() { public String getETag() {
if (eTag != null) { return eTag;
byte[] retval = new byte[eTag.length];
System.arraycopy(this.eTag, 0, retval, 0, eTag.length);
return retval;
} else {
return null;
}
} }
public void setUserMetadata(Multimap<String, String> userMetadata) { public void setUserMetadata(Map<String, String> userMetadata) {
this.userMetadata = userMetadata; this.userMetadata = userMetadata;
} }
/** /**
* Any key-value pairs associated with the object. * Any key-value pairs associated with the object.
*/ */
public Multimap<String, String> getUserMetadata() { public Map<String, String> getUserMetadata() {
return userMetadata; return userMetadata;
} }

View File

@ -50,8 +50,7 @@ public class ParseSystemAndUserMetadataFromHeaders<M extends BlobMetadata> exten
@Inject @Inject
public ParseSystemAndUserMetadataFromHeaders(DateService dateParser, public ParseSystemAndUserMetadataFromHeaders(DateService dateParser,
@Named(PROPERTY_USER_METADATA_PREFIX) String metadataPrefix, @Named(PROPERTY_USER_METADATA_PREFIX) String metadataPrefix, Provider<M> metadataFactory) {
Provider<M> metadataFactory) {
super(metadataFactory); super(metadataFactory);
this.dateParser = dateParser; this.dateParser = dateParser;
this.metadataPrefix = metadataPrefix; this.metadataPrefix = metadataPrefix;
@ -102,7 +101,7 @@ public class ParseSystemAndUserMetadataFromHeaders<M extends BlobMetadata> exten
protected void addETagTo(HttpResponse from, M metadata) { protected void addETagTo(HttpResponse from, M metadata) {
String eTag = from.getFirstHeaderOrNull(HttpHeaders.ETAG); String eTag = from.getFirstHeaderOrNull(HttpHeaders.ETAG);
if (metadata.getETag() == null && eTag != null) { if (metadata.getETag() == null && eTag != null) {
metadata.setETag(HttpUtils.fromHexString(eTag.replaceAll("\"", ""))); metadata.setETag(eTag);
} }
} }

View File

@ -157,11 +157,11 @@ public class BlobMapImpl<C extends ContainerMetadata, M extends BlobMetadata, B
*/ */
public void putAll(Map<? extends String, ? extends B> map) { public void putAll(Map<? extends String, ? extends B> map) {
try { try {
Set<Future<byte[]>> puts = Sets.newHashSet(); Set<Future<String>> puts = Sets.newHashSet();
for (B object : map.values()) { for (B object : map.values()) {
puts.add(connection.putBlob(containerName, object)); puts.add(connection.putBlob(containerName, object));
} }
for (Future<byte[]> put : puts) for (Future<String> put : puts)
// this will throw an exception if there was a problem // this will throw an exception if there was a problem
put.get(requestTimeoutMilliseconds, TimeUnit.MILLISECONDS); put.get(requestTimeoutMilliseconds, TimeUnit.MILLISECONDS);
} catch (Exception e) { } catch (Exception e) {

View File

@ -223,7 +223,7 @@ public class InputStreamMapImpl<C extends ContainerMetadata, M extends BlobMetad
@VisibleForTesting @VisibleForTesting
void putAllInternal(Map<? extends String, ? extends Object> map) { void putAllInternal(Map<? extends String, ? extends Object> map) {
try { try {
Set<Future<byte[]>> puts = Sets.newHashSet(); Set<Future<String>> puts = Sets.newHashSet();
for (Map.Entry<? extends String, ? extends Object> entry : map.entrySet()) { for (Map.Entry<? extends String, ? extends Object> entry : map.entrySet()) {
B object = blobFactory.get(); B object = blobFactory.get();
object.getMetadata().setKey(entry.getKey()); object.getMetadata().setKey(entry.getKey());
@ -234,7 +234,7 @@ public class InputStreamMapImpl<C extends ContainerMetadata, M extends BlobMetad
// / response transformer set key on the way out. // / response transformer set key on the way out.
// / ExceptionHandler convert 404 to NOT_FOUND // / ExceptionHandler convert 404 to NOT_FOUND
} }
for (Future<byte[]> put : puts) for (Future<String> put : puts)
// this will throw an exception if there was a problem // this will throw an exception if there was a problem
put.get(requestTimeoutMilliseconds, TimeUnit.MILLISECONDS); put.get(requestTimeoutMilliseconds, TimeUnit.MILLISECONDS);
} catch (Exception e) { } catch (Exception e) {

View File

@ -24,7 +24,6 @@
package org.jclouds.blobstore.domain; package org.jclouds.blobstore.domain;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotSame;
import java.io.File; import java.io.File;
@ -43,13 +42,4 @@ public class BlobTest {
assertEquals(object.getMetadata().getContentType(), MediaType.APPLICATION_OCTET_STREAM); assertEquals(object.getMetadata().getContentType(), MediaType.APPLICATION_OCTET_STREAM);
} }
@Test
void testETagCopyingNotReference() {
byte[] eTag = new byte[12];
Blob<BlobMetadata> object = new Blob<BlobMetadata>("test");
object.getMetadata().setETag(eTag);
byte[] returnedETag = object.getMetadata().getETag();
assertNotSame(eTag, returnedETag);
}
} }

View File

@ -145,7 +145,7 @@ public class ParseBlobMetadataFromHeadersTest {
from.getHeaders().put(HttpHeaders.ETAG, "0xfeb"); from.getHeaders().put(HttpHeaders.ETAG, "0xfeb");
BlobMetadata metadata = new BlobMetadata("test"); BlobMetadata metadata = new BlobMetadata("test");
parser.addETagTo(from, metadata); parser.addETagTo(from, metadata);
assertEquals(metadata.getETag(), HttpUtils.fromHexString("0xfeb")); assertEquals(metadata.getETag(), "0xfeb");
} }
@Test @Test
@ -155,6 +155,6 @@ public class ParseBlobMetadataFromHeadersTest {
from.setHeaders(allHeaders); from.setHeaders(allHeaders);
BlobMetadata metadata = new BlobMetadata("test"); BlobMetadata metadata = new BlobMetadata("test");
parser.addUserMetadataTo(from, metadata); parser.addUserMetadataTo(from, metadata);
assertEquals(metadata.getUserMetadata().get("key"), Collections.singletonList("value")); assertEquals(metadata.getUserMetadata().get("key"), "value");
} }
} }

View File

@ -37,7 +37,6 @@ import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.Collections;
import java.util.SortedSet; import java.util.SortedSet;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -142,15 +141,15 @@ public class BaseBlobIntegrationTest<S, C extends ContainerMetadata, M extends B
String key = "apples"; String key = "apples";
addObjectAndValidateContent(containerName, key); String goodETag = addObjectAndValidateContent(containerName, key);
context.getBlobStore().getBlob(containerName, key, ifETagMatches(goodETag)).get(30, context.getBlobStore().getBlob(containerName, key, ifETagMatches(goodETag)).get(30,
TimeUnit.SECONDS); TimeUnit.SECONDS);
validateContent(containerName, key); validateContent(containerName, key);
try { try {
context.getBlobStore().getBlob(containerName, key, ifETagMatches(badETag)).get(30, context.getBlobStore().getBlob(containerName, key, ifETagMatches("powerfrisbee")).get(
TimeUnit.SECONDS); 30, TimeUnit.SECONDS);
validateContent(containerName, key); validateContent(containerName, key);
} catch (ExecutionException e) { } catch (ExecutionException e) {
if (e.getCause() instanceof HttpResponseException) { if (e.getCause() instanceof HttpResponseException) {
@ -175,10 +174,10 @@ public class BaseBlobIntegrationTest<S, C extends ContainerMetadata, M extends B
String key = "apples"; String key = "apples";
addObjectAndValidateContent(containerName, key); String goodETag = addObjectAndValidateContent(containerName, key);
context.getBlobStore().getBlob(containerName, key, ifETagDoesntMatch(badETag)).get(30, context.getBlobStore().getBlob(containerName, key, ifETagDoesntMatch("powerfrisbee")).get(
TimeUnit.SECONDS); 30, TimeUnit.SECONDS);
validateContent(containerName, key); validateContent(containerName, key);
try { try {
@ -278,10 +277,11 @@ public class BaseBlobIntegrationTest<S, C extends ContainerMetadata, M extends B
} }
} }
private void addObjectAndValidateContent(String sourcecontainerName, String sourceKey) private String addObjectAndValidateContent(String sourcecontainerName, String sourceKey)
throws InterruptedException, ExecutionException, TimeoutException, IOException { throws InterruptedException, ExecutionException, TimeoutException, IOException {
addBlobToContainer(sourcecontainerName, sourceKey); String eTag = addBlobToContainer(sourcecontainerName, sourceKey);
validateContent(sourcecontainerName, sourceKey); validateContent(sourcecontainerName, sourceKey);
return eTag;
} }
@Test(groups = { "integration", "live" }) @Test(groups = { "integration", "live" })
@ -397,8 +397,7 @@ public class BaseBlobIntegrationTest<S, C extends ContainerMetadata, M extends B
protected void validateMetadata(M metadata) { protected void validateMetadata(M metadata) {
assertEquals(metadata.getContentType(), "text/plain"); assertEquals(metadata.getContentType(), "text/plain");
assertEquals(metadata.getSize(), TEST_STRING.length()); assertEquals(metadata.getSize(), TEST_STRING.length());
assertEquals(metadata.getUserMetadata().get("adrian"), Collections assertEquals(metadata.getUserMetadata().get("adrian"), "powderpuff");
.singletonList("powderpuff"));
assertEquals(metadata.getContentMD5(), HttpUtils.md5(TEST_STRING.getBytes())); assertEquals(metadata.getContentMD5(), HttpUtils.md5(TEST_STRING.getBytes()));
} }

View File

@ -44,13 +44,11 @@ import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.domain.BlobMetadata; import org.jclouds.blobstore.domain.BlobMetadata;
import org.jclouds.blobstore.domain.ContainerMetadata; import org.jclouds.blobstore.domain.ContainerMetadata;
import org.jclouds.blobstore.util.BlobStoreUtils; import org.jclouds.blobstore.util.BlobStoreUtils;
import org.jclouds.http.HttpUtils;
import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule; import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule;
import org.jclouds.util.Utils; import org.jclouds.util.Utils;
import org.testng.ITestContext; import org.testng.ITestContext;
import org.testng.annotations.AfterClass; import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeGroups;
import org.testng.annotations.BeforeSuite; import org.testng.annotations.BeforeSuite;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
@ -65,9 +63,6 @@ public class BaseBlobStoreIntegrationTest<S, C extends ContainerMetadata, M exte
public static long INCONSISTENCY_WINDOW = 5000; public static long INCONSISTENCY_WINDOW = 5000;
protected static volatile AtomicInteger containerIndex = new AtomicInteger(0); protected static volatile AtomicInteger containerIndex = new AtomicInteger(0);
protected byte[] goodETag;
protected byte[] badETag;
protected volatile BlobStoreContext<S, C, M, B> context; protected volatile BlobStoreContext<S, C, M, B> context;
protected static volatile int containerCount = 20; protected static volatile int containerCount = 20;
public static final String CONTAINER_PREFIX = System.getProperty("user.name") + "-blobstore"; public static final String CONTAINER_PREFIX = System.getProperty("user.name") + "-blobstore";
@ -77,12 +72,6 @@ public class BaseBlobStoreIntegrationTest<S, C extends ContainerMetadata, M exte
private volatile static BlockingQueue<String> containerJsr330 = new ArrayBlockingQueue<String>( private volatile static BlockingQueue<String> containerJsr330 = new ArrayBlockingQueue<String>(
containerCount); containerCount);
@BeforeGroups(groups = { "integration", "live" })
public void setupTags() throws IOException {
goodETag = HttpUtils.md5(TEST_STRING);
badETag = HttpUtils.md5("alf");
}
/** /**
* There are a lot of retries here mainly from experience running inside amazon EC2. * There are a lot of retries here mainly from experience running inside amazon EC2.
*/ */
@ -239,17 +228,17 @@ public class BaseBlobStoreIntegrationTest<S, C extends ContainerMetadata, M exte
createContainerAndEnsureEmpty(context, containerName); createContainerAndEnsureEmpty(context, containerName);
} }
protected void addBlobToContainer(String sourceContainer, String key) protected String addBlobToContainer(String sourceContainer, String key)
throws InterruptedException, ExecutionException, TimeoutException, IOException { throws InterruptedException, ExecutionException, TimeoutException, IOException {
B sourceObject = context.newBlob(key); B sourceObject = context.newBlob(key);
sourceObject.getMetadata().setContentType("text/xml"); sourceObject.getMetadata().setContentType("text/xml");
sourceObject.setData(TEST_STRING); sourceObject.setData(TEST_STRING);
addBlobToContainer(sourceContainer, sourceObject); return addBlobToContainer(sourceContainer, sourceObject);
} }
protected void addBlobToContainer(String sourceContainer, B object) throws InterruptedException, protected String addBlobToContainer(String sourceContainer, B object)
ExecutionException, TimeoutException, IOException { throws InterruptedException, ExecutionException, TimeoutException, IOException {
context.getBlobStore().putBlob(sourceContainer, object).get(30, TimeUnit.SECONDS); return context.getBlobStore().putBlob(sourceContainer, object).get(30, TimeUnit.SECONDS);
} }
protected B validateContent(String sourceContainer, String key) throws InterruptedException, protected B validateContent(String sourceContainer, String key) throws InterruptedException,

View File

@ -34,7 +34,6 @@ import java.io.ObjectInput;
import java.io.ObjectInputStream; import java.io.ObjectInputStream;
import java.io.ObjectOutput; import java.io.ObjectOutput;
import java.io.ObjectOutputStream; import java.io.ObjectOutputStream;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -71,10 +70,9 @@ import org.joda.time.DateTime;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Multimap; import com.google.common.collect.Maps;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import com.google.inject.internal.Nullable; import com.google.inject.internal.Nullable;
@ -177,8 +175,8 @@ public class StubBlobStore<C extends ContainerMetadata, M extends BlobMetadata,
} }
private void convertUserMetadataKeysToLowercase(M metadata) { private void convertUserMetadataKeysToLowercase(M metadata) {
Multimap<String, String> lowerCaseUserMetadata = HashMultimap.create(); Map<String, String> lowerCaseUserMetadata = Maps.newHashMap();
for (Entry<String, String> entry : metadata.getUserMetadata().entries()) { for (Entry<String, String> entry : metadata.getUserMetadata().entrySet()) {
lowerCaseUserMetadata.put(entry.getKey().toLowerCase(), entry.getValue()); lowerCaseUserMetadata.put(entry.getKey().toLowerCase(), entry.getValue());
} }
metadata.setUserMetadata(lowerCaseUserMetadata); metadata.setUserMetadata(lowerCaseUserMetadata);
@ -385,7 +383,7 @@ public class StubBlobStore<C extends ContainerMetadata, M extends BlobMetadata,
}, response)); }, response));
} }
public Future<byte[]> putBlob(final String bucketName, final B object) { public Future<String> putBlob(final String bucketName, final B object) {
Map<String, B> container = getContainerToBlobs().get(bucketName); Map<String, B> container = getContainerToBlobs().get(bucketName);
if (container == null) { if (container == null) {
new RuntimeException("bucketName not found: " + bucketName); new RuntimeException("bucketName not found: " + bucketName);
@ -394,9 +392,10 @@ public class StubBlobStore<C extends ContainerMetadata, M extends BlobMetadata,
M newMd = copy(object.getMetadata()); M newMd = copy(object.getMetadata());
newMd.setLastModified(new DateTime()); newMd.setLastModified(new DateTime());
byte[] data = toByteArray(object.getData()); byte[] data = toByteArray(object.getData());
final byte[] eTag = HttpUtils.md5(data); final byte[] md5 = HttpUtils.md5(data);
final String eTag = HttpUtils.toHexString(md5);
newMd.setETag(eTag); newMd.setETag(eTag);
newMd.setContentMD5(eTag); newMd.setContentMD5(md5);
newMd.setContentType(object.getMetadata().getContentType()); newMd.setContentType(object.getMetadata().getContentType());
B blob = blobProvider.get(); B blob = blobProvider.get();
@ -407,15 +406,15 @@ public class StubBlobStore<C extends ContainerMetadata, M extends BlobMetadata,
// Set HTTP headers to match metadata // Set HTTP headers to match metadata
newMd.getAllHeaders().put(HttpHeaders.LAST_MODIFIED, newMd.getAllHeaders().put(HttpHeaders.LAST_MODIFIED,
dateService.rfc822DateFormat(newMd.getLastModified())); dateService.rfc822DateFormat(newMd.getLastModified()));
newMd.getAllHeaders().put(HttpHeaders.ETAG, HttpUtils.toHexString(eTag)); newMd.getAllHeaders().put(HttpHeaders.ETAG, eTag);
newMd.getAllHeaders().put(HttpHeaders.CONTENT_TYPE, newMd.getContentType()); newMd.getAllHeaders().put(HttpHeaders.CONTENT_TYPE, newMd.getContentType());
newMd.getAllHeaders().put(HttpHeaders.CONTENT_LENGTH, newMd.getSize() + ""); newMd.getAllHeaders().put(HttpHeaders.CONTENT_LENGTH, newMd.getSize() + "");
for (Entry<String, String> userMD : newMd.getUserMetadata().entries()) { for (Entry<String, String> userMD : newMd.getUserMetadata().entrySet()) {
newMd.getAllHeaders().put(userMD.getKey(), userMD.getValue()); newMd.getAllHeaders().put(userMD.getKey(), userMD.getValue());
} }
return new FutureBase<byte[]>() { return new FutureBase<String>() {
public byte[] get() throws InterruptedException, ExecutionException { public String get() throws InterruptedException, ExecutionException {
return eTag; return eTag;
} }
}; };
@ -439,13 +438,11 @@ public class StubBlobStore<C extends ContainerMetadata, M extends BlobMetadata,
B object = realContents.get(key); B object = realContents.get(key);
if (options.getIfMatch() != null) { if (options.getIfMatch() != null) {
if (!Arrays.equals(object.getMetadata().getETag(), HttpUtils.fromHexString(options if (object.getMetadata().getETag().equals(options.getIfMatch()))
.getIfMatch().replaceAll("\"", ""))))
throwResponseException(412); throwResponseException(412);
} }
if (options.getIfNoneMatch() != null) { if (options.getIfNoneMatch() != null) {
if (Arrays.equals(object.getMetadata().getETag(), HttpUtils.fromHexString(options if (object.getMetadata().getETag().equals(options.getIfNoneMatch()))
.getIfNoneMatch().replaceAll("\"", ""))))
throwResponseException(304); throwResponseException(304);
} }
if (options.getIfModifiedSince() != null) { if (options.getIfModifiedSince() != null) {

View File

@ -28,7 +28,6 @@ import javax.ws.rs.core.HttpHeaders;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.jclouds.http.HttpException; import org.jclouds.http.HttpException;
import org.jclouds.http.HttpResponse; import org.jclouds.http.HttpResponse;
import org.jclouds.http.HttpUtils;
import com.google.common.base.Function; import com.google.common.base.Function;
@ -37,9 +36,9 @@ import com.google.common.base.Function;
* *
* @author Adrian Cole * @author Adrian Cole
*/ */
public class ParseETagHeader implements Function<HttpResponse, byte[]> { public class ParseETagHeader implements Function<HttpResponse, String> {
public byte[] apply(HttpResponse from) { public String apply(HttpResponse from) {
IOUtils.closeQuietly(from.getContent()); IOUtils.closeQuietly(from.getContent());
String eTag = from.getFirstHeaderOrNull(HttpHeaders.ETAG); String eTag = from.getFirstHeaderOrNull(HttpHeaders.ETAG);
@ -48,7 +47,7 @@ public class ParseETagHeader implements Function<HttpResponse, byte[]> {
eTag = from.getFirstHeaderOrNull("Etag"); eTag = from.getFirstHeaderOrNull("Etag");
} }
if (eTag != null) { if (eTag != null) {
return HttpUtils.fromHexString(eTag.replaceAll("\"", "")); return eTag;
} }
throw new HttpException("did not receive ETag"); throw new HttpException("did not receive ETag");
} }

View File

@ -32,7 +32,6 @@ import java.util.List;
import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.HttpHeaders;
import org.jclouds.http.HttpUtils;
import org.jclouds.util.DateService; import org.jclouds.util.DateService;
import org.joda.time.DateTime; import org.joda.time.DateTime;
@ -173,13 +172,12 @@ public class GetOptions extends BaseHttpRequestOptions {
* @throws UnsupportedEncodingException * @throws UnsupportedEncodingException
* if there was a problem converting this into an S3 eTag string * if there was a problem converting this into an S3 eTag string
*/ */
public GetOptions ifETagMatches(byte[] eTag) throws UnsupportedEncodingException { public GetOptions ifETagMatches(String eTag) throws UnsupportedEncodingException {
checkArgument(getIfNoneMatch() == null, checkArgument(getIfNoneMatch() == null,
"ifETagDoesntMatch() is not compatible with ifETagMatches()"); "ifETagDoesntMatch() is not compatible with ifETagMatches()");
checkArgument(getIfModifiedSince() == null, checkArgument(getIfModifiedSince() == null,
"ifModifiedSince() is not compatible with ifETagMatches()"); "ifModifiedSince() is not compatible with ifETagMatches()");
this.headers.put(HttpHeaders.IF_MATCH, String.format("\"%1$s\"", HttpUtils this.headers.put(HttpHeaders.IF_MATCH, String.format("\"%1$s\"", checkNotNull(eTag, "eTag")));
.toHexString(checkNotNull(eTag, "eTag"))));
return this; return this;
} }
@ -198,20 +196,20 @@ public class GetOptions extends BaseHttpRequestOptions {
/** /**
* The object should not have a eTag hash corresponding with the parameter <code>eTag</code>. * The object should not have a eTag hash corresponding with the parameter <code>eTag</code>.
* <p /> * <p />
* Not compatible with {@link #ifETagMatches(byte[])} or {@link #ifUnmodifiedSince(DateTime)} * Not compatible with {@link #ifETagMatches(String)} or {@link #ifUnmodifiedSince(DateTime)}
* *
* @param eTag * @param eTag
* hash representing the entity * hash representing the entity
* @throws UnsupportedEncodingException * @throws UnsupportedEncodingException
* if there was a problem converting this into an S3 eTag string * if there was a problem converting this into an S3 eTag string
*/ */
public GetOptions ifETagDoesntMatch(byte[] eTag) throws UnsupportedEncodingException { public GetOptions ifETagDoesntMatch(String eTag) throws UnsupportedEncodingException {
checkArgument(getIfMatch() == null, checkArgument(getIfMatch() == null,
"ifETagMatches() is not compatible with ifETagDoesntMatch()"); "ifETagMatches() is not compatible with ifETagDoesntMatch()");
checkArgument(getIfUnmodifiedSince() == null, checkArgument(getIfUnmodifiedSince() == null,
"ifUnmodifiedSince() is not compatible with ifETagDoesntMatch()"); "ifUnmodifiedSince() is not compatible with ifETagDoesntMatch()");
this.headers.put(HttpHeaders.IF_NONE_MATCH, String.format("\"%1$s\"", HttpUtils this.headers.put(HttpHeaders.IF_NONE_MATCH, String.format("\"%1$s\"", checkNotNull(eTag,
.toHexString(checkNotNull(eTag, "ifETagDoesntMatch")))); "ifETagDoesntMatch")));
return this; return this;
} }
@ -270,18 +268,17 @@ public class GetOptions extends BaseHttpRequestOptions {
} }
/** /**
* @see GetOptions#ifETagMatches(byte[]) * @see GetOptions#ifETagMatches(String)
*/ */
public static GetOptions ifETagMatches(byte[] eTag) throws UnsupportedEncodingException { public static GetOptions ifETagMatches(String eTag) throws UnsupportedEncodingException {
GetOptions options = new GetOptions(); GetOptions options = new GetOptions();
return options.ifETagMatches(eTag); return options.ifETagMatches(eTag);
} }
/** /**
* @see GetOptions#ifETagDoesntMatch(byte[]) * @see GetOptions#ifETagDoesntMatch(String)
*/ */
public static GetOptions ifETagDoesntMatch(byte[] eTag) public static GetOptions ifETagDoesntMatch(String eTag) throws UnsupportedEncodingException {
throws UnsupportedEncodingException {
GetOptions options = new GetOptions(); GetOptions options = new GetOptions();
return options.ifETagDoesntMatch(eTag); return options.ifETagDoesntMatch(eTag);
} }

View File

@ -0,0 +1,40 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
package org.jclouds.rest.annotations;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
/**
* Do not accept filters that were passed from the type.
*
* @author Adrian Cole
*/
@Target( { METHOD })
@Retention(RUNTIME)
public @interface OverrideRequestFilters {
}

View File

@ -49,4 +49,10 @@ public class GeneratedHttpRequest<T> extends HttpRequest {
URI newEndpoint = processor.replaceQuery(getEndpoint(), builder.build().getQuery()); URI newEndpoint = processor.replaceQuery(getEndpoint(), builder.build().getQuery());
setEndpoint(newEndpoint); setEndpoint(newEndpoint);
} }
public void replacePath(String path) {
UriBuilder builder = UriBuilder.fromUri(getEndpoint());
builder.replacePath(path);
setEndpoint(builder.build());
}
} }

View File

@ -80,6 +80,7 @@ import org.jclouds.rest.annotations.HostPrefixParam;
import org.jclouds.rest.annotations.MapBinder; import org.jclouds.rest.annotations.MapBinder;
import org.jclouds.rest.annotations.MapEntityParam; import org.jclouds.rest.annotations.MapEntityParam;
import org.jclouds.rest.annotations.MatrixParams; import org.jclouds.rest.annotations.MatrixParams;
import org.jclouds.rest.annotations.OverrideRequestFilters;
import org.jclouds.rest.annotations.ParamParser; import org.jclouds.rest.annotations.ParamParser;
import org.jclouds.rest.annotations.QueryParams; import org.jclouds.rest.annotations.QueryParams;
import org.jclouds.rest.annotations.RequestFilters; import org.jclouds.rest.annotations.RequestFilters;
@ -446,6 +447,8 @@ public class RestAnnotationProcessor<T> {
} }
} }
if (method.isAnnotationPresent(RequestFilters.class)) { if (method.isAnnotationPresent(RequestFilters.class)) {
if (method.isAnnotationPresent(OverrideRequestFilters.class))
request.getFilters().clear();
for (Class<? extends HttpRequestFilter> clazz : method.getAnnotation(RequestFilters.class) for (Class<? extends HttpRequestFilter> clazz : method.getAnnotation(RequestFilters.class)
.value()) { .value()) {
HttpRequestFilter instance = injector.getInstance(clazz); HttpRequestFilter instance = injector.getInstance(clazz);

View File

@ -35,7 +35,6 @@ import static org.testng.Assert.assertNull;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import org.jclouds.http.HttpUtils;
import org.jclouds.util.DateService; import org.jclouds.util.DateService;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.testng.annotations.BeforeTest; import org.testng.annotations.BeforeTest;
@ -49,7 +48,7 @@ import org.testng.annotations.Test;
@Test(groups = "unit", testName = "s3.GetOptionsTest") @Test(groups = "unit", testName = "s3.GetOptionsTest")
public class GetOptionsTest { public class GetOptionsTest {
private byte[] testBytes; private String etag;
private DateTime now; private DateTime now;
private String nowExpected; private String nowExpected;
@ -57,7 +56,7 @@ public class GetOptionsTest {
void setUp() { void setUp() {
now = new DateTime(); now = new DateTime();
nowExpected = new DateService().rfc822DateFormat(now); nowExpected = new DateService().rfc822DateFormat(now);
testBytes = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7 }; etag = "yrdy";
} }
@Test @Test
@ -213,7 +212,7 @@ public class GetOptionsTest {
@Test @Test
public void testIfETagMatches() throws UnsupportedEncodingException { public void testIfETagMatches() throws UnsupportedEncodingException {
GetOptions options = new GetOptions(); GetOptions options = new GetOptions();
options.ifETagMatches(testBytes); options.ifETagMatches(etag);
matchesHex(options.getIfMatch()); matchesHex(options.getIfMatch());
} }
@ -225,7 +224,7 @@ public class GetOptionsTest {
@Test @Test
public void testIfETagMatchesStatic() throws UnsupportedEncodingException { public void testIfETagMatchesStatic() throws UnsupportedEncodingException {
GetOptions options = ifETagMatches(testBytes); GetOptions options = ifETagMatches(etag);
matchesHex(options.getIfMatch()); matchesHex(options.getIfMatch());
} }
@ -237,7 +236,7 @@ public class GetOptionsTest {
@Test @Test
public void testIfETagDoesntMatch() throws UnsupportedEncodingException { public void testIfETagDoesntMatch() throws UnsupportedEncodingException {
GetOptions options = new GetOptions(); GetOptions options = new GetOptions();
options.ifETagDoesntMatch(testBytes); options.ifETagDoesntMatch(etag);
matchesHex(options.getIfNoneMatch()); matchesHex(options.getIfNoneMatch());
} }
@ -249,7 +248,7 @@ public class GetOptionsTest {
@Test @Test
public void testIfETagDoesntMatchStatic() throws UnsupportedEncodingException { public void testIfETagDoesntMatchStatic() throws UnsupportedEncodingException {
GetOptions options = ifETagDoesntMatch(testBytes); GetOptions options = ifETagDoesntMatch(etag);
matchesHex(options.getIfNoneMatch()); matchesHex(options.getIfNoneMatch());
} }
@ -259,7 +258,7 @@ public class GetOptionsTest {
} }
private void matchesHex(String match) throws UnsupportedEncodingException { private void matchesHex(String match) throws UnsupportedEncodingException {
String expected = "\"" + HttpUtils.toHexString(testBytes) + "\""; String expected = "\"" + etag + "\"";
assertEquals(match, expected); assertEquals(match, expected);
} }
@ -270,13 +269,13 @@ public class GetOptionsTest {
} }
public void testIfUnmodifiedAfterETagMatches() throws UnsupportedEncodingException { public void testIfUnmodifiedAfterETagMatches() throws UnsupportedEncodingException {
ifETagMatches(testBytes).ifUnmodifiedSince(now); ifETagMatches(etag).ifUnmodifiedSince(now);
} }
@Test(expectedExceptions = IllegalArgumentException.class) @Test(expectedExceptions = IllegalArgumentException.class)
public void testIfUnmodifiedAfterETagDoesntMatch() throws UnsupportedEncodingException { public void testIfUnmodifiedAfterETagDoesntMatch() throws UnsupportedEncodingException {
ifETagDoesntMatch(testBytes).ifUnmodifiedSince(now); ifETagDoesntMatch(etag).ifUnmodifiedSince(now);
} }
@Test(expectedExceptions = IllegalArgumentException.class) @Test(expectedExceptions = IllegalArgumentException.class)
@ -287,44 +286,44 @@ public class GetOptionsTest {
@Test(expectedExceptions = IllegalArgumentException.class) @Test(expectedExceptions = IllegalArgumentException.class)
public void testIfModifiedAfterETagMatches() throws UnsupportedEncodingException { public void testIfModifiedAfterETagMatches() throws UnsupportedEncodingException {
ifETagMatches(testBytes).ifModifiedSince(now); ifETagMatches(etag).ifModifiedSince(now);
} }
public void testIfModifiedAfterETagDoesntMatch() throws UnsupportedEncodingException { public void testIfModifiedAfterETagDoesntMatch() throws UnsupportedEncodingException {
ifETagDoesntMatch(testBytes).ifModifiedSince(now); ifETagDoesntMatch(etag).ifModifiedSince(now);
} }
@Test(expectedExceptions = IllegalArgumentException.class) @Test(expectedExceptions = IllegalArgumentException.class)
public void testETagMatchesAfterIfModified() throws UnsupportedEncodingException { public void testETagMatchesAfterIfModified() throws UnsupportedEncodingException {
ifModifiedSince(now).ifETagMatches(testBytes); ifModifiedSince(now).ifETagMatches(etag);
} }
public void testETagMatchesAfterIfUnmodified() throws UnsupportedEncodingException { public void testETagMatchesAfterIfUnmodified() throws UnsupportedEncodingException {
ifUnmodifiedSince(now).ifETagMatches(testBytes); ifUnmodifiedSince(now).ifETagMatches(etag);
} }
@Test(expectedExceptions = IllegalArgumentException.class) @Test(expectedExceptions = IllegalArgumentException.class)
public void testETagMatchesAfterETagDoesntMatch() throws UnsupportedEncodingException { public void testETagMatchesAfterETagDoesntMatch() throws UnsupportedEncodingException {
ifETagDoesntMatch(testBytes).ifETagMatches(testBytes); ifETagDoesntMatch(etag).ifETagMatches(etag);
} }
public void testETagDoesntMatchAfterIfModified() throws UnsupportedEncodingException { public void testETagDoesntMatchAfterIfModified() throws UnsupportedEncodingException {
ifModifiedSince(now).ifETagDoesntMatch(testBytes); ifModifiedSince(now).ifETagDoesntMatch(etag);
} }
@Test(expectedExceptions = IllegalArgumentException.class) @Test(expectedExceptions = IllegalArgumentException.class)
public void testETagDoesntMatchAfterIfUnmodified() throws UnsupportedEncodingException { public void testETagDoesntMatchAfterIfUnmodified() throws UnsupportedEncodingException {
ifUnmodifiedSince(now).ifETagDoesntMatch(testBytes); ifUnmodifiedSince(now).ifETagDoesntMatch(etag);
} }
@Test(expectedExceptions = IllegalArgumentException.class) @Test(expectedExceptions = IllegalArgumentException.class)
public void testETagDoesntMatchAfterETagMatches() throws UnsupportedEncodingException { public void testETagDoesntMatchAfterETagMatches() throws UnsupportedEncodingException {
ifETagMatches(testBytes).ifETagDoesntMatch(testBytes); ifETagMatches(etag).ifETagDoesntMatch(etag);
} }
} }

View File

@ -80,6 +80,7 @@ import org.jclouds.rest.annotations.HostPrefixParam;
import org.jclouds.rest.annotations.MapBinder; import org.jclouds.rest.annotations.MapBinder;
import org.jclouds.rest.annotations.MapEntityParam; import org.jclouds.rest.annotations.MapEntityParam;
import org.jclouds.rest.annotations.MatrixParams; import org.jclouds.rest.annotations.MatrixParams;
import org.jclouds.rest.annotations.OverrideRequestFilters;
import org.jclouds.rest.annotations.ParamParser; import org.jclouds.rest.annotations.ParamParser;
import org.jclouds.rest.annotations.QueryParams; import org.jclouds.rest.annotations.QueryParams;
import org.jclouds.rest.annotations.RequestFilters; import org.jclouds.rest.annotations.RequestFilters;
@ -496,6 +497,12 @@ public class RestAnnotationProcessorTest {
@RequestFilters(TestRequestFilter2.class) @RequestFilters(TestRequestFilter2.class)
public void get() { public void get() {
} }
@GET
@OverrideRequestFilters
@RequestFilters(TestRequestFilter2.class)
public void getOverride() {
}
} }
@Test @Test
@ -508,6 +515,14 @@ public class RestAnnotationProcessorTest {
assertEquals(httpMethod.getFilters().get(1).getClass(), TestRequestFilter2.class); assertEquals(httpMethod.getFilters().get(1).getClass(), TestRequestFilter2.class);
} }
public void testRequestFilterOverride() throws SecurityException, NoSuchMethodException {
Method method = TestRequestFilter.class.getMethod("getOverride");
HttpRequest httpMethod = factory(TestRequestFilter.class).createRequest(method,
new Object[] {});
assertEquals(httpMethod.getFilters().size(), 1);
assertEquals(httpMethod.getFilters().get(0).getClass(), TestRequestFilter2.class);
}
@SkipEncoding('/') @SkipEncoding('/')
@Endpoint(Localhost.class) @Endpoint(Localhost.class)
public class TestEncoding { public class TestEncoding {

View File

@ -45,7 +45,7 @@ import org.jclouds.mezeo.pcs2.domain.FileMetadata;
import org.jclouds.mezeo.pcs2.domain.PCSFile; import org.jclouds.mezeo.pcs2.domain.PCSFile;
import org.jclouds.mezeo.pcs2.endpoints.RootContainer; import org.jclouds.mezeo.pcs2.endpoints.RootContainer;
import org.jclouds.mezeo.pcs2.endpoints.WebDAV; import org.jclouds.mezeo.pcs2.endpoints.WebDAV;
import org.jclouds.mezeo.pcs2.functions.AddMetadataAndParseResourceIdIntoBytes; import org.jclouds.mezeo.pcs2.functions.AddMetadataAndReturnId;
import org.jclouds.mezeo.pcs2.functions.AssembleBlobFromContentAndMetadataCache; import org.jclouds.mezeo.pcs2.functions.AssembleBlobFromContentAndMetadataCache;
import org.jclouds.mezeo.pcs2.functions.ContainerAndFileNameToResourceId; import org.jclouds.mezeo.pcs2.functions.ContainerAndFileNameToResourceId;
import org.jclouds.mezeo.pcs2.functions.ContainerNameToResourceId; import org.jclouds.mezeo.pcs2.functions.ContainerNameToResourceId;
@ -116,24 +116,22 @@ public interface PCSBlobStore extends BlobStore<ContainerMetadata, FileMetadata,
Future<? extends SortedSet<FileMetadata>> listBlobs( Future<? extends SortedSet<FileMetadata>> listBlobs(
@PathParam("containerResourceId") @ParamParser(ContainerNameToResourceId.class) String containerName); @PathParam("containerResourceId") @ParamParser(ContainerNameToResourceId.class) String containerName);
@PUT @PUT
@Path("/files/{fileResourceId}/content") @Path("/files/{fileResourceId}/content")
@Endpoint(PCS.class) @Endpoint(PCS.class)
@ResponseParser(AddMetadataAndParseResourceIdIntoBytes.class) @ResponseParser(AddMetadataAndReturnId.class)
@PathParam("fileResourceId") @PathParam("fileResourceId")
@ParamParser(CreateSubFolderIfNotExistsAndNewFileResource.class) @ParamParser(CreateSubFolderIfNotExistsAndNewFileResource.class)
Future<byte[]> putBlob(String containerName, Future<String> putBlob(String containerName, @BinderParam(BindDataToEntity.class) PCSFile object);
@BinderParam(BindDataToEntity.class) PCSFile object);
// @POST
// @POST // @Path("/containers/{containerResourceId}/contents")
// @Path("/containers/{containerResourceId}/contents") // @Endpoint(PCS.class)
// @Endpoint(PCS.class) // @ResponseParser(AddMetadataAndParseResourceIdIntoBytes.class)
// @ResponseParser(AddMetadataAndParseResourceIdIntoBytes.class) // @PathParam("containerResourceId")
// @PathParam("containerResourceId") // @ParamParser(CreateSubFolderIfNotExistsAndGetResourceId.class)
// @ParamParser(CreateSubFolderIfNotExistsAndGetResourceId.class) // Future<byte[]> putBlob(String containerName,
// Future<byte[]> putBlob(String containerName, // @EntityParam(BlobAsMultipartFormBinder.class) PCSFile object);
// @EntityParam(BlobAsMultipartFormBinder.class) PCSFile object);
@DELETE @DELETE
@ExceptionParser(ReturnVoidOnNotFoundOr404.class) @ExceptionParser(ReturnVoidOnNotFoundOr404.class)
@ -156,7 +154,8 @@ public interface PCSBlobStore extends BlobStore<ContainerMetadata, FileMetadata,
@Path("{container}/{key}") @Path("{container}/{key}")
@Endpoint(WebDAV.class) @Endpoint(WebDAV.class)
@ResponseParser(AssembleBlobFromContentAndMetadataCache.class) @ResponseParser(AssembleBlobFromContentAndMetadataCache.class)
Future<PCSFile> getBlob(@PathParam("container") String container, @PathParam("key") String key, GetOptions options); Future<PCSFile> getBlob(@PathParam("container") String container, @PathParam("key") String key,
GetOptions options);
@GET @GET
@ExceptionParser(ThrowKeyNotFoundOn404.class) @ExceptionParser(ThrowKeyNotFoundOn404.class)

View File

@ -24,6 +24,7 @@
package org.jclouds.mezeo.pcs2; package org.jclouds.mezeo.pcs2;
import java.net.URI; import java.net.URI;
import java.util.Map;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import javax.ws.rs.GET; import javax.ws.rs.GET;
@ -32,7 +33,7 @@ import javax.ws.rs.Path;
import javax.ws.rs.PathParam; import javax.ws.rs.PathParam;
import org.jclouds.http.filters.BasicAuthentication; import org.jclouds.http.filters.BasicAuthentication;
import org.jclouds.mezeo.pcs2.functions.AddEntryIntoMultiMap; import org.jclouds.mezeo.pcs2.functions.AddEntryIntoMap;
import org.jclouds.rest.annotations.BinderParam; import org.jclouds.rest.annotations.BinderParam;
import org.jclouds.rest.annotations.Endpoint; import org.jclouds.rest.annotations.Endpoint;
import org.jclouds.rest.annotations.RequestFilters; import org.jclouds.rest.annotations.RequestFilters;
@ -40,8 +41,6 @@ import org.jclouds.rest.annotations.ResponseParser;
import org.jclouds.rest.annotations.SkipEncoding; import org.jclouds.rest.annotations.SkipEncoding;
import org.jclouds.rest.binders.BindToStringEntity; import org.jclouds.rest.binders.BindToStringEntity;
import com.google.common.collect.Multimap;
/** /**
* Provides access to Mezeo PCS v2 via their REST API. * Provides access to Mezeo PCS v2 via their REST API.
* <p/> * <p/>
@ -62,6 +61,6 @@ public interface PCSUtil {
@PathParam("key") String key, @BinderParam(BindToStringEntity.class) String value); @PathParam("key") String key, @BinderParam(BindToStringEntity.class) String value);
@GET @GET
@ResponseParser(AddEntryIntoMultiMap.class) @ResponseParser(AddEntryIntoMap.class)
Future<Void> addEntryToMultiMap(Multimap<String, String> map, String key, @Endpoint URI value); Future<Void> addEntryToMap(Map<String, String> map, String key, @Endpoint URI value);
} }

View File

@ -24,7 +24,6 @@
package org.jclouds.mezeo.pcs2.domain; package org.jclouds.mezeo.pcs2.domain;
import java.net.URI; import java.net.URI;
import java.util.Arrays;
import org.jclouds.mezeo.pcs2.util.PCSUtils; import org.jclouds.mezeo.pcs2.util.PCSUtils;
import org.joda.time.DateTime; import org.joda.time.DateTime;
@ -51,7 +50,7 @@ public class FileMetadata extends org.jclouds.blobstore.domain.BlobMetadata {
return "FileMetadata [key=" + key + ", created=" + created + ", isInProject=" + isInProject return "FileMetadata [key=" + key + ", created=" + created + ", isInProject=" + isInProject
+ ", isPublic=" + isPublic + ", isShared=" + isShared + ", owner=" + owner + ", isPublic=" + isPublic + ", isShared=" + isShared + ", owner=" + owner
+ ", url=" + url + ", version=" + version + ", allHeaders=" + allHeaders + ", url=" + url + ", version=" + version + ", allHeaders=" + allHeaders
+ ", dataType=" + dataType + ", eTag=" + Arrays.toString(eTag) + ", accessed=" + ", dataType=" + dataType + ", eTag=" + eTag + ", accessed="
+ accessed + ", lastModified=" + lastModified + ", size=" + size + ", userMetadata=" + accessed + ", lastModified=" + lastModified + ", size=" + size + ", userMetadata="
+ userMetadata + "]"; + userMetadata + "]";
} }
@ -138,8 +137,7 @@ public class FileMetadata extends org.jclouds.blobstore.domain.BlobMetadata {
this.isInProject = isInProject; this.isInProject = isInProject;
this.version = version; this.version = version;
this.isPublic = isPublic; this.isPublic = isPublic;
byte[] eTag = PCSUtils.getETag(url); setETag(PCSUtils.getId(url));
setETag(eTag);
} }
public FileMetadata(String key) { public FileMetadata(String key) {

View File

@ -3,6 +3,7 @@ package org.jclouds.mezeo.pcs2.functions;
import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Preconditions.checkState;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.util.Map;
import javax.inject.Inject; import javax.inject.Inject;
@ -12,18 +13,17 @@ import org.jclouds.rest.InvocationContext;
import org.jclouds.rest.internal.GeneratedHttpRequest; import org.jclouds.rest.internal.GeneratedHttpRequest;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.collect.Multimap;
/** /**
* *
* @author Adrian Cole * @author Adrian Cole
*/ */
public class AddEntryIntoMultiMap implements Function<HttpResponse, Void>, InvocationContext { public class AddEntryIntoMap implements Function<HttpResponse, Void>, InvocationContext {
ReturnStringIf200 returnIf200; ReturnStringIf200 returnIf200;
private GeneratedHttpRequest<?> request; private GeneratedHttpRequest<?> request;
@Inject @Inject
private AddEntryIntoMultiMap(ReturnStringIf200 returnIf200) { private AddEntryIntoMap(ReturnStringIf200 returnIf200) {
this.returnIf200 = returnIf200; this.returnIf200 = returnIf200;
} }
@ -44,15 +44,15 @@ public class AddEntryIntoMultiMap implements Function<HttpResponse, Void>, Invoc
{ {
checkState(request.getArgs() != null, "args should be initialized at this point"); checkState(request.getArgs() != null, "args should be initialized at this point");
Multimap<String, String> map = null; Map<String, String> map = null;
String key = null; String key = null;
for (Object arg : request.getArgs()) { for (Object arg : request.getArgs()) {
if (arg instanceof Multimap) if (arg instanceof Map)
map = (Multimap<String, String>) arg; map = (Map<String, String>) arg;
else if (arg instanceof String) else if (arg instanceof String)
key = arg.toString(); key = arg.toString();
} }
checkState(map != null, "No Multimap found in args, improper method declarations"); checkState(map != null, "No Map found in args, improper method declarations");
checkState(key != null, "No String found in args, improper method declarations"); checkState(key != null, "No String found in args, improper method declarations");
map.put(key, returnIf200.apply(from).trim()); map.put(key, returnIf200.apply(from).trim());

View File

@ -45,7 +45,6 @@ import org.jclouds.http.HttpResponse;
import org.jclouds.logging.Logger; import org.jclouds.logging.Logger;
import org.jclouds.mezeo.pcs2.PCSUtil; import org.jclouds.mezeo.pcs2.PCSUtil;
import org.jclouds.mezeo.pcs2.domain.PCSFile; import org.jclouds.mezeo.pcs2.domain.PCSFile;
import org.jclouds.mezeo.pcs2.util.PCSUtils;
import org.jclouds.rest.InvocationContext; import org.jclouds.rest.InvocationContext;
import org.jclouds.rest.internal.GeneratedHttpRequest; import org.jclouds.rest.internal.GeneratedHttpRequest;
import org.jclouds.util.Utils; import org.jclouds.util.Utils;
@ -58,7 +57,7 @@ import com.google.common.collect.Sets;
* *
* @author Adrian Cole * @author Adrian Cole
*/ */
public class AddMetadataAndParseResourceIdIntoBytes implements Function<HttpResponse, byte[]>, public class AddMetadataAndReturnId implements Function<HttpResponse, String>,
InvocationContext { InvocationContext {
private final PCSUtil util; private final PCSUtil util;
private final ConcurrentMap<Key, String> fileCache; private final ConcurrentMap<Key, String> fileCache;
@ -75,12 +74,12 @@ public class AddMetadataAndParseResourceIdIntoBytes implements Function<HttpResp
private GeneratedHttpRequest<?> request; private GeneratedHttpRequest<?> request;
@Inject @Inject
public AddMetadataAndParseResourceIdIntoBytes(ConcurrentMap<Key, String> fileCache, PCSUtil util) { public AddMetadataAndReturnId(ConcurrentMap<Key, String> fileCache, PCSUtil util) {
this.fileCache = fileCache; this.fileCache = fileCache;
this.util = util; this.util = util;
} }
public byte[] apply(HttpResponse from) { public String apply(HttpResponse from) {
if (from.getStatusCode() > 204) if (from.getStatusCode() > 204)
throw new BlobRuntimeException("Incorrect code for: " + from); throw new BlobRuntimeException("Incorrect code for: " + from);
checkState(request != null, "request should be initialized at this point"); checkState(request != null, "request should be initialized at this point");
@ -97,7 +96,7 @@ public class AddMetadataAndParseResourceIdIntoBytes implements Function<HttpResp
IOUtils.closeQuietly(from.getContent()); IOUtils.closeQuietly(from.getContent());
Set<Future<Void>> puts = Sets.newHashSet(); Set<Future<Void>> puts = Sets.newHashSet();
for (Entry<String, String> entry : file.getMetadata().getUserMetadata().entries()) { for (Entry<String, String> entry : file.getMetadata().getUserMetadata().entrySet()) {
puts.add(util.putMetadata(id, entry.getKey(), entry.getValue())); puts.add(util.putMetadata(id, entry.getKey(), entry.getValue()));
} }
for (Future<Void> put : puts) { for (Future<Void> put : puts) {
@ -108,8 +107,7 @@ public class AddMetadataAndParseResourceIdIntoBytes implements Function<HttpResp
throw new BlobRuntimeException("Error putting metadata for file: " + file, e); throw new BlobRuntimeException("Error putting metadata for file: " + file, e);
} }
} }
return id;
return PCSUtils.getETag(id);
} }
public void setContext(GeneratedHttpRequest<?> request) { public void setContext(GeneratedHttpRequest<?> request) {

View File

@ -25,8 +25,6 @@ package org.jclouds.mezeo.pcs2.util;
import java.net.URI; import java.net.URI;
import org.jclouds.http.HttpUtils;
/** /**
* Utilities for PCS connections. * Utilities for PCS connections.
* *
@ -36,16 +34,8 @@ public class PCSUtils {
/** /**
* converts the object id into something we can use as an etag * converts the object id into something we can use as an etag
*/ */
public static byte[] getETag(URI url) { public static String getId(URI url) {
String id = url.getPath().substring(url.getPath().lastIndexOf('/') + 1); return url.getPath().substring(url.getPath().lastIndexOf('/') + 1);
return getETag(id);
}
public static byte[] getETag(String id) {
id = id.replaceAll("-", "");
// parse url to create an "etag"
byte[] eTag = HttpUtils.fromHexString(id);
return eTag;
} }
public static String getContainerId(URI url) { public static String getContainerId(URI url) {

View File

@ -24,6 +24,7 @@
package org.jclouds.mezeo.pcs2.xml; package org.jclouds.mezeo.pcs2.xml;
import java.net.URI; import java.net.URI;
import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -40,8 +41,7 @@ import org.jclouds.util.Utils;
import org.xml.sax.Attributes; import org.xml.sax.Attributes;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
import com.google.common.collect.HashMultimap; import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
/** /**
@ -50,7 +50,7 @@ import com.google.common.collect.Sets;
public class FileMetadataHandler extends BaseFileMetadataHandler<FileMetadata> { public class FileMetadataHandler extends BaseFileMetadataHandler<FileMetadata> {
private final PCSUtil util; private final PCSUtil util;
private FileMetadata fileMetadata = null; private FileMetadata fileMetadata = null;
protected Multimap<String, String> userMetadata = HashMultimap.create(); protected Map<String, String> userMetadata = Maps.newHashMap();
Set<Future<Void>> puts = Sets.newHashSet(); Set<Future<Void>> puts = Sets.newHashSet();
/** /**
* maximum duration of an blob Request * maximum duration of an blob Request
@ -89,7 +89,7 @@ public class FileMetadataHandler extends BaseFileMetadataHandler<FileMetadata> {
int index = attributes.getIndex("xlink:href"); int index = attributes.getIndex("xlink:href");
if (index != -1) { if (index != -1) {
String key = attributes.getValue(index).replaceAll(".*/metadata/", ""); String key = attributes.getValue(index).replaceAll(".*/metadata/", "");
puts.add(util.addEntryToMultiMap(userMetadata, key.toLowerCase(), URI.create(attributes puts.add(util.addEntryToMap(userMetadata, key.toLowerCase(), URI.create(attributes
.getValue(index)))); .getValue(index))));
} }
} }

View File

@ -31,6 +31,7 @@ import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.net.URI; import java.net.URI;
import java.util.Collections; import java.util.Collections;
import java.util.Map;
import java.util.SortedSet; import java.util.SortedSet;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
@ -60,8 +61,8 @@ import org.jclouds.mezeo.pcs2.domain.FileMetadata;
import org.jclouds.mezeo.pcs2.domain.PCSFile; import org.jclouds.mezeo.pcs2.domain.PCSFile;
import org.jclouds.mezeo.pcs2.endpoints.RootContainer; import org.jclouds.mezeo.pcs2.endpoints.RootContainer;
import org.jclouds.mezeo.pcs2.endpoints.WebDAV; import org.jclouds.mezeo.pcs2.endpoints.WebDAV;
import org.jclouds.mezeo.pcs2.functions.AddEntryIntoMultiMap; import org.jclouds.mezeo.pcs2.functions.AddEntryIntoMap;
import org.jclouds.mezeo.pcs2.functions.AddMetadataAndParseResourceIdIntoBytes; import org.jclouds.mezeo.pcs2.functions.AddMetadataAndReturnId;
import org.jclouds.mezeo.pcs2.functions.AssembleBlobFromContentAndMetadataCache; import org.jclouds.mezeo.pcs2.functions.AssembleBlobFromContentAndMetadataCache;
import org.jclouds.mezeo.pcs2.functions.InvalidateContainerNameCacheAndReturnTrueIf2xx; import org.jclouds.mezeo.pcs2.functions.InvalidateContainerNameCacheAndReturnTrueIf2xx;
import org.jclouds.mezeo.pcs2.functions.InvalidatePCSKeyCacheAndReturnVoidIf2xx; import org.jclouds.mezeo.pcs2.functions.InvalidatePCSKeyCacheAndReturnVoidIf2xx;
@ -74,9 +75,8 @@ import org.jclouds.util.DateService;
import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSortedSet; import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Multimap;
import com.google.inject.AbstractModule; import com.google.inject.AbstractModule;
import com.google.inject.Guice; import com.google.inject.Guice;
import com.google.inject.Injector; import com.google.inject.Injector;
@ -315,7 +315,7 @@ public class PCSBlobStoreTest {
.singletonList(file.getData().toString().getBytes().length + "")); .singletonList(file.getData().toString().getBytes().length + ""));
assertEquals(httpMethod.getEntity(), file.getData()); assertEquals(httpMethod.getEntity(), file.getData());
assertEquals(processor.createResponseParser(method, httpMethod).getClass(), assertEquals(processor.createResponseParser(method, httpMethod).getClass(),
AddMetadataAndParseResourceIdIntoBytes.class); AddMetadataAndReturnId.class);
} }
public void testRemoveBlob() throws SecurityException, NoSuchMethodException, IOException { public void testRemoveBlob() throws SecurityException, NoSuchMethodException, IOException {
@ -405,12 +405,12 @@ public class PCSBlobStoreTest {
ReturnVoidIf2xx.class); ReturnVoidIf2xx.class);
} }
public void testAddEntryToMultiMap() throws SecurityException, NoSuchMethodException { public void testAddEntryToMap() throws SecurityException, NoSuchMethodException {
Method method = PCSUtil.class.getMethod("addEntryToMultiMap", Multimap.class, String.class, Method method = PCSUtil.class.getMethod("addEntryToMap", Map.class, String.class,
URI.class); URI.class);
GeneratedHttpRequest<PCSUtil> httpMethod = utilProcessor GeneratedHttpRequest<PCSUtil> httpMethod = utilProcessor
.createRequest(method, new Object[] { ImmutableMultimap.of("key", "value"), .createRequest(method, new Object[] { ImmutableMap.of("key", "value"),
"newkey", URI.create("http://localhost/pow") }); "newkey", URI.create("http://localhost/pow") });
assertEquals(httpMethod.getEndpoint().getHost(), "localhost"); assertEquals(httpMethod.getEndpoint().getHost(), "localhost");
assertEquals(httpMethod.getEndpoint().getPath(), "/pow"); assertEquals(httpMethod.getEndpoint().getPath(), "/pow");
@ -418,7 +418,7 @@ public class PCSBlobStoreTest {
assertEquals(httpMethod.getHeaders().size(), 0); assertEquals(httpMethod.getHeaders().size(), 0);
assertEquals(utilProcessor.createExceptionParserOrNullIfNotFound(method), null); assertEquals(utilProcessor.createExceptionParserOrNullIfNotFound(method), null);
assertEquals(utilProcessor.createResponseParser(method, httpMethod).getClass(), assertEquals(utilProcessor.createResponseParser(method, httpMethod).getClass(),
AddEntryIntoMultiMap.class); AddEntryIntoMap.class);
} }
RestAnnotationProcessor<PCSBlobStore> processor; RestAnnotationProcessor<PCSBlobStore> processor;
@ -451,7 +451,7 @@ public class PCSBlobStoreTest {
public PCSUtil getPCSUtil() { public PCSUtil getPCSUtil() {
return new PCSUtil() { return new PCSUtil() {
public Future<Void> addEntryToMultiMap(Multimap<String, String> map, public Future<Void> addEntryToMap(Map<String, String> map,
String key, URI value) { String key, URI value) {
return null; return null;
} }

View File

@ -35,6 +35,8 @@ import org.jclouds.concurrent.WithinThreadExecutorService;
import org.jclouds.concurrent.config.ExecutorServiceModule; import org.jclouds.concurrent.config.ExecutorServiceModule;
import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule; import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule;
import org.jclouds.http.filters.BasicAuthentication; import org.jclouds.http.filters.BasicAuthentication;
import org.jclouds.logging.Logger;
import org.jclouds.logging.Logger.LoggerFactory;
import org.jclouds.mezeo.pcs2.PCSCloud.Response; import org.jclouds.mezeo.pcs2.PCSCloud.Response;
import org.jclouds.rest.RestClientFactory; import org.jclouds.rest.RestClientFactory;
import org.jclouds.rest.config.RestModule; import org.jclouds.rest.config.RestModule;
@ -81,6 +83,11 @@ public class PCSCloudLiveTest {
@Override @Override
protected void configure() { protected void configure() {
bind(URI.class).annotatedWith(PCS.class).toInstance(endpoint); bind(URI.class).annotatedWith(PCS.class).toInstance(endpoint);
bind(Logger.LoggerFactory.class).toInstance(new LoggerFactory() {
public Logger getLogger(String category) {
return Logger.NULL;
}
});
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")

View File

@ -31,6 +31,7 @@ import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.net.URI; import java.net.URI;
import java.util.Collections; import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.Future; import java.util.concurrent.Future;
@ -61,7 +62,6 @@ import org.jclouds.util.Utils;
import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import com.google.common.collect.Multimap;
import com.google.inject.AbstractModule; import com.google.inject.AbstractModule;
import com.google.inject.Guice; import com.google.inject.Guice;
import com.google.inject.Injector; import com.google.inject.Injector;
@ -240,7 +240,7 @@ public class PCSConnectionTest {
public PCSUtil getPCSUtil() { public PCSUtil getPCSUtil() {
return new PCSUtil() { return new PCSUtil() {
public Future<Void> addEntryToMultiMap(Multimap<String, String> map, public Future<Void> addEntryToMap(Map<String, String> map,
String key, URI value) { String key, URI value) {
return null; return null;
} }

View File

@ -44,7 +44,6 @@ import org.jclouds.blobstore.domain.Key;
import org.jclouds.concurrent.WithinThreadExecutorService; import org.jclouds.concurrent.WithinThreadExecutorService;
import org.jclouds.concurrent.config.ExecutorServiceModule; import org.jclouds.concurrent.config.ExecutorServiceModule;
import org.jclouds.http.HttpResponse; import org.jclouds.http.HttpResponse;
import org.jclouds.http.HttpUtils;
import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule; import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule;
import org.jclouds.logging.Logger; import org.jclouds.logging.Logger;
import org.jclouds.logging.Logger.LoggerFactory; import org.jclouds.logging.Logger.LoggerFactory;
@ -67,14 +66,14 @@ import com.google.inject.TypeLiteral;
* *
* @author Adrian Cole * @author Adrian Cole
*/ */
@Test(groups = "unit", testName = "pcs2.AddMetadataAndParseResourceIdIntoBytesTest") @Test(groups = "unit", testName = "pcs2.AddMetadataAndReturnIdTest")
public class AddMetadataAndParseResourceIdIntoBytesTest { public class AddMetadataAndReturnIdTest {
static { static {
RuntimeDelegate.setInstance(new RuntimeDelegateImpl()); RuntimeDelegate.setInstance(new RuntimeDelegateImpl());
} }
HttpResponse response = new HttpResponse(); HttpResponse response = new HttpResponse();
ConcurrentMap<Key, String> fileCache; ConcurrentMap<Key, String> fileCache;
private RestAnnotationProcessor<org.jclouds.mezeo.pcs2.functions.AddMetadataAndParseResourceIdIntoBytesTest.TestService> factory; private RestAnnotationProcessor<TestService> factory;
private Method method; private Method method;
private static interface TestService { private static interface TestService {
@ -104,37 +103,31 @@ public class AddMetadataAndParseResourceIdIntoBytesTest {
@Test(expectedExceptions = IllegalStateException.class) @Test(expectedExceptions = IllegalStateException.class)
public void testNoArgs() { public void testNoArgs() {
AddMetadataAndParseResourceIdIntoBytes function = new AddMetadataAndParseResourceIdIntoBytes( AddMetadataAndReturnId function = new AddMetadataAndReturnId(fileCache, createPCSUtil());
fileCache, createPCSUtil());
function.apply(response); function.apply(response);
} }
@Test(expectedExceptions = IllegalStateException.class) @Test(expectedExceptions = IllegalStateException.class)
public void testNoRequest() { public void testNoRequest() {
AddMetadataAndParseResourceIdIntoBytes function = new AddMetadataAndParseResourceIdIntoBytes( AddMetadataAndReturnId function = new AddMetadataAndReturnId(fileCache, createPCSUtil());
fileCache, createPCSUtil());
function.apply(response); function.apply(response);
} }
public void testGetEtag() { public void testGetEtag() {
PCSUtil connection = createPCSUtil(); PCSUtil connection = createPCSUtil();
AddMetadataAndParseResourceIdIntoBytes function = new AddMetadataAndParseResourceIdIntoBytes( AddMetadataAndReturnId function = new AddMetadataAndReturnId(fileCache, connection);
fileCache, connection);
function.setContext(factory.createRequest(method, "container", new PCSFile("key"), URI function.setContext(factory.createRequest(method, "container", new PCSFile("key"), URI
.create("http://localhost:8080"))); .create("http://localhost:8080")));
response.setContent(IOUtils response.setContent(IOUtils
.toInputStream("http://localhost/contents/7F143552-AAF5-11DE-BBB0-0BC388ED913B")); .toInputStream("http://localhost/contents/7F143552-AAF5-11DE-BBB0-0BC388ED913B"));
byte[] eTag = function.apply(response); String eTag = function.apply(response);
assertEquals(eTag, "7F143552-AAF5-11DE-BBB0-0BC388ED913B");
byte[] expected = HttpUtils.fromHexString("7F143552AAF511DEBBB00BC388ED913B");
assertEquals(eTag, expected);
} }
public void testMetadataGetEtag() { public void testMetadataGetEtag() {
PCSUtil connection = createPCSUtil(); PCSUtil connection = createPCSUtil();
AddMetadataAndParseResourceIdIntoBytes function = new AddMetadataAndParseResourceIdIntoBytes( AddMetadataAndReturnId function = new AddMetadataAndReturnId(fileCache, connection);
fileCache, connection);
PCSFile pcsFile = new PCSFile("key"); PCSFile pcsFile = new PCSFile("key");
pcsFile.getMetadata().getUserMetadata().put("foo", "bar"); pcsFile.getMetadata().getUserMetadata().put("foo", "bar");
pcsFile.getMetadata().getUserMetadata().put("biz", "baz"); pcsFile.getMetadata().getUserMetadata().put("biz", "baz");
@ -143,10 +136,8 @@ public class AddMetadataAndParseResourceIdIntoBytesTest {
.create("http://localhost:8080"))); .create("http://localhost:8080")));
response.setContent(IOUtils response.setContent(IOUtils
.toInputStream("http://localhost/contents/7F143552-AAF5-11DE-BBB0-0BC388ED913B")); .toInputStream("http://localhost/contents/7F143552-AAF5-11DE-BBB0-0BC388ED913B"));
byte[] eTag = function.apply(response); String eTag = function.apply(response);
assertEquals(eTag, "7F143552-AAF5-11DE-BBB0-0BC388ED913B");
byte[] expected = HttpUtils.fromHexString("7F143552AAF511DEBBB00BC388ED913B");
assertEquals(eTag, expected);
verify(connection); verify(connection);
} }

View File

@ -26,7 +26,6 @@ package org.jclouds.mezeo.pcs2.integration;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
import java.io.IOException; import java.io.IOException;
import java.util.Collections;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
@ -48,8 +47,7 @@ public class PCSBlobStoreIntegrationTest extends
protected void validateMetadata(FileMetadata metadata) { protected void validateMetadata(FileMetadata metadata) {
assertEquals(metadata.getContentType(), "text/plain"); assertEquals(metadata.getContentType(), "text/plain");
assertEquals(metadata.getSize(), TEST_STRING.length()); assertEquals(metadata.getSize(), TEST_STRING.length());
assertEquals(metadata.getUserMetadata().get("adrian"), Collections assertEquals(metadata.getUserMetadata().get("adrian"), "powderpuff");
.singletonList("powderpuff"));
// Issue 105 // Issue 105
// assertEquals(metadata.getContentMD5(), HttpUtils.md5(TEST_STRING.getBytes())); // assertEquals(metadata.getContentMD5(), HttpUtils.md5(TEST_STRING.getBytes()));
} }

View File

@ -27,7 +27,6 @@ import static org.testng.Assert.assertEquals;
import java.net.URI; import java.net.URI;
import org.jclouds.http.HttpUtils;
import org.testng.annotations.Test; import org.testng.annotations.Test;
/** /**
@ -38,10 +37,8 @@ import org.testng.annotations.Test;
@Test(groups = "unit", testName = "pcs2.PCSUtilsTest") @Test(groups = "unit", testName = "pcs2.PCSUtilsTest")
public class PCSUtilsTest { public class PCSUtilsTest {
public void testGetEtag() { public void testGetEtag() {
byte[] expected = HttpUtils.fromHexString("7F143552AAF511DEBBB00BC388ED913B"); String eTag = PCSUtils.getId(URI
byte[] eTag = PCSUtils.getETag(URI
.create("http://localhost/contents/7F143552-AAF5-11DE-BBB0-0BC388ED913B")); .create("http://localhost/contents/7F143552-AAF5-11DE-BBB0-0BC388ED913B"));
assertEquals(eTag, expected); assertEquals(eTag, "7F143552-AAF5-11DE-BBB0-0BC388ED913B");
} }
} }

View File

@ -28,6 +28,7 @@ import static org.testng.Assert.assertEquals;
import java.io.InputStream; import java.io.InputStream;
import java.net.URI; import java.net.URI;
import java.util.Map;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import javax.inject.Singleton; import javax.inject.Singleton;
@ -40,7 +41,6 @@ import org.jclouds.util.DateService;
import org.testng.annotations.BeforeTest; import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import com.google.common.collect.Multimap;
import com.google.inject.AbstractModule; import com.google.inject.AbstractModule;
import com.google.inject.Provides; import com.google.inject.Provides;
@ -70,7 +70,7 @@ public class FileMetadataHandlerTest extends BaseHandlerTest {
PCSUtil provideUtil() { PCSUtil provideUtil() {
final Future<Void> voidF = createMock(Future.class); final Future<Void> voidF = createMock(Future.class);
return new PCSUtil() { return new PCSUtil() {
public Future<Void> addEntryToMultiMap(Multimap<String, String> map, String key, public Future<Void> addEntryToMap(Map<String, String> map, String key,
URI value) { URI value) {
map.put(key.toLowerCase(), "bar"); map.put(key.toLowerCase(), "bar");
return voidF; return voidF;

View File

@ -421,8 +421,12 @@ pageTracker._trackPageview();
<value>${jclouds.test.endpoint}</value> <value>${jclouds.test.endpoint}</value>
</property> </property>
<property> <property>
<name>jclouds.test.app</name> <name>jclouds.test.appname</name>
<value>${jclouds.test.app}</value> <value>${jclouds.test.appname}</value>
</property>
<property>
<name>jclouds.test.appid</name>
<value>${jclouds.test.appid}</value>
</property> </property>
<property> <property>
<name>jclouds.blobstore.httpstream.url</name> <name>jclouds.blobstore.httpstream.url</name>

View File

@ -111,7 +111,7 @@ public interface CloudFilesBlobStore extends
@PUT @PUT
@Path("{container}/{key}") @Path("{container}/{key}")
@ResponseParser(ParseETagHeader.class) @ResponseParser(ParseETagHeader.class)
Future<byte[]> putBlob( Future<String> putBlob(
@PathParam("container") String container, @PathParam("container") String container,
@PathParam("key") @ParamParser(BlobKey.class) @BinderParam(BindBlobToEntity.class) Blob<BlobMetadata> object); @PathParam("key") @ParamParser(BlobKey.class) @BinderParam(BindBlobToEntity.class) Blob<BlobMetadata> object);

View File

@ -139,7 +139,8 @@ public interface CloudFilesConnection {
@POST @POST
@Path("{container}/{key}") @Path("{container}/{key}")
boolean setObjectMetadata(@PathParam("container") String container, boolean setObjectMetadata(
@PathParam("container") String container,
@PathParam("key") String key, @PathParam("key") String key,
@BinderParam(BindMultimapToHeadersWithPrefix.class) Multimap<String, String> userMetadata); @BinderParam(BindMultimapToHeadersWithPrefix.class) Multimap<String, String> userMetadata);
@ -203,7 +204,7 @@ public interface CloudFilesConnection {
@PUT @PUT
@Path("{container}/{key}") @Path("{container}/{key}")
@ResponseParser(ParseETagHeader.class) @ResponseParser(ParseETagHeader.class)
Future<byte[]> putObject( Future<String> putObject(
@PathParam("container") String container, @PathParam("container") String container,
@PathParam("key") @ParamParser(BlobKey.class) @BinderParam(BindBlobToEntity.class) Blob<BlobMetadata> object); @PathParam("key") @ParamParser(BlobKey.class) @BinderParam(BindBlobToEntity.class) Blob<BlobMetadata> object);
@ -218,11 +219,13 @@ public interface CloudFilesConnection {
@ResponseParser(ParseObjectMetadataFromHeaders.class) @ResponseParser(ParseObjectMetadataFromHeaders.class)
@ExceptionParser(ThrowKeyNotFoundOn404.class) @ExceptionParser(ThrowKeyNotFoundOn404.class)
@Path("{container}/{key}") @Path("{container}/{key}")
BlobMetadata getObjectMetadata(@PathParam("container") String container, @PathParam("key") String key); BlobMetadata getObjectMetadata(@PathParam("container") String container,
@PathParam("key") String key);
@DELETE @DELETE
@ExceptionParser(ReturnTrueOn404.class) @ExceptionParser(ReturnTrueOn404.class)
@Path("{container}/{key}") @Path("{container}/{key}")
Future<Boolean> removeObject(@PathParam("container") String container, @PathParam("key") String key); Future<Boolean> removeObject(@PathParam("container") String container,
@PathParam("key") String key);
} }

View File

@ -83,7 +83,7 @@ public class ParseBlobMetadataListFromJsonResponse extends ParseJson<SortedSet<B
metadata.setSize(from.bytes); metadata.setSize(from.bytes);
metadata.setLastModified(from.last_modified); metadata.setLastModified(from.last_modified);
metadata.setContentType(from.content_type); metadata.setContentType(from.content_type);
metadata.setETag(HttpUtils.fromHexString(from.hash)); metadata.setETag(from.hash);
metadata.setContentMD5(HttpUtils.fromHexString(from.hash)); metadata.setContentMD5(HttpUtils.fromHexString(from.hash));
return metadata; return metadata;
} }

View File

@ -60,9 +60,9 @@ public class ParseObjectMetadataFromHeaders extends
// etag comes back incorrect case // etag comes back incorrect case
String eTagHeader = from.getFirstHeaderOrNull("Etag"); String eTagHeader = from.getFirstHeaderOrNull("Etag");
if (eTagHeader != null) { if (eTagHeader != null) {
metadata.setETag(HttpUtils.fromHexString(eTagHeader)); metadata.setETag(eTagHeader);
} }
} }
metadata.setContentMD5(metadata.getETag()); metadata.setContentMD5(HttpUtils.fromHexString(metadata.getETag()));
} }
} }

View File

@ -284,7 +284,7 @@ public class CloudFilesConnectionLiveTest {
object.getMetadata().setContentType("text/plain"); object.getMetadata().setContentType("text/plain");
object.getMetadata().getUserMetadata().put("Metadata", "metadata-value"); object.getMetadata().getUserMetadata().put("Metadata", "metadata-value");
byte[] md5 = object.getMetadata().getContentMD5(); byte[] md5 = object.getMetadata().getContentMD5();
byte[] newEtag = connection.putObject(containerName, object).get(10, TimeUnit.SECONDS); String newEtag = connection.putObject(containerName, object).get(10, TimeUnit.SECONDS);
assertEquals(HttpUtils.toHexString(md5), HttpUtils.toHexString(object.getMetadata() assertEquals(HttpUtils.toHexString(md5), HttpUtils.toHexString(object.getMetadata()
.getContentMD5())); .getContentMD5()));
@ -303,8 +303,8 @@ public class CloudFilesConnectionLiveTest {
assertEquals(HttpUtils.toHexString(md5), HttpUtils.toHexString(object.getMetadata() assertEquals(HttpUtils.toHexString(md5), HttpUtils.toHexString(object.getMetadata()
.getContentMD5())); .getContentMD5()));
assertEquals(metadata.getETag(), newEtag); assertEquals(metadata.getETag(), newEtag);
assertEquals(metadata.getUserMetadata().entries().size(), 1); assertEquals(metadata.getUserMetadata().entrySet().size(), 1);
assertEquals(Iterables.getLast(metadata.getUserMetadata().get("metadata")), "metadata-value"); assertEquals(metadata.getUserMetadata().get("metadata"), "metadata-value");
// // Test POST to update object's metadata // // Test POST to update object's metadata
Multimap<String, String> userMetadata = HashMultimap.create(); Multimap<String, String> userMetadata = HashMultimap.create();
@ -328,18 +328,14 @@ public class CloudFilesConnectionLiveTest {
assertEquals(HttpUtils.toHexString(md5), HttpUtils.toHexString(getBlob.getMetadata() assertEquals(HttpUtils.toHexString(md5), HttpUtils.toHexString(getBlob.getMetadata()
.getContentMD5())); .getContentMD5()));
assertEquals(newEtag, getBlob.getMetadata().getETag()); assertEquals(newEtag, getBlob.getMetadata().getETag());
assertEquals(getBlob.getMetadata().getUserMetadata().entries().size(), 2); assertEquals(getBlob.getMetadata().getUserMetadata().entrySet().size(), 2);
assertEquals( assertEquals(getBlob.getMetadata().getUserMetadata().get("new-metadata-1"), "value-1");
Iterables.getLast(getBlob.getMetadata().getUserMetadata().get("new-metadata-1")), assertEquals(getBlob.getMetadata().getUserMetadata().get("new-metadata-2"), "value-2");
"value-1");
assertEquals(
Iterables.getLast(getBlob.getMetadata().getUserMetadata().get("new-metadata-2")),
"value-2");
// Test PUT with invalid ETag (as if object's data was corrupted in transit) // Test PUT with invalid ETag (as if object's data was corrupted in transit)
String correctEtag = HttpUtils.toHexString(newEtag); String correctEtag = newEtag;
String incorrectEtag = "0" + correctEtag.substring(1); String incorrectEtag = "0" + correctEtag.substring(1);
object.getMetadata().setETag(HttpUtils.fromHexString(incorrectEtag)); object.getMetadata().setETag(incorrectEtag);
try { try {
connection.putObject(containerName, object).get(10, TimeUnit.SECONDS); connection.putObject(containerName, object).get(10, TimeUnit.SECONDS);
} catch (Throwable e) { } catch (Throwable e) {

View File

@ -29,7 +29,6 @@ import java.io.InputStream;
import java.util.List; import java.util.List;
import org.jclouds.blobstore.domain.BlobMetadata; import org.jclouds.blobstore.domain.BlobMetadata;
import org.jclouds.http.HttpUtils;
import org.jclouds.http.functions.config.ParserModule; import org.jclouds.http.functions.config.ParserModule;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.testng.annotations.Test; import org.testng.annotations.Test;
@ -53,13 +52,13 @@ public class ParseBlobMetadataListFromJsonResponseTest {
InputStream is = getClass().getResourceAsStream("/test_list_container.json"); InputStream is = getClass().getResourceAsStream("/test_list_container.json");
List<BlobMetadata> expects = Lists.newArrayList(); List<BlobMetadata> expects = Lists.newArrayList();
BlobMetadata one = new BlobMetadata("test_obj_1"); BlobMetadata one = new BlobMetadata("test_obj_1");
one.setETag(HttpUtils.fromHexString("4281c348eaf83e70ddce0e07221c3d28")); one.setETag("4281c348eaf83e70ddce0e07221c3d28");
one.setSize(14); one.setSize(14);
one.setContentType("application/octet-stream"); one.setContentType("application/octet-stream");
one.setLastModified(new DateTime("2009-02-03T05:26:32.612278")); one.setLastModified(new DateTime("2009-02-03T05:26:32.612278"));
expects.add(one); expects.add(one);
BlobMetadata two = new BlobMetadata("test_obj_2"); BlobMetadata two = new BlobMetadata("test_obj_2");
two.setETag(HttpUtils.fromHexString("b039efe731ad111bc1b0ef221c3849d0")); two.setETag("b039efe731ad111bc1b0ef221c3849d0");
two.setSize(64); two.setSize(64);
two.setContentType("application/octet-stream"); two.setContentType("application/octet-stream");
two.setLastModified(new DateTime("2009-02-03T05:26:32.612278")); two.setLastModified(new DateTime("2009-02-03T05:26:32.612278"));

View File

@ -122,7 +122,7 @@ public class StubCloudFilesConnection extends
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
public Future<byte[]> putObject(String container, Blob<BlobMetadata> object) { public Future<String> putObject(String container, Blob<BlobMetadata> object) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }