mirror of https://github.com/apache/jclouds.git
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.
This commit is contained in:
parent
e473d7df6a
commit
50026c8f2d
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue