Updated Branches:
  refs/heads/master 6fbc1932e -> cc1e2ae4d

JCLOUDS-307. Support Swift Static Large Object


Project: 
http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/repo
Commit: 
http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/commit/cc1e2ae4
Tree: 
http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/tree/cc1e2ae4
Diff: 
http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/diff/cc1e2ae4

Branch: refs/heads/master
Commit: cc1e2ae4d7f6db038e7b4b9f7dd228b2321a6cd6
Parents: d7b6e87
Author: Adrian Cole <[email protected]>
Authored: Sun Sep 29 13:23:57 2013 -0700
Committer: Adrian Cole <[email protected]>
Committed: Sun Sep 29 13:36:20 2013 -0700

----------------------------------------------------------------------
 .../jclouds/openstack/swift/v1/SwiftApi.java    |   7 +
 .../openstack/swift/v1/domain/Segment.java      | 129 +++++++++++++++++++
 .../openstack/swift/v1/domain/SwiftObject.java  |   2 +-
 .../swift/v1/features/StaticLargeObjectApi.java |  87 +++++++++++++
 .../swift/v1/functions/ETagHeader.java          |   3 +-
 .../swift/v1/features/BulkApiLiveTest.java      |  26 +---
 .../features/StaticLargeObjectApiLiveTest.java  | 128 ++++++++++++++++++
 .../features/StaticLargeObjectApiMockTest.java  | 109 ++++++++++++++++
 .../swift/v1/internal/BaseSwiftApiLiveTest.java |  23 ++++
 9 files changed, 490 insertions(+), 24 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/cc1e2ae4/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/SwiftApi.java
----------------------------------------------------------------------
diff --git 
a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/SwiftApi.java 
b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/SwiftApi.java
index b18a521..57ebc9c 100644
--- a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/SwiftApi.java
+++ b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/SwiftApi.java
@@ -29,6 +29,7 @@ import org.jclouds.openstack.swift.v1.features.AccountApi;
 import org.jclouds.openstack.swift.v1.features.BulkApi;
 import org.jclouds.openstack.swift.v1.features.ContainerApi;
 import org.jclouds.openstack.swift.v1.features.ObjectApi;
+import org.jclouds.openstack.swift.v1.features.StaticLargeObjectApi;
 import org.jclouds.rest.annotations.Delegate;
 import org.jclouds.rest.annotations.EndpointParam;
 
@@ -60,4 +61,10 @@ public interface SwiftApi extends Closeable {
    @Path("/{containerName}")
    ObjectApi objectApiInRegionForContainer(@EndpointParam(parser = 
RegionToEndpoint.class) @Nullable String region,
          @PathParam("containerName") String containerName);
+
+   @Delegate
+   @Path("/{containerName}")
+   StaticLargeObjectApi staticLargeObjectApiInRegionForContainer(
+         @EndpointParam(parser = RegionToEndpoint.class) @Nullable String 
region,
+         @PathParam("containerName") String containerName);
 }

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/cc1e2ae4/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/Segment.java
----------------------------------------------------------------------
diff --git 
a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/Segment.java
 
