JCLOUDS-457: Added list Multipart operations

Added listParts and listMultipartUploads operations to the Glacier
client.
diff --git a/glacier/src/main/java/org/jclouds/glacier/GlacierAsyncClient.java b/glacier/src/main/java/org/jclouds/glacier/GlacierAsyncClient.java
index e492b7c..7517f56 100644
--- a/glacier/src/main/java/org/jclouds/glacier/GlacierAsyncClient.java
+++ b/glacier/src/main/java/org/jclouds/glacier/GlacierAsyncClient.java
@@ -38,12 +38,16 @@
 import org.jclouds.glacier.binders.BindHashesToHeaders;
 import org.jclouds.glacier.binders.BindMultipartTreeHashToHeaders;
 import org.jclouds.glacier.binders.BindPartSizeToHeaders;
+import org.jclouds.glacier.domain.MultipartUploadMetadata;
+import org.jclouds.glacier.domain.PaginatedMultipartUploadCollection;
 import org.jclouds.glacier.domain.PaginatedVaultCollection;
 import org.jclouds.glacier.domain.VaultMetadata;
 import org.jclouds.glacier.fallbacks.FalseOnIllegalArgumentException;
 import org.jclouds.glacier.filters.RequestAuthorizeSignature;
 import org.jclouds.glacier.functions.ParseArchiveIdHeader;
 import org.jclouds.glacier.functions.ParseMultipartUploadIdHeader;
+import org.jclouds.glacier.functions.ParseMultipartUploadListFromHttpContent;
+import org.jclouds.glacier.functions.ParseMultipartUploadPartListFromHttpContent;
 import org.jclouds.glacier.functions.ParseMultipartUploadTreeHashHeader;
 import org.jclouds.glacier.functions.ParseVaultMetadataFromHttpContent;
 import org.jclouds.glacier.functions.ParseVaultMetadataListFromHttpContent;
@@ -214,4 +218,48 @@
    ListenableFuture<Boolean> abortMultipartUpload(
          @ParamValidators(VaultNameValidator.class) @PathParam("vault") String vaultName,
          @PathParam("uploadId") String uploadId);
+
+   /**
+    * @see GlacierClient#listParts
+    */
+   @Named("ListParts")
+   @GET
+   @Path("/-/vaults/{vault}/multipart-uploads/{uploadId}")
+   @ResponseParser(ParseMultipartUploadPartListFromHttpContent.class)
+   ListenableFuture<MultipartUploadMetadata> listParts(
+         @ParamValidators(VaultNameValidator.class) @PathParam("vault") String vaultName,
+         @PathParam("uploadId") String uploadId,
+         PaginationOptions options);
+
+   /**
+    * @see GlacierClient#listParts
+    */
+   @Named("ListParts")
+   @GET
+   @Path("/-/vaults/{vault}/multipart-uploads/{uploadId}")
+   @ResponseParser(ParseMultipartUploadPartListFromHttpContent.class)
+   ListenableFuture<MultipartUploadMetadata> listParts(
+         @ParamValidators(VaultNameValidator.class) @PathParam("vault") String vaultName,
+         @PathParam("uploadId") String uploadId);
+
+   /**
+    * @see GlacierClient#listMultipartUploads
+    */
+   @Named("ListMultipartUploads")
+   @GET
+   @Path("/-/vaults/{vault}/multipart-uploads")
+   @ResponseParser(ParseMultipartUploadListFromHttpContent.class)
+   ListenableFuture<PaginatedMultipartUploadCollection> listMultipartUploads(
+         @ParamValidators(VaultNameValidator.class) @PathParam("vault") String vaultName,
+         PaginationOptions options);
+
+   /**
+    * @see GlacierClient#listMultipartUploads
+    */
+   @Named("ListMultipartUploads")
+   @GET
+   @Path("/-/vaults/{vault}/multipart-uploads")
+   @ResponseParser(ParseMultipartUploadListFromHttpContent.class)
+   ListenableFuture<PaginatedMultipartUploadCollection> listMultipartUploads(
+         @ParamValidators(VaultNameValidator.class) @PathParam("vault") String vaultName);
 }
