diff --git a/blobstore/src/main/java/org/jclouds/blobstore/TransientStorageStrategy.java b/blobstore/src/main/java/org/jclouds/blobstore/TransientStorageStrategy.java index 398d55b6ec..76a66b4695 100644 --- a/blobstore/src/main/java/org/jclouds/blobstore/TransientStorageStrategy.java +++ b/blobstore/src/main/java/org/jclouds/blobstore/TransientStorageStrategy.java @@ -193,7 +193,14 @@ public class TransientStorageStrategy implements LocalStorageStrategy { Closeables2.closeQuietly(input); } - Blob newBlob = createUpdatedCopyOfBlobInContainer(containerName, blob, payload, actualHashCode); + String eTag = null; + if (blob.getMetadata() != null) { + eTag = blob.getMetadata().getETag(); + } + if (eTag == null) { + eTag = base16().lowerCase().encode(actualHashCode.asBytes()); + } + Blob newBlob = createUpdatedCopyOfBlobInContainer(containerName, blob, payload, actualHashCode, eTag); Map map = containerToBlobs.get(containerName); String blobName = newBlob.getMetadata().getName(); map.put(blobName, newBlob); @@ -240,11 +247,12 @@ public class TransientStorageStrategy implements LocalStorageStrategy { return "/"; } - private Blob createUpdatedCopyOfBlobInContainer(String containerName, Blob in, byte[] input, HashCode contentMd5) { + private Blob createUpdatedCopyOfBlobInContainer(String containerName, Blob in, byte[] input, HashCode contentMd5, String eTag) { checkNotNull(containerName, "containerName"); checkNotNull(in, "blob"); checkNotNull(input, "input"); checkNotNull(contentMd5, "contentMd5"); + checkNotNull(eTag, "eTag"); Payload payload = createPayload(input); MutableContentMetadata oldMd = in.getPayload().getContentMetadata(); HttpUtils.copy(oldMd, payload.getContentMetadata()); @@ -255,7 +263,6 @@ public class TransientStorageStrategy implements LocalStorageStrategy { blob.getMetadata().setContainer(containerName); blob.getMetadata().setLastModified(new Date()); blob.getMetadata().setSize((long) input.length); - String eTag = base16().lowerCase().encode(contentMd5.asBytes()); blob.getMetadata().setETag(eTag); // Set HTTP headers to match metadata blob.getAllHeaders().replaceValues(HttpHeaders.LAST_MODIFIED, diff --git a/blobstore/src/test/java/org/jclouds/blobstore/integration/TransientBlobIntegrationTest.java b/blobstore/src/test/java/org/jclouds/blobstore/integration/TransientBlobIntegrationTest.java index e2b842ecc5..d2d32c6a76 100644 --- a/blobstore/src/test/java/org/jclouds/blobstore/integration/TransientBlobIntegrationTest.java +++ b/blobstore/src/test/java/org/jclouds/blobstore/integration/TransientBlobIntegrationTest.java @@ -16,10 +16,19 @@ */ package org.jclouds.blobstore.integration; +import com.google.common.hash.Hasher; +import com.google.common.hash.Hashing; +import com.google.common.io.BaseEncoding; +import org.jclouds.blobstore.domain.Blob; +import org.jclouds.blobstore.domain.MultipartPart; import org.jclouds.blobstore.integration.internal.BaseBlobIntegrationTest; import org.testng.annotations.Test; import org.testng.SkipException; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + @Test(groups = { "integration" }) public class TransientBlobIntegrationTest extends BaseBlobIntegrationTest { public TransientBlobIntegrationTest() { @@ -31,4 +40,20 @@ public class TransientBlobIntegrationTest extends BaseBlobIntegrationTest { public void testSetBlobAccess() throws Exception { throw new SkipException("transient does not support anonymous access"); } + + @Override + protected void checkMPUParts(Blob blob, List parts) { + assertThat(blob.getMetadata().getETag()).endsWith(String.format("-%d\"", parts.size())); + Hasher eTagHasher = Hashing.md5().newHasher(); + for (MultipartPart part : parts) { + eTagHasher.putBytes(BaseEncoding.base16().lowerCase().decode(part.partETag())); + } + String expectedETag = new StringBuilder("\"") + .append(eTagHasher.hash()) + .append("-") + .append(parts.size()) + .append("\"") + .toString(); + assertThat(blob.getMetadata().getETag()).isEqualTo(expectedETag); + } }