Store the MPU ETag for the transient blobstore
JCLOUDS-1582: fixes a bug in the transient blobstore where after
uploading a multipart upload, GET/HEAD returns the hash of the content,
rather than the MPU ETag.
diff --git a/blobstore/src/main/java/org/jclouds/blobstore/TransientStorageStrategy.java b/blobstore/src/main/java/org/jclouds/blobstore/TransientStorageStrategy.java
index 398d55b..76a66b4 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 @@
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<String, Blob> map = containerToBlobs.get(containerName);
String blobName = newBlob.getMetadata().getName();
map.put(blobName, newBlob);
@@ -240,11 +247,12 @@
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 @@
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 e2b842e..d2d32c6 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 void testSetBlobAccess() throws Exception {
throw new SkipException("transient does not support anonymous access");
}
+
+ @Override
+ protected void checkMPUParts(Blob blob, List<MultipartPart> 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);
+ }
}