diff --git a/glacier/src/main/java/org/jclouds/glacier/GlacierClient.java b/glacier/src/main/java/org/jclouds/glacier/GlacierClient.java
index 29f743d..162ec91 100644
--- a/glacier/src/main/java/org/jclouds/glacier/GlacierClient.java
+++ b/glacier/src/main/java/org/jclouds/glacier/GlacierClient.java
@@ -20,6 +20,8 @@
 import java.net.URI;
 import java.util.Map;
 
+import org.jclouds.glacier.domain.MultipartUploadMetadata;
+import org.jclouds.glacier.domain.PaginatedMultipartUploadCollection;
 import org.jclouds.glacier.domain.PaginatedVaultCollection;
 import org.jclouds.glacier.domain.VaultMetadata;
 import org.jclouds.glacier.options.PaginationOptions;
@@ -180,4 +182,40 @@
     * @see <a href="http://docs.aws.amazon.com/amazonglacier/latest/dev/api-multipart-abort-upload.html" />
     */
    boolean abortMultipartUpload(String vaultName, String uploadId);
+
+   /**
+    * Lists the multipart upload parts.
+    *
+    * @param vaultName
+    *           Name of the Vault where the archive is going to be stored.
+    * @param uploadId
+    *           Multipart upload identifier.
+    * @param options
+    *          Options used for pagination.
+    * @return A MultipartUploadMetadata, containing an iterable part list with a marker.
+    * @see <a href="http://docs.aws.amazon.com/amazonglacier/latest/dev/api-multipart-list-parts.html" />
+    */
+   MultipartUploadMetadata listParts(String vaultName, String uploadId, PaginationOptions options);
+
+   /**
+    * Lists the multipart upload parts.
+    */
+   MultipartUploadMetadata listParts(String vaultName, String uploadId);
+
+   /**
+    * Lists the multipart uploads in a vault.
+    *
+    * @param vaultName
+    *           Name of the Vault where the archive is going to be stored.
+    * @param options
+    *          Options used for pagination.
+    * @return A PaginatedMultipartUploadCollection, containing an iterable multipart upload list with a marker.
+    * @see <a href="http://docs.aws.amazon.com/amazonglacier/latest/dev/api-multipart-list-uploads.html" />
+    */
+   PaginatedMultipartUploadCollection listMultipartUploads(String vaultName, PaginationOptions options);
+
+   /**
+    * Lists the multipart uploads in a vault.
+    */
+   PaginatedMultipartUploadCollection listMultipartUploads(String vaultName);
 }