b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/Segment.java
new file mode 100644
index 0000000..3d24b05
--- /dev/null
+++ 
b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/Segment.java
@@ -0,0 +1,129 @@
+/*
+ * 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.domain;
+
+import static com.google.common.base.Objects.equal;
+import static com.google.common.base.Objects.toStringHelper;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.base.Objects;
+
+/**
+ * One piece of a multi-part upload.
+ * 
+ * @see <a
+ *      
href="http://docs.openstack.org/api/openstack-object-storage/1.0/content/static-large-objects.html";>api
+ *      doc</a>
+ */
+public class Segment {
+
+   private final String path;
+   private final String etag;
+   private final long size_bytes;
+
+   private Segment(String path, String etag, long sizeBytes) {
+      this.path = checkNotNull(path, "path");
+      this.etag = checkNotNull(etag, "etag of %s", path);
+      this.size_bytes = checkNotNull(sizeBytes, "sizeBytes of %s", path);
+   }
+
+   /**
+    * {@code /container/objectName} which corresponds to the path of
+    * {@link SwiftObject#uri()}.
+    */
+   public String path() {
+      return path;
+   }
+
+   /**
+    * Corresponds to the {@code ETag} header of the response, and is usually 
the
+    * MD5 checksum of the object
+    */
+   public String etag() {
+      return etag;
+   }
+
+   public long sizeBytes() {
+      return size_bytes;
+   }
+
+   @Override
+   public boolean equals(Object object) {
+      if (this == object) {
+         return true;
+      }
+      if (object instanceof Segment) {
+         Segment that = Segment.class.cast(object);
+         return equal(path(), that.path()) //
+               && equal(etag(), that.etag()) //
+               && equal(sizeBytes(), that.sizeBytes());
+      } else {
+         return false;
+      }
+   }
+
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(path(), etag(), sizeBytes());
+   }
+
+   @Override
+   public String toString() {
+      return toStringHelper("") //
+            .add("path", path()) //
+            .add("etag", etag()) //
+            .add("sizeBytes", sizeBytes()).toString();
+   }
+
+   public static Builder builder() {
+      return new Builder();
+   }
+
+   public static class Builder {
+      protected String path;
+      protected String etag;
+      protected long sizeBytes;
+
+      /**
+       * @see Segment#path()
+       */
+      public Builder path(String path) {
+         this.path = path;
+         return this;
+      }
+
+      /**
+       * @see Segment#etag()
+       */
+      public Builder etag(String etag) {
+         this.etag = etag;
+         return this;
+      }
+
+      /**
+       * @see Segment#sizeBytes()
+       */
+      public Builder sizeBytes(long sizeBytes) {
+         this.sizeBytes = sizeBytes;
+         return this;
+      }
+
+      public Segment build() {
+         return new Segment(path, etag, sizeBytes);
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/cc1e2ae4/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/SwiftObject.java
----------------------------------------------------------------------
diff --git 
a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/SwiftObject.java
 
b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/SwiftObject.java
index e94fde4..81f690c 100644
--- 
a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/SwiftObject.java
+++ 
b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/domain/SwiftObject.java
@@ -51,7 +51,7 @@ public class SwiftObject implements Comparable<SwiftObject> {
          Payload payload) {
       this.name = checkNotNull(name, "name");
       this.uri = checkNotNull(uri, "uri of %s", uri);
-      this.etag = checkNotNull(etag, "etag of %s", name);
+      this.etag = checkNotNull(etag, "etag of %s", name).replace("\"", "");
       this.lastModified = checkNotNull(lastModified, "lastModified of %s", 
name);
       this.metadata = metadata == null ? ImmutableMap.<String, String> of() : 
metadata;
       this.payload = checkNotNull(payload, "payload of %s", name);

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/cc1e2ae4/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/features/StaticLargeObjectApi.java
----------------------------------------------------------------------
diff --git 
a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/features/StaticLargeObjectApi.java
 
b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/features/StaticLargeObjectApi.java
new file mode 100644
index 0000000..cc651cd
--- /dev/null
+++ 
b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/features/StaticLargeObjectApi.java
@@ -0,0 +1,87 @@
+/*
+ * 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.features;
+
+import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.inject.Named;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+
+import org.jclouds.Fallbacks.VoidOnNotFoundOr404;
+import org.jclouds.openstack.keystone.v2_0.filters.AuthenticateRequest;
+import 
org.jclouds.openstack.swift.v1.binders.BindMetadataToHeaders.BindObjectMetadataToHeaders;
+import org.jclouds.openstack.swift.v1.domain.Segment;
+import org.jclouds.openstack.swift.v1.domain.SwiftObject;
+import org.jclouds.openstack.swift.v1.functions.ETagHeader;
+import org.jclouds.rest.annotations.BinderParam;
+import org.jclouds.rest.annotations.Fallback;
+import org.jclouds.rest.annotations.QueryParams;
+import org.jclouds.rest.annotations.RequestFilters;
+import org.jclouds.rest.annotations.ResponseParser;
+import org.jclouds.rest.binders.BindToJsonPayload;
+
+/**
+ * @see <a
+ *      
href="http://docs.openstack.org/api/openstack-object-storage/1.0/content/static-large-objects.html";>
+ *      Static Large Objects API</a>
+ */
+@RequestFilters(AuthenticateRequest.class)
+@Consumes(APPLICATION_JSON)
+public interface StaticLargeObjectApi {
+
+   /**
+    * Creates or updates a static large object's manifest.
+    * 
+    * @param objectName
+    *           corresponds to {@link SwiftObject#name()}.
+    * @param segments
+    *           ordered parts which will be concatenated upon download.
+    * @param metadata
+    *           corresponds to {@link SwiftObject#metadata()}.
+    * 
+    * @return {@link SwiftObject#etag()} of the object, which is the MD5
+    *         checksum of the concatenated ETag values of the {@code segments}.
+    */
+   @Named("CreateOrUpdateStaticLargeObjectManifest")
+   @PUT
+   @ResponseParser(ETagHeader.class)
+   @Path("/{objectName}")
+   @QueryParams(keys = "multipart-manifest", values = "put")
+   String replaceManifest(@PathParam("objectName") String objectName,
+         @BinderParam(BindToJsonPayload.class) List<Segment> segments,
+         @BinderParam(BindObjectMetadataToHeaders.class) Map<String, String> 
metadata);
+
+   /**
+    * Deletes a static large object, if present, including all of its segments.
+    * 
+    * @param objectName
+    *           corresponds to {@link SwiftObject#name()}.
+    */
+   @Named("DeleteStaticLargeObject")
+   @DELETE
+   @Fallback(VoidOnNotFoundOr404.class)
+   @Path("/{objectName}")
+   @QueryParams(keys = "multipart-manifest", values = "delete")
+   void delete(@PathParam("objectName") String objectName);
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/cc1e2ae4/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/functions/ETagHeader.java
----------------------------------------------------------------------
diff --git 
a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/functions/ETagHeader.java
 
b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/functions/ETagHeader.java
index f5c9dd2..25a749b 100644
--- 
a/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/functions/ETagHeader.java
+++ 
b/openstack-swift/src/main/java/org/jclouds/openstack/swift/v1/functions/ETagHeader.java
@@ -26,6 +26,7 @@ public class ETagHeader implements Function<HttpResponse, 
String> {
 
    @Override
    public String apply(HttpResponse from) {
-      return from.getFirstHeaderOrNull(ETAG);
+      String etag = from.getFirstHeaderOrNull(ETAG);
+      return etag != null ? etag.replace("\"", "") : null;
    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/cc1e2ae4/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/BulkApiLiveTest.java
----------------------------------------------------------------------
diff --git 
a/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/BulkApiLiveTest.java
 
b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/BulkApiLiveTest.java
index 26d2045..70f5c2f 100644
--- 
a/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/BulkApiLiveTest.java
+++ 
b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/BulkApiLiveTest.java
@@ -16,7 +16,6 @@
  */
 package org.jclouds.openstack.swift.v1.features;
 
-import static com.google.common.base.Preconditions.checkState;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertTrue;
 
@@ -31,15 +30,12 @@ import org.jboss.shrinkwrap.api.exporter.TarGzExporter;
 import org.jclouds.io.Payloads;
 import org.jclouds.openstack.swift.v1.domain.BulkDeleteResponse;
 import org.jclouds.openstack.swift.v1.domain.ExtractArchiveResponse;
-import org.jclouds.openstack.swift.v1.domain.SwiftObject;
 import org.jclouds.openstack.swift.v1.internal.BaseSwiftApiLiveTest;
 import org.jclouds.openstack.swift.v1.options.CreateContainerOptions;
-import org.jclouds.openstack.swift.v1.options.ListContainerOptions;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
-import com.google.common.base.Function;
 import com.google.common.base.Throwables;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
@@ -71,7 +67,8 @@ public class BulkApiLiveTest extends BaseSwiftApiLiveTest {
          
assertEquals(api.containerApiInRegion(regionId).get(containerName).objectCount(),
 OBJECT_COUNT);
 
          // repeat the command
-         extractResponse = 
api.bulkApiInRegion(regionId).extractArchive(containerName, 
Payloads.newPayload(tarGz), "tar.gz");
+         extractResponse = 
api.bulkApiInRegion(regionId).extractArchive(containerName, 
Payloads.newPayload(tarGz),
+               "tar.gz");
          assertEquals(extractResponse.created(), OBJECT_COUNT);
          assertTrue(extractResponse.errors().isEmpty());
       }
@@ -99,7 +96,7 @@ public class BulkApiLiveTest extends BaseSwiftApiLiveTest {
          boolean created = 
api.containerApiInRegion(regionId).createIfAbsent(containerName,
                new CreateContainerOptions());
          if (!created) {
-            deleteAllObjectsInContainer(regionId);
+            deleteAllObjectsInContainer(regionId, containerName);
          }
       }
       GenericArchive files = ShrinkWrap.create(GenericArchive.class, 
"files.tar.gz");
@@ -119,24 +116,9 @@ public class BulkApiLiveTest extends BaseSwiftApiLiveTest {
    @AfterClass(groups = "live")
    public void tearDown() {
       for (String regionId : api.configuredRegions()) {
-         deleteAllObjectsInContainer(regionId);
+         deleteAllObjectsInContainer(regionId, containerName);
          api.containerApiInRegion(regionId).deleteIfEmpty(containerName);
       }
       super.tearDown();
    }
-
-   void deleteAllObjectsInContainer(String regionId) {
-      ImmutableList<String> pathsToDelete = 
api.objectApiInRegionForContainer(regionId, containerName)
-            .list(new ListContainerOptions()).transform(new 
Function<SwiftObject, String>() {
-
-               public String apply(SwiftObject input) {
-                  return containerName + "/" + input.name();
-               }
-
-            }).toList();
-      if (!pathsToDelete.isEmpty()) {
-         BulkDeleteResponse response = 
api.bulkApiInRegion(regionId).bulkDelete(pathsToDelete);
-         checkState(response.errors().isEmpty(), "Errors deleting paths %s: 
%s", pathsToDelete, response);
-      }
-   }
 }

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/cc1e2ae4/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/StaticLargeObjectApiLiveTest.java
----------------------------------------------------------------------
diff --git 
a/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/StaticLargeObjectApiLiveTest.java
 
b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/StaticLargeObjectApiLiveTest.java
new file mode 100644
index 0000000..36af318
--- /dev/null
+++ 
b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/StaticLargeObjectApiLiveTest.java
@@ -0,0 +1,128 @@
+/*
+ * 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.features;
+
+import static java.lang.String.format;
+import static org.jclouds.io.Payloads.newPayload;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotEquals;
+import static org.testng.Assert.assertNotNull;
+
+import java.util.List;
+import java.util.UUID;
+
+import org.jclouds.openstack.swift.v1.domain.Segment;
+import org.jclouds.openstack.swift.v1.domain.SwiftObject;
+import org.jclouds.openstack.swift.v1.internal.BaseSwiftApiLiveTest;
+import org.jclouds.openstack.swift.v1.options.CreateContainerOptions;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+@Test(groups = "live", testName = "StaticLargeObjectApiLiveTest")
+public class StaticLargeObjectApiLiveTest extends BaseSwiftApiLiveTest {
+
+   private String name = getClass().getSimpleName();
+   private String containerName = getClass().getSimpleName() + "Container";
+   private byte[] megOf1s;
+   private byte[] megOf2s;
+
+   public void notPresentWhenDeleting() throws Exception {
+      for (String regionId : api.configuredRegions()) {
+         api.staticLargeObjectApiInRegionForContainer(regionId, 
containerName).delete(UUID.randomUUID().toString());
+      }
+   }
+
+   public void replaceManifest() throws Exception {
+      for (String regionId : api.configuredRegions()) {
+         ObjectApi objectApi = api.objectApiInRegionForContainer(regionId, 
containerName);
+         String etag1s = objectApi.replace(name + "/1", newPayload(megOf1s), 
ImmutableMap.<String, String> of());
+         assertMegabyteAndETagMatches(regionId, name + "/1", etag1s);
+
+         String etag2s = objectApi.replace(name + "/2", newPayload(megOf2s), 
ImmutableMap.<String, String> of());
+         assertMegabyteAndETagMatches(regionId, name + "/2", etag2s);
+
+         List<Segment> segments = ImmutableList.<Segment> builder()
+               .add(Segment.builder()
+                           .path(format("%s/%s/1", containerName, 
name)).etag(etag1s).sizeBytes(1024 * 1024)
+                           .build())
+               .add(Segment.builder()
+                           .path(format("%s/%s/2", containerName, 
name)).etag(etag2s).sizeBytes(1024 * 1024)
+                           .build())
+               .build();
+
+         String etagOfEtags = 
api.staticLargeObjectApiInRegionForContainer(regionId, 
containerName).replaceManifest(
+               name, segments, ImmutableMap.of("myfoo", "Bar"));
+
+         assertNotNull(etagOfEtags);
+
+         SwiftObject bigObject = api.objectApiInRegionForContainer(regionId, 
containerName).head(name);
+         assertNotEquals(bigObject.etag(), etagOfEtags);
+         
assertEquals(bigObject.payload().getContentMetadata().getContentLength(), new 
Long(2 * 1024 * 1024));
+         assertEquals(bigObject.metadata(), ImmutableMap.of("myfoo", "Bar"));
+
+         // segments are visible
+         
assertEquals(api.containerApiInRegion(regionId).get(containerName).objectCount(),
 3);
+      }
+   }
+
+   @Test(dependsOnMethods = "replaceManifest")
+   public void delete() throws Exception {
+      for (String regionId : api.configuredRegions()) {
+         api.staticLargeObjectApiInRegionForContainer(regionId, 
containerName).delete(name);
+         
assertEquals(api.containerApiInRegion(regionId).get(containerName).objectCount(),
 0);
+      }
+   }
+
+   @Override
+   @BeforeClass(groups = "live")
+   public void setup() {
+      super.setup();
+      for (String regionId : api.configuredRegions()) {
+         boolean created = 
api.containerApiInRegion(regionId).createIfAbsent(containerName,
+               new CreateContainerOptions());
+         if (!created) {
+            deleteAllObjectsInContainer(regionId, containerName);
+         }
+      }
+      megOf1s = new byte[1024 * 1024];
+      megOf2s = new byte[1024 * 1024];
+      for (int i = 0; i < 1024 * 1024; i++) {
+         megOf1s[i] = 1;
+         megOf2s[i] = 2;
+      }
+   }
+
+   @Override
+   @AfterClass(groups = "live")
+   public void tearDown() {
+      for (String regionId : api.configuredRegions()) {
+         deleteAllObjectsInContainer(regionId, containerName);
+         api.containerApiInRegion(regionId).deleteIfEmpty(containerName);
+      }
+      super.tearDown();
+   }
+
+   protected void assertMegabyteAndETagMatches(String regionId, String name, 
String etag1s) {
+      SwiftObject object1s = api.objectApiInRegionForContainer(regionId, 
containerName).head(name);
+      assertEquals(object1s.etag(), etag1s);
+      assertEquals(object1s.payload().getContentMetadata().getContentLength(), 
new Long(1024 * 1024));
+   }
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/cc1e2ae4/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/StaticLargeObjectApiMockTest.java
----------------------------------------------------------------------
diff --git 
a/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/StaticLargeObjectApiMockTest.java
 
b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/StaticLargeObjectApiMockTest.java
new file mode 100644
index 0000000..a44e2d1
--- /dev/null
+++ 
b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/features/StaticLargeObjectApiMockTest.java
@@ -0,0 +1,109 @@
+/*
+ * 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.features;
+
+import static org.testng.Assert.assertEquals;
+
+import org.jclouds.openstack.swift.v1.SwiftApi;
+import org.jclouds.openstack.swift.v1.domain.Segment;
+import org.jclouds.openstack.swift.v1.internal.BaseSwiftMockTest;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.net.HttpHeaders;
+import com.squareup.okhttp.mockwebserver.MockResponse;
+import com.squareup.okhttp.mockwebserver.MockWebServer;
+import com.squareup.okhttp.mockwebserver.RecordedRequest;
+
+@Test
+public class StaticLargeObjectApiMockTest extends BaseSwiftMockTest {
+
+   public void replaceManifest() throws Exception {
+      MockWebServer server = mockSwiftServer();
+      server.enqueue(new MockResponse().setBody(access));
+      server.enqueue(new MockResponse().addHeader(HttpHeaders.ETAG, 
"\"abcd\""));
+
+      try {
+         SwiftApi api = swiftApi(server.getUrl("/").toString());
+         assertEquals(
+               api.staticLargeObjectApiInRegionForContainer("DFW", 
"myContainer").replaceManifest(
+                     "myObject",
+                     ImmutableList
+                           .<Segment> builder()
+                           
.add(Segment.builder().path("/mycontainer/objseg1").etag("0228c7926b8b642dfb29554cd1f00963")
+                                 .sizeBytes(1468006).build())
+                           
.add(Segment.builder().path("/mycontainer/pseudodir/seg-obj2")
+                                 
.etag("5bfc9ea51a00b790717eeb934fb77b9b").sizeBytes(1572864).build())
+                           
.add(Segment.builder().path("/other-container/seg-final")
+                                 
.etag("b9c3da507d2557c1ddc51f27c54bae51").sizeBytes(256).build()).build(),
+                     ImmutableMap.of("MyFoo", "Bar")), "abcd");
+
+         assertEquals(server.getRequestCount(), 2);
+         assertEquals(server.takeRequest().getRequestLine(), "POST /tokens 
HTTP/1.1");
+         RecordedRequest replaceRequest = server.takeRequest();
+         assertEquals(replaceRequest.getRequestLine(),
+               "PUT 
/v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9/myContainer/myObject?multipart-manifest=put
 HTTP/1.1");
+         assertEquals(replaceRequest.getHeader("x-object-meta-myfoo"), "Bar");
+         assertEquals(
+               new String(replaceRequest.getBody()),
+         
"[{\"path\":\"/mycontainer/objseg1\",\"etag\":\"0228c7926b8b642dfb29554cd1f00963\",\"size_bytes\":1468006},"
 +
+          
"{\"path\":\"/mycontainer/pseudodir/seg-obj2\",\"etag\":\"5bfc9ea51a00b790717eeb934fb77b9b\",\"size_bytes\":1572864},"
 +
+          
"{\"path\":\"/other-container/seg-final\",\"etag\":\"b9c3da507d2557c1ddc51f27c54bae51\",\"size_bytes\":256}]");
+      } finally {
+         server.shutdown();
+      }
+   }
+
+   public void delete() throws Exception {
+      MockWebServer server = mockSwiftServer();
+      server.enqueue(new MockResponse().setBody(access));
+      server.enqueue(new MockResponse().setResponseCode(204));
+
+      try {
+         SwiftApi api = swiftApi(server.getUrl("/").toString());
+         api.staticLargeObjectApiInRegionForContainer("DFW", 
"myContainer").delete("myObject");
+
+         assertEquals(server.getRequestCount(), 2);
+         assertEquals(server.takeRequest().getRequestLine(), "POST /tokens 
HTTP/1.1");
+         RecordedRequest deleteRequest = server.takeRequest();
+         assertEquals(deleteRequest.getRequestLine(),
+               "DELETE 
/v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9/myContainer/myObject?multipart-manifest=delete
 HTTP/1.1");
+      } finally {
+         server.shutdown();
+      }
+   }
+
+   public void alreadyDeleted() throws Exception {
+      MockWebServer server = mockSwiftServer();
+      server.enqueue(new MockResponse().setBody(access));
+      server.enqueue(new MockResponse().setResponseCode(404));
+
+      try {
+         SwiftApi api = swiftApi(server.getUrl("/").toString());
+         api.staticLargeObjectApiInRegionForContainer("DFW", 
"myContainer").delete("myObject");
+
+         assertEquals(server.getRequestCount(), 2);
+         assertEquals(server.takeRequest().getRequestLine(), "POST /tokens 
HTTP/1.1");
+         RecordedRequest deleteRequest = server.takeRequest();
+         assertEquals(deleteRequest.getRequestLine(),
+               "DELETE 
/v1/MossoCloudFS_5bcf396e-39dd-45ff-93a1-712b9aba90a9/myContainer/myObject?multipart-manifest=delete
 HTTP/1.1");
+      } finally {
+         server.shutdown();
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/incubator-jclouds-labs-openstack/blob/cc1e2ae4/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/internal/BaseSwiftApiLiveTest.java
----------------------------------------------------------------------
diff --git 
a/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/internal/BaseSwiftApiLiveTest.java
 
b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/internal/BaseSwiftApiLiveTest.java
index fc01490..491a5f6 100644
--- 
a/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/internal/BaseSwiftApiLiveTest.java
+++ 
b/openstack-swift/src/test/java/org/jclouds/openstack/swift/v1/internal/BaseSwiftApiLiveTest.java
@@ -16,11 +16,19 @@
  */
 package org.jclouds.openstack.swift.v1.internal;
 
+import static com.google.common.base.Preconditions.checkState;
+
 import java.util.Properties;
 
 import org.jclouds.apis.BaseApiLiveTest;
 import org.jclouds.openstack.keystone.v2_0.config.KeystoneProperties;
 import org.jclouds.openstack.swift.v1.SwiftApi;
+import org.jclouds.openstack.swift.v1.domain.BulkDeleteResponse;
+import org.jclouds.openstack.swift.v1.domain.SwiftObject;
+import org.jclouds.openstack.swift.v1.options.ListContainerOptions;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableList;
 
 public class BaseSwiftApiLiveTest extends BaseApiLiveTest<SwiftApi> {
 
@@ -34,4 +42,19 @@ public class BaseSwiftApiLiveTest extends 
BaseApiLiveTest<SwiftApi> {
       setIfTestSystemPropertyPresent(props, 
KeystoneProperties.CREDENTIAL_TYPE);
       return props;
    }
+
+   protected void deleteAllObjectsInContainer(String regionId, final String 
containerName) {
+      ImmutableList<String> pathsToDelete = 
api.objectApiInRegionForContainer(regionId, containerName)
+            .list(new ListContainerOptions()).transform(new 
Function<SwiftObject, String>() {
+
+               public String apply(SwiftObject input) {
+                  return containerName + "/" + input.name();
+               }
+
+            }).toList();
+      if (!pathsToDelete.isEmpty()) {
+         BulkDeleteResponse response = 
api.bulkApiInRegion(regionId).bulkDelete(pathsToDelete);
+         checkState(response.errors().isEmpty(), "Errors deleting paths %s: 
%s", pathsToDelete, response);
+      }
+   }
 }

Reply via email to