From d2041667d770bcb865a7bfb451b5910dd9084d91 Mon Sep 17 00:00:00 2001 From: Andrew Gaul Date: Thu, 16 Feb 2017 18:52:44 -0800 Subject: [PATCH] Return lastModified and size when listing S3 parts --- .../main/java/org/jclouds/s3/S3Client.java | 14 ++++ .../domain/ListMultipartUploadResponse.java | 42 ++++++++++ .../s3/xml/PartIdsFromHttpResponse.java | 3 + .../s3/xml/PartIdsFromHttpResponseFull.java | 79 +++++++++++++++++++ .../java/org/jclouds/s3/S3ClientLiveTest.java | 7 +- 5 files changed, 143 insertions(+), 2 deletions(-) create mode 100644 apis/s3/src/main/java/org/jclouds/s3/domain/ListMultipartUploadResponse.java create mode 100644 apis/s3/src/main/java/org/jclouds/s3/xml/PartIdsFromHttpResponseFull.java diff --git a/apis/s3/src/main/java/org/jclouds/s3/S3Client.java b/apis/s3/src/main/java/org/jclouds/s3/S3Client.java index 7f80f3b71a..35fce21df4 100644 --- a/apis/s3/src/main/java/org/jclouds/s3/S3Client.java +++ b/apis/s3/src/main/java/org/jclouds/s3/S3Client.java @@ -75,6 +75,7 @@ import org.jclouds.s3.domain.BucketMetadata; import org.jclouds.s3.domain.CannedAccessPolicy; import org.jclouds.s3.domain.DeleteResult; import org.jclouds.s3.domain.ListBucketResponse; +import org.jclouds.s3.domain.ListMultipartUploadResponse; import org.jclouds.s3.domain.ListMultipartUploadsResponse; import org.jclouds.s3.domain.ObjectMetadata; import org.jclouds.s3.domain.Payer; @@ -104,8 +105,10 @@ import org.jclouds.s3.xml.ListBucketHandler; import org.jclouds.s3.xml.ListMultipartUploadsHandler; import org.jclouds.s3.xml.LocationConstraintHandler; import org.jclouds.s3.xml.PartIdsFromHttpResponse; +import org.jclouds.s3.xml.PartIdsFromHttpResponseFull; import org.jclouds.s3.xml.PayerHandler; +import com.google.common.annotations.Beta; import com.google.inject.Provides; /** @@ -780,6 +783,8 @@ public interface S3Client extends Closeable { @PathParam("key") String key, @QueryParam("uploadId") String uploadId, @BinderParam(BindPartIdsAndETagsToRequest.class) Map parts); + /** @deprecated see #listMultipartPartsFull */ + @Deprecated @Named("ListMultipartParts") @GET @Path("/{key}") @@ -788,6 +793,15 @@ public interface S3Client extends Closeable { @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators(BucketNameValidator.class) String bucketName, @PathParam("key") String key, @QueryParam("uploadId") String uploadId); + @Beta + @Named("ListMultipartParts") + @GET + @Path("/{key}") + @XMLResponseParser(PartIdsFromHttpResponseFull.class) + Map listMultipartPartsFull(@Bucket @EndpointParam(parser = AssignCorrectHostnameForBucket.class) + @BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators(BucketNameValidator.class) String bucketName, + @PathParam("key") String key, @QueryParam("uploadId") String uploadId); + @Named("ListMultipartUploads") @GET @Path("/") diff --git a/apis/s3/src/main/java/org/jclouds/s3/domain/ListMultipartUploadResponse.java b/apis/s3/src/main/java/org/jclouds/s3/domain/ListMultipartUploadResponse.java new file mode 100644 index 0000000000..3c34e3e399 --- /dev/null +++ b/apis/s3/src/main/java/org/jclouds/s3/domain/ListMultipartUploadResponse.java @@ -0,0 +1,42 @@ +/* + * 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.s3.domain; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.Date; + +import com.google.auto.value.AutoValue; +import com.google.common.annotations.Beta; + +@AutoValue +@Beta +public abstract class ListMultipartUploadResponse { + public abstract int partNumber(); + public abstract Date lastModified(); + public abstract String eTag(); + public abstract long size(); + + public static ListMultipartUploadResponse create(int partNumber, Date lastModified, String eTag, long size) { + checkArgument(partNumber > 0, "partNumber must be greater than zero, was: %s", partNumber); + checkNotNull(eTag, "eTag"); + lastModified = (Date) checkNotNull(lastModified, "lastModified").clone(); + checkArgument(size >= 0, "size must be positive, was: %s", size); + return new AutoValue_ListMultipartUploadResponse(partNumber, lastModified, eTag, size); + } +} diff --git a/apis/s3/src/main/java/org/jclouds/s3/xml/PartIdsFromHttpResponse.java b/apis/s3/src/main/java/org/jclouds/s3/xml/PartIdsFromHttpResponse.java index add073cbe4..fa8ac81932 100644 --- a/apis/s3/src/main/java/org/jclouds/s3/xml/PartIdsFromHttpResponse.java +++ b/apis/s3/src/main/java/org/jclouds/s3/xml/PartIdsFromHttpResponse.java @@ -33,7 +33,10 @@ import com.google.common.collect.ImmutableMap; * Parses the following XML document: *

* ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01" + * + * @deprecated see PartIdsFromHttpResponseFull */ +@Deprecated public class PartIdsFromHttpResponse extends ParseSax.HandlerWithResult> { private final StringBuilder currentText = new StringBuilder(); diff --git a/apis/s3/src/main/java/org/jclouds/s3/xml/PartIdsFromHttpResponseFull.java b/apis/s3/src/main/java/org/jclouds/s3/xml/PartIdsFromHttpResponseFull.java new file mode 100644 index 0000000000..471a362241 --- /dev/null +++ b/apis/s3/src/main/java/org/jclouds/s3/xml/PartIdsFromHttpResponseFull.java @@ -0,0 +1,79 @@ +/* + * 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.s3.xml; + +import static org.jclouds.util.SaxUtils.currentOrNull; + +import java.util.Date; +import java.util.Map; + +import javax.inject.Inject; + +import org.jclouds.date.DateService; +import org.jclouds.http.functions.ParseSax; +import org.jclouds.s3.domain.ListMultipartUploadResponse; + +import com.google.common.annotations.Beta; +import com.google.common.collect.ImmutableMap; + +/** + * Parses the following XML document: + *

+ * ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01" + */ +@Beta +public final class PartIdsFromHttpResponseFull extends ParseSax.HandlerWithResult> { + private final StringBuilder currentText = new StringBuilder(); + + private final DateService dateParser; + + private int partNumber; + private Date lastModfied; + private String eTag; + private long size; + + private final ImmutableMap.Builder parts = ImmutableMap.builder(); + + @Inject + PartIdsFromHttpResponseFull(DateService dateParser) { + this.dateParser = dateParser; + } + + public Map getResult() { + return parts.build(); + } + + public void endElement(String uri, String name, String qName) { + if (qName.equals("PartNumber")) { + partNumber = Integer.parseInt(currentText.toString().trim()); + } else if (qName.equals("LastModified")) { + lastModfied = dateParser.iso8601DateOrSecondsDateParse(currentOrNull(currentText)); + } else if (qName.equals("ETag")) { + eTag = currentText.toString().trim(); + } else if (qName.equals("Size")) { + size = Long.parseLong(currentText.toString().trim()); + } else if (qName.equals("Part")) { + parts.put(partNumber, ListMultipartUploadResponse.create(partNumber, lastModfied, eTag, size)); + } + currentText.setLength(0); + } + + public void characters(char[] ch, int start, int length) { + currentText.append(ch, start, length); + } +} + diff --git a/apis/s3/src/test/java/org/jclouds/s3/S3ClientLiveTest.java b/apis/s3/src/test/java/org/jclouds/s3/S3ClientLiveTest.java index 73a9eff6f6..ede55513a2 100644 --- a/apis/s3/src/test/java/org/jclouds/s3/S3ClientLiveTest.java +++ b/apis/s3/src/test/java/org/jclouds/s3/S3ClientLiveTest.java @@ -56,6 +56,7 @@ import org.jclouds.s3.domain.AccessControlList.GroupGranteeURI; import org.jclouds.s3.domain.AccessControlList.Permission; import org.jclouds.s3.domain.CannedAccessPolicy; import org.jclouds.s3.domain.DeleteResult; +import org.jclouds.s3.domain.ListMultipartUploadResponse; import org.jclouds.s3.domain.ListMultipartUploadsResponse; import org.jclouds.s3.domain.ObjectMetadata; import org.jclouds.s3.domain.ObjectMetadataBuilder; @@ -514,7 +515,7 @@ public class S3ClientLiveTest extends BaseBlobStoreIntegrationTest { String key = "constitution.txt"; String uploadId = getApi().initiateMultipartUpload(containerName, ObjectMetadataBuilder.create().key(key).contentMD5(oneHundredOneConstitutionsMD5.asBytes()).build()); - assertThat(getApi().listMultipartParts(containerName, key, uploadId)).isEmpty(); + assertThat(getApi().listMultipartPartsFull(containerName, key, uploadId)).isEmpty(); byte[] buffer = oneHundredOneConstitutions.read(); assertEquals(oneHundredOneConstitutions.size(), (long) buffer.length); @@ -534,7 +535,9 @@ public class S3ClientLiveTest extends BaseBlobStoreIntegrationTest { // available there. eTagOf1 = getApi().uploadPart(containerName, key, 1, uploadId, part1); } - assertThat(getApi().listMultipartParts(containerName, key, uploadId)).containsOnlyKeys(1); + Map map = getApi().listMultipartPartsFull(containerName, key, uploadId); + assertThat(map).containsOnlyKeys(1); + assertThat(map.get(1).eTag()).isEqualTo(eTagOf1); getApi().completeMultipartUpload(containerName, key, uploadId, ImmutableMap.of(1, eTagOf1));