diff --git a/glacier/src/main/java/org/jclouds/glacier/domain/MultipartUploadMetadata.java b/glacier/src/main/java/org/jclouds/glacier/domain/MultipartUploadMetadata.java
new file mode 100644
index 0000000..1f68d07
--- /dev/null
+++ b/glacier/src/main/java/org/jclouds/glacier/domain/MultipartUploadMetadata.java
@@ -0,0 +1,141 @@
+/*
+ * 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.glacier.domain;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
+import java.util.Date;
+import java.util.Iterator;
+
+import org.jclouds.collect.IterableWithMarker;
+import org.jclouds.glacier.options.PaginationOptions;
+import org.jclouds.javax.annotation.Nullable;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Optional;
+import com.google.common.collect.ComparisonChain;
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * Defines the attributes needed for Multipart uploads. Extends IterableWithMarker to support requesting paginated
+ * multipart upload parts.
+ */
+public class MultipartUploadMetadata extends IterableWithMarker<PartMetadata> implements Comparable<MultipartUploadMetadata> {
+
+   @SerializedName("ArchiveDescription")
+   private final String archiveDescription;
+   @SerializedName("CreationDate")
+   private final Date creationDate;
+   @SerializedName("MultipartUploadId")
+   private final String multipartUploadId;
+   @SerializedName("PartSizeInBytes")
+   private final long partSizeInBytes;
+   @SerializedName("VaultARN")
+   private final String vaultARN;
+   @SerializedName("Parts")
+   private final Iterable<PartMetadata> parts;
+   @SerializedName("Marker")
+   private final String marker;
+
+   @ConstructorProperties({ "ArchiveDescription", "CreationDate", "MultipartUploadId", "PartSizeInBytes", "VaultARN",
+         "Parts", "Marker" })
+   public MultipartUploadMetadata(@Nullable String archiveDescription, Date creationDate, String multipartUploadId,
+         long partSizeInBytes, String vaultARN, @Nullable Iterable<PartMetadata> parts, @Nullable String marker) {
+      super();
+      this.archiveDescription = archiveDescription;
+      this.creationDate = (Date) checkNotNull(creationDate, "creationDate").clone();
+      this.multipartUploadId = checkNotNull(multipartUploadId, "multipartUploadId");
+      this.partSizeInBytes = partSizeInBytes;
+      this.vaultARN = checkNotNull(vaultARN, "vaultARN");
+      this.parts = parts;
+      this.marker = marker;
+   }
+
+   public String getArchiveDescription() {
+      return archiveDescription;
+   }
+
+   public Date getCreationDate() {
+      return (Date) creationDate.clone();
+   }
+
+   public String getMultipartUploadId() {
+      return multipartUploadId;
+   }
+
+   public long getPartSizeInBytes() {
+      return partSizeInBytes;
+   }
+
+   public long getPartSizeInMB() {
+      return partSizeInBytes >> 20;
+   }
+
+   public String getVaultARN() {
+      return vaultARN;
+   }
+
+   @Override
+   public Iterator<PartMetadata> iterator() {
+      return parts == null ? null : parts.iterator();
+   }
+
+   @Override
+   public Optional<Object> nextMarker() {
+      return Optional.<Object>fromNullable(marker);
+   }
+
+   public PaginationOptions nextPaginationOptions() {
+      return PaginationOptions.class.cast(nextMarker().get());
+   }
+
+   @Override
+   public int hashCode() {
+      return Objects.hashCode(this.archiveDescription, this.creationDate, this.multipartUploadId, this.partSizeInBytes,
+            this.vaultARN, this.marker, this.parts);
+   }
+
+   @Override
+   public boolean equals(Object obj) {
+      if (obj == null)
+         return false;
+      if (getClass() != obj.getClass())
+         return false;
+      MultipartUploadMetadata other = (MultipartUploadMetadata) obj;
+
+      return Objects.equal(this.archiveDescription, other.archiveDescription)
+            && Objects.equal(this.creationDate, other.creationDate)
+            && Objects.equal(this.multipartUploadId, other.multipartUploadId)
+            && Objects.equal(this.partSizeInBytes, other.partSizeInBytes)
+            && Objects.equal(this.vaultARN, other.vaultARN)
+            && Objects.equal(this.marker, other.marker)
+            && Objects.equal(this.parts, other.parts);
+   }
+
+   @Override
+   public String toString() {
+      return "MultipartUploadMetadata [archiveDescription=" + archiveDescription + ", creationDate=" + creationDate
+            + ", multipartUploadId=" + multipartUploadId + ", partSizeInBytes=" + partSizeInBytes + ", vaultARN="
+            + vaultARN + ", marker=" + marker + ", parts=" + parts + "]";
+   }
+
+   @Override
+   public int compareTo(MultipartUploadMetadata o) {
+      return ComparisonChain.start().compare(this.creationDate, o.creationDate).result();
+   }
+}
diff --git a/glacier/src/main/java/org/jclouds/glacier/domain/PaginatedMultipartUploadCollection.java b/glacier/src/main/java/org/jclouds/glacier/domain/PaginatedMultipartUploadCollection.java
new file mode 100644
index 0000000..9555e41
--- /dev/null
+++ b/glacier/src/main/java/org/jclouds/glacier/domain/PaginatedMultipartUploadCollection.java
@@ -0,0 +1,60 @@
+/*
+ * 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.glacier.domain;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
+import java.util.Iterator;
+
+import org.jclouds.collect.IterableWithMarker;
+import org.jclouds.glacier.options.PaginationOptions;
+import org.jclouds.javax.annotation.Nullable;
+
+import com.google.common.base.Optional;
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * Paginated collection used to store multipart upload lists.
+ */
+public class PaginatedMultipartUploadCollection extends IterableWithMarker<MultipartUploadMetadata> {
+
+   @SerializedName("UploadsList")
+   private final Iterable<MultipartUploadMetadata> uploads;
+   @SerializedName("Marker")
+   private final String marker;
+
+   @ConstructorProperties({ "UploadsList", "Marker" })
+   public PaginatedMultipartUploadCollection(Iterable<MultipartUploadMetadata> uploads, @Nullable String marker) {
+      this.uploads = checkNotNull(uploads, "uploads");
+      this.marker = marker;
+   }
+
+   @Override
+   public Iterator<MultipartUploadMetadata> iterator() {
+      return uploads.iterator();
+   }
+
+   @Override
+   public Optional<Object> nextMarker() {
+      return Optional.<Object>fromNullable(marker);
+   }
+
+   public PaginationOptions nextPaginationOptions() {
+      return PaginationOptions.class.cast(nextMarker().get());
+   }
+}
diff --git a/glacier/src/main/java/org/jclouds/glacier/domain/PartMetadata.java b/glacier/src/main/java/org/jclouds/glacier/domain/PartMetadata.java
new file mode 100644
index 0000000..3a08447
--- /dev/null
+++ b/glacier/src/main/java/org/jclouds/glacier/domain/PartMetadata.java
@@ -0,0 +1,53 @@
+/*
+ * 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.glacier.domain;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.beans.ConstructorProperties;
+
+import org.jclouds.glacier.util.ContentRange;
+
+import com.google.common.hash.HashCode;
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * Defines the attributes needed for a multipart upload part.
+ */
+public class PartMetadata {
+
+   @SerializedName("SHA256TreeHash")
+   private final HashCode treeHash;
+   @SerializedName("RangeInBytes")
+   private final ContentRange range;
+
+   @ConstructorProperties({ "SHA256TreeHash", "RangeInBytes" })
+   public PartMetadata(String treeHash, String range) {
+      super();
+      this.treeHash = HashCode.fromString(checkNotNull(treeHash, "treeHash"));
+      this.range = ContentRange.fromString(checkNotNull(range, "range"));
+   }
+
+   public ContentRange getRange() {
+      return range;
+   }
+
+   public HashCode getTreeHash() {
+      return treeHash;
+   }
+
+}
diff --git a/glacier/src/main/java/org/jclouds/glacier/functions/ParseMultipartUploadListFromHttpContent.java b/glacier/src/main/java/org/jclouds/glacier/functions/ParseMultipartUploadListFromHttpContent.java
new file mode 100644
index 0000000..c2b5c23
--- /dev/null
+++ b/glacier/src/main/java/org/jclouds/glacier/functions/ParseMultipartUploadListFromHttpContent.java
@@ -0,0 +1,35 @@
+/*
+ * 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.glacier.functions;
+
+import org.jclouds.glacier.domain.PaginatedMultipartUploadCollection;
+import org.jclouds.http.functions.ParseJson;
+import org.jclouds.json.Json;
+
+import com.google.inject.Inject;
+import com.google.inject.TypeLiteral;
+
+/**
+ * Parses the JSON multipart upload list from the HttpResponse.
+ */
+public class ParseMultipartUploadListFromHttpContent extends ParseJson<PaginatedMultipartUploadCollection> {
+
+   @Inject
+   public ParseMultipartUploadListFromHttpContent(Json json) {
+      super(json, TypeLiteral.get(PaginatedMultipartUploadCollection.class));
+   }
+}
diff --git a/glacier/src/main/java/org/jclouds/glacier/functions/ParseMultipartUploadPartListFromHttpContent.java b/glacier/src/main/java/org/jclouds/glacier/functions/ParseMultipartUploadPartListFromHttpContent.java
new file mode 100644
index 0000000..684ac7a
--- /dev/null
+++ b/glacier/src/main/java/org/jclouds/glacier/functions/ParseMultipartUploadPartListFromHttpContent.java
@@ -0,0 +1,35 @@
+/*
+ * 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.glacier.functions;
+
+import org.jclouds.glacier.domain.MultipartUploadMetadata;
+import org.jclouds.http.functions.ParseJson;
+import org.jclouds.json.Json;
+
+import com.google.inject.Inject;
+import com.google.inject.TypeLiteral;
+
+/**
+ * Parses the JSON multipart upload part list from the HttpResponse.
+ */
+public class ParseMultipartUploadPartListFromHttpContent extends ParseJson<MultipartUploadMetadata> {
+
+   @Inject
+   public ParseMultipartUploadPartListFromHttpContent(Json json) {
+      super(json, TypeLiteral.get(MultipartUploadMetadata.class));
+   }
+}
diff --git a/glacier/src/test/java/org/jclouds/glacier/GlacierClientLiveTest.java b/glacier/src/test/java/org/jclouds/glacier/GlacierClientLiveTest.java
index 100d990..a5142b1 100644
--- a/glacier/src/test/java/org/jclouds/glacier/GlacierClientLiveTest.java
+++ b/glacier/src/test/java/org/jclouds/glacier/GlacierClientLiveTest.java
@@ -16,16 +16,25 @@
  */
 package org.jclouds.glacier;
 
