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;
          }
 
       }

Reply via email to