Repository: jclouds Updated Branches: refs/heads/master e473d7df6 -> 50026c8f2
Return Atmos objectID as ETag Previously Atmos returned null. Also rework the fix for JCLOUDS-339 which does not reproduce with AT&T Synaptic. Fixes gaul/s3proxy#247. Project: http://git-wip-us.apache.org/repos/asf/jclouds/repo Commit: http://git-wip-us.apache.org/repos/asf/jclouds/commit/50026c8f Tree: http://git-wip-us.apache.org/repos/asf/jclouds/tree/50026c8f Diff: http://git-wip-us.apache.org/repos/asf/jclouds/diff/50026c8f Branch: refs/heads/master Commit: 50026c8f2db5ece473bd6f9765c0bf4e81081f5e Parents: e473d7d Author: Andrew Gaul <[email protected]> Authored: Sat Nov 11 11:08:56 2017 -0800 Committer: Andrew Gaul <[email protected]> Committed: Sat Nov 11 14:20:49 2017 -0800 ---------------------------------------------------------------------- .../java/org/jclouds/atmos/AtmosClient.java | 4 +- ...irectoryEntryListToResourceMetadataList.java | 2 +- .../functions/ObjectToBlobMetadata.java | 1 + ...ullableURIFromListOrLocationHeaderIf20x.java | 40 -------------------- .../java/org/jclouds/atmos/util/AtmosUtils.java | 13 +++++-- .../java/org/jclouds/atmos/AtmosClientTest.java | 5 +-- .../integration/AtmosIntegrationLiveTest.java | 31 +++++++++++++++ .../ParseURIFromListOrLocationHeaderIf20x.java | 2 +- 8 files changed, 48 insertions(+), 50 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/jclouds/blob/50026c8f/apis/atmos/src/main/java/org/jclouds/atmos/AtmosClient.java ---------------------------------------------------------------------- diff --git a/apis/atmos/src/main/java/org/jclouds/atmos/AtmosClient.java b/apis/atmos/src/main/java/org/jclouds/atmos/AtmosClient.java index cda7b3c..5d537ef 100644 --- a/apis/atmos/src/main/java/org/jclouds/atmos/AtmosClient.java +++ b/apis/atmos/src/main/java/org/jclouds/atmos/AtmosClient.java @@ -45,7 +45,6 @@ import org.jclouds.atmos.fallbacks.TrueOn404FalseOnPathNotEmpty; import org.jclouds.atmos.filters.SignRequest; import org.jclouds.atmos.functions.AtmosObjectName; import org.jclouds.atmos.functions.ParseDirectoryListFromContentAndHeaders; -import org.jclouds.atmos.functions.ParseNullableURIFromListOrLocationHeaderIf20x; import org.jclouds.atmos.functions.ParseObjectFromHeadersAndHttpContent; import org.jclouds.atmos.functions.ParseSystemMetadataFromHeaders; import org.jclouds.atmos.functions.ParseUserMetadataFromHeaders; @@ -55,6 +54,7 @@ import org.jclouds.atmos.options.PutOptions; import org.jclouds.blobstore.BlobStoreFallbacks.NullOnKeyAlreadyExists; import org.jclouds.blobstore.BlobStoreFallbacks.ThrowContainerNotFoundOn404; import org.jclouds.blobstore.BlobStoreFallbacks.ThrowKeyNotFoundOn404; +import org.jclouds.http.functions.ParseURIFromListOrLocationHeaderIf20x; import org.jclouds.http.options.GetOptions; import org.jclouds.javax.annotation.Nullable; import org.jclouds.rest.annotations.BinderParam; @@ -105,7 +105,7 @@ public interface AtmosClient extends Closeable { @POST @Path("/{parent}/{name}") @Headers(keys = EXPECT, values = "100-continue") - @ResponseParser(ParseNullableURIFromListOrLocationHeaderIf20x.class) + @ResponseParser(ParseURIFromListOrLocationHeaderIf20x.class) @Consumes(MediaType.WILDCARD) URI createFile(@PathParam("parent") String parent, @PathParam("name") @ParamParser(AtmosObjectName.class) @BinderParam(BindMetadataToHeaders.class) AtmosObject object, PutOptions... options); http://git-wip-us.apache.org/repos/asf/jclouds/blob/50026c8f/apis/atmos/src/main/java/org/jclouds/atmos/blobstore/functions/DirectoryEntryListToResourceMetadataList.java ---------------------------------------------------------------------- diff --git a/apis/atmos/src/main/java/org/jclouds/atmos/blobstore/functions/DirectoryEntryListToResourceMetadataList.java b/apis/atmos/src/main/java/org/jclouds/atmos/blobstore/functions/DirectoryEntryListToResourceMetadataList.java index b14dd85..e7fe4ed 100644 --- a/apis/atmos/src/main/java/org/jclouds/atmos/blobstore/functions/DirectoryEntryListToResourceMetadataList.java +++ b/apis/atmos/src/main/java/org/jclouds/atmos/blobstore/functions/DirectoryEntryListToResourceMetadataList.java @@ -59,7 +59,7 @@ public class DirectoryEntryListToResourceMetadataList implements .get(), null, null, null, null, ImmutableMap.<String, String>of()); else { BlobMetadataImpl metadata = new BlobMetadataImpl(from.getObjectID(), from.getObjectName(), defaultLocation.get(), - null, null, null, from.getModifiedTime(), ImmutableMap.<String, String>of(), null, + null, from.getObjectID(), null, from.getModifiedTime(), ImmutableMap.<String, String>of(), null, null, new BaseMutableContentMetadata()); MutableBlobMetadataImpl mutable = new MutableBlobMetadataImpl(metadata); mutable.setSize(from.getSize()); http://git-wip-us.apache.org/repos/asf/jclouds/blob/50026c8f/apis/atmos/src/main/java/org/jclouds/atmos/blobstore/functions/ObjectToBlobMetadata.java ---------------------------------------------------------------------- diff --git a/apis/atmos/src/main/java/org/jclouds/atmos/blobstore/functions/ObjectToBlobMetadata.java b/apis/atmos/src/main/java/org/jclouds/atmos/blobstore/functions/ObjectToBlobMetadata.java index 0ea70cbe..45c124c 100644 --- a/apis/atmos/src/main/java/org/jclouds/atmos/blobstore/functions/ObjectToBlobMetadata.java +++ b/apis/atmos/src/main/java/org/jclouds/atmos/blobstore/functions/ObjectToBlobMetadata.java @@ -80,6 +80,7 @@ public class ObjectToBlobMetadata implements Function<AtmosObject, MutableBlobMe to.setUserMetadata(lowerKeyMetadata); to.setSize(from.getContentMetadata().getContentLength()); to.setTier(Tier.STANDARD); + to.setETag(from.getSystemMetadata().getObjectID()); return to; } } http://git-wip-us.apache.org/repos/asf/jclouds/blob/50026c8f/apis/atmos/src/main/java/org/jclouds/atmos/functions/ParseNullableURIFromListOrLocationHeaderIf20x.java ---------------------------------------------------------------------- diff --git a/apis/atmos/src/main/java/org/jclouds/atmos/functions/ParseNullableURIFromListOrLocationHeaderIf20x.java b/apis/atmos/src/main/java/org/jclouds/atmos/functions/ParseNullableURIFromListOrLocationHeaderIf20x.java deleted file mode 100644 index 6a28c31..0000000 --- a/apis/atmos/src/main/java/org/jclouds/atmos/functions/ParseNullableURIFromListOrLocationHeaderIf20x.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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.atmos.functions; - -import static org.jclouds.http.HttpUtils.releasePayload; - -import java.net.URI; - -import org.jclouds.http.HttpResponse; -import org.jclouds.http.functions.ParseURIFromListOrLocationHeaderIf20x; - -/** - * Parses a single URI from a list, returning null when blob length was zero. - * Atmos returns "HTTP/1.1 201 null" when putting zero-length blobs. - */ -public class ParseNullableURIFromListOrLocationHeaderIf20x extends ParseURIFromListOrLocationHeaderIf20x { - - @Override - public URI apply(HttpResponse from) { - if (from.getStatusCode() == 201 && request.getPayload().getContentMetadata().getContentLength() == 0) { - releasePayload(from); - return null; - } - return super.apply(from); - } -} http://git-wip-us.apache.org/repos/asf/jclouds/blob/50026c8f/apis/atmos/src/main/java/org/jclouds/atmos/util/AtmosUtils.java ---------------------------------------------------------------------- diff --git a/apis/atmos/src/main/java/org/jclouds/atmos/util/AtmosUtils.java b/apis/atmos/src/main/java/org/jclouds/atmos/util/AtmosUtils.java index 660393a..a2b33cf 100644 --- a/apis/atmos/src/main/java/org/jclouds/atmos/util/AtmosUtils.java +++ b/apis/atmos/src/main/java/org/jclouds/atmos/util/AtmosUtils.java @@ -20,6 +20,7 @@ import static org.jclouds.util.Predicates2.retry; import java.io.ByteArrayInputStream; import java.io.InputStream; +import java.net.URI; import javax.inject.Inject; import javax.inject.Provider; @@ -73,13 +74,19 @@ public class AtmosUtils { final String path = container + "/" + blob.getMetadata().getName(); final AtmosObject object = blob2Object.apply(blob); + URI uri; try { - sync.createFile(container, object, options); + uri = sync.createFile(container, object, options); } catch (KeyAlreadyExistsException e) { deletePathAndEnsureGone(sync, path); - sync.createFile(container, object, options); + uri = sync.createFile(container, object, options); } - return path; + + // return object ID as the ETag + String objectId = uri.getPath(); + String prefix = "/rest/objects/"; + checkState(objectId.startsWith(prefix), objectId); + return objectId.substring(prefix.length()); } public static void deletePathAndEnsureGone(final AtmosClient sync, String path) { http://git-wip-us.apache.org/repos/asf/jclouds/blob/50026c8f/apis/atmos/src/test/java/org/jclouds/atmos/AtmosClientTest.java ---------------------------------------------------------------------- diff --git a/apis/atmos/src/test/java/org/jclouds/atmos/AtmosClientTest.java b/apis/atmos/src/test/java/org/jclouds/atmos/AtmosClientTest.java index c96c70e..5f27c62 100644 --- a/apis/atmos/src/test/java/org/jclouds/atmos/AtmosClientTest.java +++ b/apis/atmos/src/test/java/org/jclouds/atmos/AtmosClientTest.java @@ -30,7 +30,6 @@ import org.jclouds.atmos.domain.AtmosObject; import org.jclouds.atmos.fallbacks.TrueOn404FalseOnPathNotEmpty; import org.jclouds.atmos.filters.SignRequest; import org.jclouds.atmos.functions.ParseDirectoryListFromContentAndHeaders; -import org.jclouds.atmos.functions.ParseNullableURIFromListOrLocationHeaderIf20x; import org.jclouds.atmos.functions.ParseObjectFromHeadersAndHttpContent; import org.jclouds.atmos.functions.ParseSystemMetadataFromHeaders; import org.jclouds.atmos.functions.ReturnTrueIfGroupACLIsOtherRead; @@ -164,7 +163,7 @@ public class AtmosClientTest extends BaseRestAnnotationProcessingTest<AtmosClien assertNonPayloadHeadersEqual(request, HttpHeaders.ACCEPT + ": */*\nExpect: 100-continue\n"); assertPayloadEquals(request, "hello", "text/plain", false); - assertResponseParserClassEquals(method, request, ParseNullableURIFromListOrLocationHeaderIf20x.class); + assertResponseParserClassEquals(method, request, ParseURIFromListOrLocationHeaderIf20x.class); assertSaxResponseParserClassEquals(method, null); assertFallbackClassEquals(method, null); @@ -182,7 +181,7 @@ public class AtmosClientTest extends BaseRestAnnotationProcessingTest<AtmosClien + ": */*\nExpect: 100-continue\nx-emc-groupacl: other=READ\nx-emc-useracl: root=FULL_CONTROL\n"); assertPayloadEquals(request, "hello", "text/plain", false); - assertResponseParserClassEquals(method, request, ParseNullableURIFromListOrLocationHeaderIf20x.class); + assertResponseParserClassEquals(method, request, ParseURIFromListOrLocationHeaderIf20x.class); assertSaxResponseParserClassEquals(method, null); assertFallbackClassEquals(method, null); http://git-wip-us.apache.org/repos/asf/jclouds/blob/50026c8f/apis/atmos/src/test/java/org/jclouds/atmos/blobstore/integration/AtmosIntegrationLiveTest.java ---------------------------------------------------------------------- diff --git a/apis/atmos/src/test/java/org/jclouds/atmos/blobstore/integration/AtmosIntegrationLiveTest.java b/apis/atmos/src/test/java/org/jclouds/atmos/blobstore/integration/AtmosIntegrationLiveTest.java index f999810..e4923a3 100644 --- a/apis/atmos/src/test/java/org/jclouds/atmos/blobstore/integration/AtmosIntegrationLiveTest.java +++ b/apis/atmos/src/test/java/org/jclouds/atmos/blobstore/integration/AtmosIntegrationLiveTest.java @@ -23,14 +23,19 @@ import static org.testng.Assert.assertEquals; import java.io.IOException; import java.util.concurrent.ExecutionException; +import org.jclouds.blobstore.BlobStore; import org.jclouds.blobstore.domain.Blob; import org.jclouds.blobstore.domain.BlobMetadata; +import org.jclouds.blobstore.domain.StorageMetadata; import org.jclouds.blobstore.domain.Tier; import org.jclouds.blobstore.integration.internal.BaseBlobIntegrationTest; +import org.jclouds.blobstore.options.ListContainerOptions; import org.testng.SkipException; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; +import com.google.common.io.ByteSource; + @Test(groups = { "integration", "live" }) public class AtmosIntegrationLiveTest extends BaseBlobIntegrationTest { public AtmosIntegrationLiveTest() { @@ -197,4 +202,30 @@ public class AtmosIntegrationLiveTest extends BaseBlobIntegrationTest { // Atmos maps all tiers to STANDARD assertThat(metadata.getTier()).isEqualTo(Tier.STANDARD); } + + // TODO: promote test to portable abstraction? + @Test(groups = { "integration", "live" }) + public void testETag() throws Exception { + String blobName = "test-etag"; + ByteSource payload = ByteSource.empty(); + BlobStore blobStore = view.getBlobStore(); + String containerName = getContainerName(); + try { + Blob blob = blobStore.blobBuilder(blobName) + .payload(payload) + .contentLength(payload.size()) + .build(); + String eTag = blobStore.putBlob(containerName, blob); + assertThat(eTag).hasSize(44); + + BlobMetadata metadata = blobStore.blobMetadata(containerName, blobName); + assertThat(metadata.getETag()).isEqualTo(eTag); + + for (StorageMetadata sm : blobStore.list(containerName, ListContainerOptions.NONE)) { + assertThat(sm.getETag()).isEqualTo(eTag); + } + } finally { + returnContainer(containerName); + } + } } http://git-wip-us.apache.org/repos/asf/jclouds/blob/50026c8f/core/src/main/java/org/jclouds/http/functions/ParseURIFromListOrLocationHeaderIf20x.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/jclouds/http/functions/ParseURIFromListOrLocationHeaderIf20x.java b/core/src/main/java/org/jclouds/http/functions/ParseURIFromListOrLocationHeaderIf20x.java index 7d3e746..611397e 100644 --- a/core/src/main/java/org/jclouds/http/functions/ParseURIFromListOrLocationHeaderIf20x.java +++ b/core/src/main/java/org/jclouds/http/functions/ParseURIFromListOrLocationHeaderIf20x.java @@ -71,7 +71,7 @@ public class ParseURIFromListOrLocationHeaderIf20x implements Function<HttpRespo locationUri = URI.create(location); return Uris.uriBuilder(request.getEndpoint()).path(locationUri.getPath()).query(locationUri.getQuery()).build(); } else { - throw new HttpResponseException("no uri in headers or content", null, from); + return null; } }