+import static org.jclouds.glacier.util.TestUtils.MiB;
+import static org.jclouds.glacier.util.TestUtils.buildPayload;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertNull;
 import static org.testng.Assert.assertTrue;
 
 import java.util.UUID;
 
 import org.jclouds.apis.BaseApiLiveTest;
+import org.jclouds.glacier.domain.MultipartUploadMetadata;
+import org.jclouds.glacier.domain.PaginatedMultipartUploadCollection;
 import org.jclouds.glacier.domain.PaginatedVaultCollection;
 import org.jclouds.glacier.domain.VaultMetadata;
+import org.jclouds.glacier.util.ContentRange;
 import org.testng.annotations.Test;
 
+import com.google.common.collect.ImmutableList;
+
 /**
  * Live test for Glacier.
  */
@@ -68,7 +77,43 @@
       assertTrue(vaults.contains(api.describeVault(VAULT_NAME3)));
    }
 
-   @Test(groups = { "integration", "live" }, dependsOnMethods = { "testListAndDescribeVaults" })
+   @Test(groups = { "integration", "live" }, dependsOnMethods = { "testCreateVault" })
+   public void testListMultipartUploadsWithEmptyList() throws Exception {
+      assertEquals(api.listMultipartUploads(VAULT_NAME1).size(), 0);
+   }
+
+   @Test(groups = { "integration", "live" }, dependsOnMethods = { "testListMultipartUploadsWithEmptyList" })
+   public void testInitiateAndAbortMultipartUpload() throws Exception {
+      String uploadId = api.initiateMultipartUpload(VAULT_NAME1, 8);
+      try {
+         assertNotNull(uploadId);
+      } finally {
+         api.abortMultipartUpload(VAULT_NAME1, uploadId);
+      }
+   }
+
+   @Test(groups = { "integration", "live" }, dependsOnMethods = { "testInitiateAndAbortMultipartUpload" })
+   public void testListMultipartUploads() throws Exception {
+      long partSizeInMb = 1;
+      String uploadId = api.initiateMultipartUpload(VAULT_NAME1, partSizeInMb);
+      try {
+         assertNotNull(api.uploadPart(VAULT_NAME1, uploadId,
+               ContentRange.fromPartNumber(0, partSizeInMb), buildPayload(partSizeInMb * MiB)));
+         PaginatedMultipartUploadCollection uploads = api.listMultipartUploads(VAULT_NAME1);
+         ImmutableList.Builder<String> list = ImmutableList.builder();
+         for (MultipartUploadMetadata upload : uploads) {
+            list.add(upload.getMultipartUploadId());
+         }
+         assertTrue(list.build().contains(uploadId));
+         assertTrue(api.abortMultipartUpload(VAULT_NAME1, uploadId));
+      } finally {
+         api.abortMultipartUpload(VAULT_NAME1, uploadId);
+      }
+   }
+
+   @Test(groups = { "integration", "live" },
+         dependsOnMethods = { "testListAndDescribeVaults", "testListMultipartUploadsWithEmptyList",
+         "testInitiateAndAbortMultipartUpload", "testListMultipartUploads" })
    public void testDeleteVault() throws Exception {
       assertTrue(api.deleteVault(VAULT_NAME1));
       assertTrue(api.deleteVault(VAULT_NAME2));
diff --git a/glacier/src/test/java/org/jclouds/glacier/GlacierClientMockTest.java b/glacier/src/test/java/org/jclouds/glacier/GlacierClientMockTest.java
index 6932b25..d996bdf 100644
--- a/glacier/src/test/java/org/jclouds/glacier/GlacierClientMockTest.java
+++ b/glacier/src/test/java/org/jclouds/glacier/GlacierClientMockTest.java
@@ -37,7 +37,10 @@
 
 import org.jclouds.ContextBuilder;
 import org.jclouds.concurrent.config.ExecutorServiceModule;
+import org.jclouds.glacier.domain.MultipartUploadMetadata;
+import org.jclouds.glacier.domain.PaginatedMultipartUploadCollection;
 import org.jclouds.glacier.domain.PaginatedVaultCollection;
+import org.jclouds.glacier.domain.PartMetadata;
 import org.jclouds.glacier.domain.VaultMetadata;
 import org.jclouds.glacier.options.PaginationOptions;
 import org.jclouds.glacier.reference.GlacierHeaders;
@@ -85,6 +88,7 @@
    private static final String DESCRIPTION = "test description";
    private static final String MULTIPART_UPLOAD_LOCATION = VAULT_LOCATION + "/multipart-uploads/" + ARCHIVE_ID;
    private static final String MULTIPART_UPLOAD_ID = "OW2fM5iVylEpFEMM9_HpKowRapC3vn5sSL39_396UW9zLFUWVrnRHaPjUJddQ5OxSHVXjYtrN47NBZ-khxOjyEXAMPLE";
+   private static final String MARKER = "xsQdFIRsfJr20CW2AbZBKpRZAFTZSJIMtL2hYf8mvp8dM0m4RUzlaqoEye6g3h3ecqB_zqwB7zLDMeSWhwo65re4C4Ev";
    private static final Set<Module> modules = ImmutableSet.<Module> of(new ExecutorServiceModule(sameThreadExecutor(),
          sameThreadExecutor()));
 
@@ -305,4 +309,58 @@
       assertEquals(server.takeRequest().getRequestLine(),
             "DELETE /-/vaults/" + VAULT_NAME + "/multipart-uploads/" + MULTIPART_UPLOAD_ID + " " + HTTP);
    }
