You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jclouds.apache.org by ga...@apache.org on 2016/02/10 01:36:12 UTC
jclouds git commit: Add support for Swift conditional copy
Repository: jclouds
Updated Branches:
refs/heads/master a697396e8 -> 2bd055011
Add support for Swift conditional copy
Project: http://git-wip-us.apache.org/repos/asf/jclouds/repo
Commit: http://git-wip-us.apache.org/repos/asf/jclouds/commit/2bd05501
Tree: http://git-wip-us.apache.org/repos/asf/jclouds/tree/2bd05501
Diff: http://git-wip-us.apache.org/repos/asf/jclouds/diff/2bd05501
Branch: refs/heads/master
Commit: 2bd0550110acca8e5b2e9026f0eb6c9c4d4f34b4
Parents: a697396
Author: Andrew Gaul <ga...@apache.org>
Authored: Mon Feb 8 20:53:28 2016 -0800
Committer: Andrew Gaul <ga...@apache.org>
Committed: Tue Feb 9 16:34:48 2016 -0800
----------------------------------------------------------------------
.../openstack/swift/v1/features/ObjectApi.java | 96 ++++++++++++++++++++
.../openstack/swift/v1/options/CopyOptions.java | 48 ++++++++++
.../swift/v1/features/ObjectApiLiveTest.java | 60 ++++++++++++
.../swift/v1/features/ObjectApiMockTest.java | 26 ++++++
4 files changed, 230 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/jclouds/blob/2bd05501/apis/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/features/ObjectApi.java
----------------------------------------------------------------------
diff --git a/apis/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/features/ObjectApi.java b/apis/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/features/ObjectApi.java
index 335dfa6..f56a8f5 100644
--- a/apis/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/features/ObjectApi.java
+++ b/apis/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/features/ObjectApi.java
@@ -51,6 +51,7 @@ import org.jclouds.openstack.swift.v1.domain.SwiftObject;
import org.jclouds.openstack.swift.v1.functions.ETagHeader;
import org.jclouds.openstack.swift.v1.functions.ParseObjectFromResponse;
import org.jclouds.openstack.swift.v1.functions.ParseObjectListFromResponse;
+import org.jclouds.openstack.swift.v1.options.CopyOptions;
import org.jclouds.openstack.swift.v1.options.ListContainerOptions;
import org.jclouds.openstack.swift.v1.options.PutOptions;
import org.jclouds.rest.annotations.BinderParam;
@@ -264,8 +265,10 @@ public interface ObjectApi {
* @param sourceObject
* the source object name.
*
+ * @deprecated call copy(String, String, String, CopyOptions) instead
* @throws KeyNotFoundException if the source or destination container do not exist.
*/
+ @Deprecated
@Named("object:copy")
@PUT
@Path("/{destinationObject}")
@@ -275,6 +278,32 @@ public interface ObjectApi {
@PathParam("sourceObject") String sourceObject);
/**
+ * Copies an object from one container to another.
+ *
+ * <h3>NOTE</h3>
+ * This is a server side copy.
+ *
+ * @param destinationObject
+ * the destination object name.
+ * @param sourceContainer
+ * the source container name.
+ * @param sourceObject
+ * the source object name.
+ * @param options
+ * conditional copy
+ *
+ * @throws KeyNotFoundException if the source or destination container do not exist.
+ */
+ @Named("object:copy")
+ @PUT
+ @Path("/{destinationObject}")
+ @Headers(keys = OBJECT_COPY_FROM, values = "/{sourceContainer}/{sourceObject}")
+ void copy(@PathParam("destinationObject") String destinationObject,
+ @PathParam("sourceContainer") String sourceContainer,
+ @PathParam("sourceObject") String sourceObject,
+ CopyOptions options);
+
+ /**
* Copies an object from one container to another, replacing metadata.
*
* <h3>NOTE</h3>
@@ -291,8 +320,10 @@ public interface ObjectApi {
* @param objectMetadata
* Unprefixed/unescaped metadata, such as Content-Disposition
*
+ * @deprecated call copy(String, String, String, Map, Map, CopyOptions) instead
* @throws KeyNotFoundException if the source or destination container do not exist.
*/
+ @Deprecated
@Named("object:copy")
@PUT
@Path("/{destinationObject}")
@@ -303,6 +334,37 @@ public interface ObjectApi {
@BinderParam(BindObjectMetadataToHeaders.class) Map<String, String> userMetadata,
@BinderParam(BindToHeaders.class) Map<String, String> objectMetadata);
+ /**
+ * Copies an object from one container to another, replacing metadata.
+ *
+ * <h3>NOTE</h3>
+ * This is a server side copy.
+ *
+ * @param destinationObject
+ * the destination object name.
+ * @param sourceContainer
+ * the source container name.
+ * @param sourceObject
+ * the source object name.
+ * @param userMetadata
+ * Freeform metadata for the object, automatically prefixed/escaped
+ * @param objectMetadata
+ * Unprefixed/unescaped metadata, such as Content-Disposition
+ * @param options
+ * conditional copy
+ *
+ * @throws KeyNotFoundException if the source or destination container do not exist.
+ */
+ @Named("object:copy")
+ @PUT
+ @Path("/{destinationObject}")
+ @Headers(keys = {OBJECT_COPY_FROM, OBJECT_COPY_FRESH_METADATA}, values = {"/{sourceContainer}/{sourceObject}", "True"})
+ void copy(@PathParam("destinationObject") String destinationObject,
+ @PathParam("sourceContainer") String sourceContainer,
+ @PathParam("sourceObject") String sourceObject,
+ @BinderParam(BindObjectMetadataToHeaders.class) Map<String, String> userMetadata,
+ @BinderParam(BindToHeaders.class) Map<String, String> objectMetadata,
+ CopyOptions options);
/**
* Copies an object from one container to another, appending metadata.
@@ -321,8 +383,10 @@ public interface ObjectApi {
* @param objectMetadata
* Unprefixed/unescaped metadata, such as Content-Disposition
*
+ * @deprecated call copyAppendMetadata(String, String, String, Map, Map, CopyOptions) instead
* @throws KeyNotFoundException if the source or destination container do not exist.
*/
+ @Deprecated
@Named("object:copy")
@PUT
@Path("/{destinationObject}")
@@ -332,4 +396,36 @@ public interface ObjectApi {
@PathParam("sourceObject") String sourceObject,
@BinderParam(BindObjectMetadataToHeaders.class) Map<String, String> userMetadata,
@BinderParam(BindToHeaders.class) Map<String, String> objectMetadata);
+
+ /**
+ * Copies an object from one container to another, appending metadata.
+ *
+ * <h3>NOTE</h3>
+ * This is a server side copy.
+ *
+ * @param destinationObject
+ * the destination object name.
+ * @param sourceContainer
+ * the source container name.
+ * @param sourceObject
+ * the source object name.
+ * @param userMetadata
+ * Freeform metadata for the object, automatically prefixed/escaped
+ * @param objectMetadata
+ * Unprefixed/unescaped metadata, such as Content-Disposition
+ * @param options
+ * conditional copy
+ *
+ * @throws KeyNotFoundException if the source or destination container do not exist.
+ */
+ @Named("object:copy")
+ @PUT
+ @Path("/{destinationObject}")
+ @Headers(keys = OBJECT_COPY_FROM, values = "/{sourceContainer}/{sourceObject}")
+ void copyAppendMetadata(@PathParam("destinationObject") String destinationObject,
+ @PathParam("sourceContainer") String sourceContainer,
+ @PathParam("sourceObject") String sourceObject,
+ @BinderParam(BindObjectMetadataToHeaders.class) Map<String, String> userMetadata,
+ @BinderParam(BindToHeaders.class) Map<String, String> objectMetadata,
+ CopyOptions options);
}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/2bd05501/apis/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/options/CopyOptions.java
----------------------------------------------------------------------
diff --git a/apis/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/options/CopyOptions.java b/apis/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/options/CopyOptions.java
new file mode 100644
index 0000000..f7c35b78
--- /dev/null
+++ b/apis/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/options/CopyOptions.java
@@ -0,0 +1,48 @@
+/*
+ * 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.openstack.swift.v1.options;
+
+import java.util.Date;
+
+import org.jclouds.date.DateService;
+import org.jclouds.date.internal.SimpleDateFormatDateService;
+import org.jclouds.http.options.BaseHttpRequestOptions;
+
+import com.google.common.net.HttpHeaders;
+
+public final class CopyOptions extends BaseHttpRequestOptions {
+ public static final CopyOptions NONE = new CopyOptions();
+
+ private static final DateService dateService = new SimpleDateFormatDateService();
+
+ public CopyOptions ifMatch(String ifMatch) {
+ this.headers.put(HttpHeaders.IF_MATCH, ifMatch);
+ return this;
+ }
+
+ // Swift only supports If-None-Match: * which is not useful for copy
+
+ public CopyOptions ifModifiedSince(Date ifModifiedSince) {
+ this.headers.put(HttpHeaders.IF_MODIFIED_SINCE, dateService.rfc822DateFormat(ifModifiedSince));
+ return this;
+ }
+
+ public CopyOptions ifUnmodifiedSince(Date ifUnmodifiedSince) {
+ this.headers.put(HttpHeaders.IF_UNMODIFIED_SINCE, dateService.rfc822DateFormat(ifUnmodifiedSince));
+ return this;
+ }
+}
http://git-wip-us.apache.org/repos/asf/jclouds/blob/2bd05501/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/ObjectApiLiveTest.java
----------------------------------------------------------------------
diff --git a/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/ObjectApiLiveTest.java b/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/ObjectApiLiveTest.java
index dc5b4c1..33d9ac7 100644
--- a/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/ObjectApiLiveTest.java
+++ b/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/ObjectApiLiveTest.java
@@ -33,13 +33,16 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;
+import org.assertj.core.api.Fail;
import org.jclouds.blobstore.KeyNotFoundException;
+import org.jclouds.http.HttpResponseException;
import org.jclouds.http.options.GetOptions;
import org.jclouds.io.Payload;
import org.jclouds.openstack.swift.v1.SwiftApi;
import org.jclouds.openstack.swift.v1.domain.ObjectList;
import org.jclouds.openstack.swift.v1.domain.SwiftObject;
import org.jclouds.openstack.swift.v1.internal.BaseSwiftApiLiveTest;
+import org.jclouds.openstack.swift.v1.options.CopyOptions;
import org.jclouds.openstack.swift.v1.options.ListContainerOptions;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
@@ -245,6 +248,63 @@ public class ObjectApiLiveTest extends BaseSwiftApiLiveTest<SwiftApi> {
}
}
+ public void testCopyObjectConditional() throws Exception {
+ for (String regionId : regions) {
+ // source
+ String sourceContainer = "src" + containerName;
+ String sourceObjectName = "original.txt";
+ String badSource = "badSource";
+
+ // destination
+ String destinationContainer = "dest" + containerName;
+ String destinationObject = "copy.txt";
+ String destinationPath = "/" + destinationContainer + "/" + destinationObject;
+
+ ContainerApi containerApi = api.getContainerApi(regionId);
+
+ // create source and destination dirs
+ containerApi.create(sourceContainer);
+ containerApi.create(destinationContainer);
+
+ // get the api for this region and container
+ ObjectApi srcApi = api.getObjectApi(regionId, sourceContainer);
+ ObjectApi destApi = api.getObjectApi(regionId, destinationContainer);
+
+ // Create source object
+ assertNotNull(srcApi.put(sourceObjectName, PAYLOAD));
+ SwiftObject sourceObject = srcApi.get(sourceObjectName);
+ checkObject(sourceObject);
+
+ destApi.copy(destinationObject, sourceContainer, sourceObjectName, new CopyOptions().ifMatch(sourceObject.getETag()));
+ try {
+ destApi.copy(destinationObject, sourceContainer, sourceObjectName, new CopyOptions().ifMatch("fake-etag"));
+ Fail.failBecauseExceptionWasNotThrown(HttpResponseException.class);
+ } catch (HttpResponseException hre) {
+ assertThat(hre.getResponse().getStatusCode()).isEqualTo(412);
+ }
+
+ long now = System.currentTimeMillis();
+ Date before = new Date(now - 1000 * 1000);
+ Date after = new Date(now + 1000 * 1000);
+
+ destApi.copy(destinationObject, sourceContainer, sourceObjectName, new CopyOptions().ifModifiedSince(before));
+ try {
+ destApi.copy(destinationObject, sourceContainer, sourceObjectName, new CopyOptions().ifModifiedSince(after));
+ Fail.failBecauseExceptionWasNotThrown(HttpResponseException.class);
+ } catch (HttpResponseException hre) {
+ assertThat(hre.getResponse().getStatusCode()).isEqualTo(304);
+ }
+
+ try {
+ destApi.copy(destinationObject, sourceContainer, sourceObjectName, new CopyOptions().ifUnmodifiedSince(before));
+ Fail.failBecauseExceptionWasNotThrown(HttpResponseException.class);
+ } catch (HttpResponseException hre) {
+ assertThat(hre.getResponse().getStatusCode()).isEqualTo(412);
+ }
+ destApi.copy(destinationObject, sourceContainer, sourceObjectName, new CopyOptions().ifUnmodifiedSince(after));
+ }
+ }
+
public void testList() throws Exception {
for (String regionId : regions) {
ObjectApi objectApi = api.getObjectApi(regionId, containerName);
http://git-wip-us.apache.org/repos/asf/jclouds/blob/2bd05501/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/ObjectApiMockTest.java
----------------------------------------------------------------------
diff --git a/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/ObjectApiMockTest.java b/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/ObjectApiMockTest.java
index 77305eb..096a0e1 100644
--- a/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/ObjectApiMockTest.java
+++ b/apis/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/ObjectApiMockTest.java
@@ -19,6 +19,7 @@ package org.jclouds.openstack.swift.v1.features;
import static com.google.common.base.Charsets.US_ASCII;
import static com.google.common.io.BaseEncoding.base16;
import static com.google.common.net.HttpHeaders.EXPIRES;
+import static org.assertj.core.api.Assertions.assertThat;
import static org.jclouds.Constants.PROPERTY_MAX_RETRIES;
import static org.jclouds.Constants.PROPERTY_RETRY_DELAY_START;
import static org.jclouds.Constants.PROPERTY_SO_TIMEOUT;
@@ -52,6 +53,7 @@ import org.jclouds.io.payloads.ByteSourcePayload;
import org.jclouds.openstack.swift.v1.SwiftApi;
import org.jclouds.openstack.swift.v1.domain.ObjectList;
import org.jclouds.openstack.swift.v1.domain.SwiftObject;
+import org.jclouds.openstack.swift.v1.options.CopyOptions;
import org.jclouds.openstack.swift.v1.options.ListContainerOptions;
import org.jclouds.openstack.swift.v1.reference.SwiftHeaders;
import org.jclouds.openstack.v2_0.internal.BaseOpenStackMockTest;
@@ -484,6 +486,30 @@ public class ObjectApiMockTest extends BaseOpenStackMockTest<SwiftApi> {
}
}
+ public void testCopyObjectConditional() throws Exception {
+ MockWebServer server = mockOpenStackServer();
+ server.enqueue(addCommonHeaders(new MockResponse().setBody(stringFromResource("/access.json"))));
+ server.enqueue(addCommonHeaders(new MockResponse().setResponseCode(201)
+ .addHeader(SwiftHeaders.OBJECT_COPY_FROM, "/bar/foo.txt")));
+ try {
+ SwiftApi api = api(server.getUrl("/").toString(), "openstack-swift");
+ api.getObjectApi("DFW", "foo").copy("bar.txt", "bar", "foo.txt", new CopyOptions().ifMatch("fakeetag"));
+
+ assertEquals(server.getRequestCount(), 2);
+ assertEquals(server.takeRequest().getRequestLine(), "POST /tokens HTTP/1.1");
+
+ RecordedRequest copyRequest = server.takeRequest();
+ assertEquals(copyRequest.getRequestLine(),
+ "PUT /v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9/foo/bar.txt HTTP/1.1");
+
+ List<String> requestHeaders = copyRequest.getHeaders();
+ assertThat(requestHeaders).contains("If-Match: fakeetag");
+ assertThat(requestHeaders).contains(SwiftHeaders.OBJECT_COPY_FROM + ": /bar/foo.txt");
+ } finally {
+ server.shutdown();
+ }
+ }
+
@Test(expectedExceptions = KeyNotFoundException.class)
public void testCopyObjectFail() throws InterruptedException, IOException {
MockWebServer server = mockOpenStackServer();