+
+   @Test
+   public void testListParts() throws IOException, InterruptedException {
+      MockResponse mr = buildBaseResponse(200);
+      mr.addHeader(HttpHeaders.CONTENT_TYPE, MediaType.JSON_UTF_8);
+      mr.setBody(getResponseBody("/json/listPartsResponseBody.json"));
+      mr.addHeader(HttpHeaders.CONTENT_LENGTH, mr.getBody().length);
+      server.enqueue(mr);
+
+      MultipartUploadMetadata result = client.listParts(VAULT_NAME, MULTIPART_UPLOAD_ID,
+            PaginationOptions.Builder.limit(1).marker("1001"));
+      assertEquals(result.getArchiveDescription(), "archive description 1");
+      assertEquals(result.getMultipartUploadId(), MULTIPART_UPLOAD_ID);
+      assertEquals(result.getPartSizeInBytes(), 4194304);
+      PartMetadata part = result.iterator().next();
+      assertEquals(part.getTreeHash(), HashCode.fromString("01d34dabf7be316472c93b1ef80721f5d4"));
+      assertEquals("4194304-8388607", part.getRange().getFrom() + "-" + part.getRange().getTo());
+      assertEquals(server.takeRequest().getRequestLine(),
+            "GET /-/vaults/examplevault/multipart-uploads/" + MULTIPART_UPLOAD_ID + "?limit=1&marker=1001 " + HTTP);
+   }
+
+   @Test
+   public void testListMultipartUploads() throws IOException, InterruptedException {
+      MockResponse mr = buildBaseResponse(200);
+      mr.addHeader(HttpHeaders.CONTENT_TYPE, MediaType.JSON_UTF_8);
+      mr.setBody(getResponseBody("/json/listMultipartUploadsResponseBody.json"));
+      mr.addHeader(HttpHeaders.CONTENT_LENGTH, mr.getBody().length);
+      server.enqueue(mr);
+
+      PaginatedMultipartUploadCollection result = client.listMultipartUploads(
+            VAULT_NAME, PaginationOptions.Builder.limit(1).marker(MARKER));
+      MultipartUploadMetadata mum = result.iterator().next();
+      assertEquals(mum.getArchiveDescription(), "archive 2");
+      assertEquals(mum.getMultipartUploadId(),
+            "nPyGOnyFcx67qqX7E-0tSGiRi88hHMOwOxR-_jNyM6RjVMFfV29lFqZ3rNsSaWBugg6OP92pRtufeHdQH7ClIpSF6uJc");
+      assertEquals(mum.iterator(), null);
+      assertEquals(mum.getPartSizeInBytes(), 4194304);
+      assertEquals(mum.getVaultARN(), VAULT_ARN);
+      assertEquals(server.takeRequest().getRequestLine(),
+            "GET /-/vaults/examplevault/multipart-uploads?limit=1&marker=" + MARKER + " " + HTTP);
+   }
+
+   @Test
+   public void testListMultipartUploadsWithEmptyList() throws IOException, InterruptedException {
+      MockResponse mr = buildBaseResponse(200);
+      mr.addHeader(HttpHeaders.CONTENT_TYPE, MediaType.JSON_UTF_8);
+      mr.setBody(getResponseBody("/json/listMultipartUploadsWithEmptyListResponseBody.json"));
+      mr.addHeader(HttpHeaders.CONTENT_LENGTH, mr.getBody().length);
+      server.enqueue(mr);
+
+      assertEquals(client.listMultipartUploads(VAULT_NAME, PaginationOptions.Builder.limit(1).marker(MARKER)).size(), 0);
+      assertEquals(server.takeRequest().getRequestLine(),
+            "GET /-/vaults/examplevault/multipart-uploads?limit=1&marker=" + MARKER + " " + HTTP);
+   }
 }
diff --git a/glacier/src/test/resources/json/listMultipartUploadsResponseBody.json b/glacier/src/test/resources/json/listMultipartUploadsResponseBody.json
new file mode 100644
index 0000000..1524578
--- /dev/null
+++ b/glacier/src/test/resources/json/listMultipartUploadsResponseBody.json
@@ -0,0 +1,12 @@
+{
+  "Marker": "qt-RBst_7yO8gVIonIBsAxr2t-db0pE4s8MNeGjKjGdNpuU-cdSAcqG62guwV9r5jh5mLyFPzFEitTpNE7iQfHiu1XoV",
+  "UploadsList" : [
+    {
+      "ArchiveDescription": "archive 2",
+      "CreationDate": "2012-04-01T15:00:00.000Z",
+      "MultipartUploadId": "nPyGOnyFcx67qqX7E-0tSGiRi88hHMOwOxR-_jNyM6RjVMFfV29lFqZ3rNsSaWBugg6OP92pRtufeHdQH7ClIpSF6uJc",
+      "PartSizeInBytes": 4194304,
+      "VaultARN": "arn:aws:glacier:us-east-1:012345678901:vaults/examplevault"
+    }
+  ]
+}
diff --git a/glacier/src/test/resources/json/listMultipartUploadsWithEmptyListResponseBody.json b/glacier/src/test/resources/json/listMultipartUploadsWithEmptyListResponseBody.json
new file mode 100644
index 0000000..0e98a77
--- /dev/null
+++ b/glacier/src/test/resources/json/listMultipartUploadsWithEmptyListResponseBody.json
@@ -0,0 +1,4 @@
+{
+  "Marker": "qt-RBst_7yO8gVIonIBsAxr2t-db0pE4s8MNeGjKjGdNpuU-cdSAcqG62guwV9r5jh5mLyFPzFEitTpNE7iQfHiu1XoV",
+  "UploadsList" : []
+}
diff --git a/glacier/src/test/resources/json/listPartsResponseBody.json b/glacier/src/test/resources/json/listPartsResponseBody.json
new file mode 100644
index 0000000..cab3b85
--- /dev/null
+++ b/glacier/src/test/resources/json/listPartsResponseBody.json
@@ -0,0 +1,13 @@
+{
+  "ArchiveDescription" : "archive description 1",
+  "CreationDate" : "2012-03-20T17:03:43.221Z",
+  "Marker": "MfgsKHVjbQ6EldVl72bn3_n5h2TaGZQUO-Qb3B9j3TITf7WajQ",
+  "MultipartUploadId" : "OW2fM5iVylEpFEMM9_HpKowRapC3vn5sSL39_396UW9zLFUWVrnRHaPjUJddQ5OxSHVXjYtrN47NBZ-khxOjyEXAMPLE",
+  "PartSizeInBytes" : 4194304,
+  "Parts" :
+  [ {
+    "RangeInBytes" : "4194304-8388607",
+    "SHA256TreeHash" : "01d34dabf7be316472c93b1ef80721f5d4"
+    }],
+  "VaultARN" : "arn:aws:glacier:us-east-1:012345678901:vaults/demo1-vault"
+}