mirror of https://github.com/apache/jclouds.git
JCLOUDS-1125: S3 list multipart uploads
This commit is contained in:
parent
61ab28cc50
commit
445664c9f1
|
@ -100,6 +100,11 @@
|
|||
<artifactId>auto-service</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.auto.value</groupId>
|
||||
<artifactId>auto-value</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<profiles>
|
||||
|
|
|
@ -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.ListMultipartUploadsResponse;
|
||||
import org.jclouds.s3.domain.ObjectMetadata;
|
||||
import org.jclouds.s3.domain.Payer;
|
||||
import org.jclouds.s3.domain.S3Object;
|
||||
|
@ -100,6 +101,7 @@ import org.jclouds.s3.xml.CopyObjectHandler;
|
|||
import org.jclouds.s3.xml.DeleteResultHandler;
|
||||
import org.jclouds.s3.xml.ListAllMyBucketsHandler;
|
||||
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.PayerHandler;
|
||||
|
@ -785,4 +787,15 @@ public interface S3Client extends Closeable {
|
|||
Map<Integer, String> listMultipartParts(@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("/")
|
||||
@QueryParams(keys = "uploads")
|
||||
@XMLResponseParser(ListMultipartUploadsHandler.class)
|
||||
ListMultipartUploadsResponse listMultipartUploads(@Bucket @EndpointParam(parser = AssignCorrectHostnameForBucket.class)
|
||||
@BinderParam(BindAsHostPrefixIfConfigured.class) @ParamValidators(BucketNameValidator.class) String bucketName,
|
||||
@QueryParam("delimiter") @Nullable String delimiter, @QueryParam("max-uploads") @Nullable Integer maxUploads,
|
||||
@QueryParam("key-marker") @Nullable String keyMarker, @QueryParam("prefix") @Nullable String prefix,
|
||||
@QueryParam("upload-id-marker") @Nullable String uploadIdMarker);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* 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 java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.jclouds.javax.annotation.Nullable;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
@AutoValue
|
||||
public abstract class ListMultipartUploadsResponse {
|
||||
public abstract String bucket();
|
||||
@Nullable public abstract String keyMarker();
|
||||
@Nullable public abstract String uploadIdMarker();
|
||||
@Nullable public abstract String nextKeyMarker();
|
||||
@Nullable public abstract String nextUploadIdMarker();
|
||||
public abstract int maxUploads();
|
||||
public abstract boolean isTruncated();
|
||||
public abstract List<Upload> uploads();
|
||||
|
||||
public static ListMultipartUploadsResponse create(String bucket, @Nullable String keyMarker, @Nullable String uploadIdMarker, @Nullable String nextKeyMarker, @Nullable String nextUploadIdMarker, int maxUploads, boolean isTruncated, List<Upload> uploads) {
|
||||
uploads = ImmutableList.copyOf(uploads);
|
||||
return new AutoValue_ListMultipartUploadsResponse(bucket, keyMarker, uploadIdMarker, nextKeyMarker, nextUploadIdMarker, maxUploads, isTruncated, uploads);
|
||||
}
|
||||
|
||||
@AutoValue
|
||||
public abstract static class Upload {
|
||||
public abstract String key();
|
||||
public abstract String uploadId();
|
||||
public abstract CanonicalUser initiator();
|
||||
public abstract CanonicalUser owner();
|
||||
public abstract ObjectMetadata.StorageClass storageClass();
|
||||
public abstract Date initiated();
|
||||
|
||||
public static Upload create(String key, String uploadId, CanonicalUser initiator, CanonicalUser owner, ObjectMetadata.StorageClass storageClass, Date initiated) {
|
||||
initiated = (Date) initiated.clone();
|
||||
return new AutoValue_ListMultipartUploadsResponse_Upload(key, uploadId, initiator, owner, storageClass, initiated);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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.s3.xml;
|
||||
|
||||
import static org.jclouds.util.SaxUtils.currentOrNull;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.jclouds.date.DateService;
|
||||
import org.jclouds.http.functions.ParseSax;
|
||||
import org.jclouds.s3.domain.CanonicalUser;
|
||||
import org.jclouds.s3.domain.ListMultipartUploadsResponse;
|
||||
import org.jclouds.s3.domain.ObjectMetadata;
|
||||
import org.xml.sax.Attributes;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
public final class ListMultipartUploadsHandler extends ParseSax.HandlerWithResult<ListMultipartUploadsResponse> {
|
||||
private String bucket;
|
||||
private String keyMarker;
|
||||
private String uploadIdMarker;
|
||||
private String nextKeyMarker;
|
||||
private String nextUploadIdMarker;
|
||||
private int maxUploads;
|
||||
private boolean isTruncated;
|
||||
private final ImmutableList.Builder<ListMultipartUploadsResponse.Upload> uploads = ImmutableList.builder();
|
||||
|
||||
private String key;
|
||||
private String uploadId;
|
||||
private String id;
|
||||
private String displayName;
|
||||
private CanonicalUser initiator;
|
||||
private CanonicalUser owner;
|
||||
private ObjectMetadata.StorageClass storageClass;
|
||||
private Date initiated;
|
||||
|
||||
private final DateService dateParser;
|
||||
private final StringBuilder currentText = new StringBuilder();
|
||||
private boolean inUpload;
|
||||
private boolean inInitiator;
|
||||
private boolean inOwner;
|
||||
|
||||
@Inject
|
||||
public ListMultipartUploadsHandler(DateService dateParser) {
|
||||
this.dateParser = dateParser;
|
||||
}
|
||||
|
||||
public ListMultipartUploadsResponse getResult() {
|
||||
return ListMultipartUploadsResponse.create(bucket, keyMarker, uploadIdMarker, nextKeyMarker, nextUploadIdMarker, maxUploads, isTruncated, uploads.build());
|
||||
}
|
||||
|
||||
public void startElement(String uri, String name, String qName, Attributes attrs) {
|
||||
if (qName.equals("Upload")) {
|
||||
inUpload = true;
|
||||
} else if (qName.equals("Initiator")) {
|
||||
inInitiator = true;
|
||||
} else if (qName.equals("Owner")) {
|
||||
inOwner = true;
|
||||
}
|
||||
currentText.setLength(0);
|
||||
}
|
||||
|
||||
public void endElement(String uri, String name, String qName) {
|
||||
if (qName.equals("Bucket")) {
|
||||
bucket = currentOrNull(currentText);
|
||||
} else if (qName.equals("KeyMarker")) {
|
||||
keyMarker = currentOrNull(currentText);
|
||||
} else if (qName.equals("UploadIdMarker")) {
|
||||
uploadIdMarker = currentOrNull(currentText);
|
||||
} else if (qName.equals("NextKeyMarker")) {
|
||||
nextKeyMarker = currentOrNull(currentText);
|
||||
} else if (qName.equals("NextUploadIdMarker")) {
|
||||
nextUploadIdMarker = currentOrNull(currentText);
|
||||
} else if (qName.equals("MaxUploads")) {
|
||||
maxUploads = Integer.parseInt(currentOrNull(currentText));
|
||||
} else if (qName.equals("IsTruncated")) {
|
||||
isTruncated = Boolean.parseBoolean(currentOrNull(currentText));
|
||||
} else if (qName.equals("Key")) {
|
||||
key = currentOrNull(currentText);
|
||||
} else if (qName.equals("UploadId")) {
|
||||
uploadId = currentOrNull(currentText);
|
||||
} else if (qName.equals("StorageClass")) {
|
||||
storageClass = ObjectMetadata.StorageClass.valueOf(currentOrNull(currentText));
|
||||
} else if (qName.equals("Initiated")) {
|
||||
initiated = dateParser.iso8601DateOrSecondsDateParse(currentOrNull(currentText));
|
||||
} else if (qName.equals("Upload")) {
|
||||
uploads.add(ListMultipartUploadsResponse.Upload.create(key, uploadId, initiator, owner, storageClass, initiated));
|
||||
key = null;
|
||||
uploadId = null;
|
||||
id = null;
|
||||
displayName = null;
|
||||
initiator = null;
|
||||
owner = null;
|
||||
storageClass = null;
|
||||
initiated = null;
|
||||
inUpload = false;
|
||||
} else if (qName.equals("Initiator")) {
|
||||
initiator = new CanonicalUser(id, displayName);
|
||||
id = null;
|
||||
displayName = null;
|
||||
inInitiator = false;
|
||||
} else if (qName.equals("Owner")) {
|
||||
owner = new CanonicalUser(id, displayName);
|
||||
id = null;
|
||||
displayName = null;
|
||||
inOwner = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void characters(char ch[], int start, int length) {
|
||||
currentText.append(ch, start, length);
|
||||
}
|
||||
}
|
|
@ -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.ListMultipartUploadsResponse;
|
||||
import org.jclouds.s3.domain.ObjectMetadata;
|
||||
import org.jclouds.s3.domain.ObjectMetadataBuilder;
|
||||
import org.jclouds.s3.domain.S3Object;
|
||||
|
@ -566,6 +567,35 @@ public class S3ClientLiveTest extends BaseBlobStoreIntegrationTest {
|
|||
}
|
||||
}
|
||||
|
||||
public void testListMultipartUploads() throws Exception {
|
||||
String containerName = getContainerName();
|
||||
String key = "testListMultipartUploads";
|
||||
String uploadId = null;
|
||||
try {
|
||||
ListMultipartUploadsResponse response = getApi().listMultipartUploads(containerName, null, null, null, null, null);
|
||||
assertThat(response.bucket()).isEqualTo(containerName);
|
||||
assertThat(response.isTruncated()).isFalse();
|
||||
assertThat(response.uploads()).isEmpty();
|
||||
|
||||
uploadId = getApi().initiateMultipartUpload(containerName, ObjectMetadataBuilder.create().key(key).build());
|
||||
|
||||
response = getApi().listMultipartUploads(containerName, null, null, null, null, null);
|
||||
assertThat(response.bucket()).isEqualTo(containerName);
|
||||
assertThat(response.isTruncated()).isFalse();
|
||||
assertThat(response.uploads()).hasSize(1);
|
||||
|
||||
ListMultipartUploadsResponse.Upload upload = response.uploads().get(0);
|
||||
assertThat(upload.key()).isEqualTo(key);
|
||||
assertThat(upload.uploadId()).isEqualTo(uploadId);
|
||||
assertThat(upload.storageClass()).isEqualTo(ObjectMetadata.StorageClass.STANDARD);
|
||||
} finally {
|
||||
if (uploadId != null) {
|
||||
getApi().abortMultipartUpload(containerName, key, uploadId);
|
||||
}
|
||||
returnContainer(containerName);
|
||||
}
|
||||
}
|
||||
|
||||
public void testDeleteMultipleObjects() throws InterruptedException {
|
||||
String container = getContainerName();
|
||||
try {
|
||||
|
|
|
@ -20,6 +20,7 @@ import static org.jclouds.reflect.Reflection2.method;
|
|||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
|
||||
import org.jclouds.Fallbacks.VoidOnNotFoundOr404;
|
||||
|
@ -68,6 +69,7 @@ import org.jclouds.s3.xml.BucketLoggingHandler;
|
|||
import org.jclouds.s3.xml.CopyObjectHandler;
|
||||
import org.jclouds.s3.xml.ListAllMyBucketsHandler;
|
||||
import org.jclouds.s3.xml.ListBucketHandler;
|
||||
import org.jclouds.s3.xml.ListMultipartUploadsHandler;
|
||||
import org.jclouds.s3.xml.LocationConstraintHandler;
|
||||
import org.jclouds.s3.xml.PayerHandler;
|
||||
import org.jclouds.util.Strings2;
|
||||
|
@ -625,6 +627,22 @@ public abstract class S3ClientTest<T extends S3Client> extends BaseS3ClientTest<
|
|||
checkFilters(request);
|
||||
}
|
||||
|
||||
public void testListMultipartUploads() throws Exception {
|
||||
Invokable<?, ?> method = method(S3Client.class, "listMultipartUploads", String.class, String.class,
|
||||
Integer.class, String.class, String.class, String.class);
|
||||
GeneratedHttpRequest request = processor.createRequest(method, Arrays.<Object> asList("bucket", null, null, null, null, null));
|
||||
|
||||
assertRequestLineEquals(request, "GET https://bucket." + url + "/?uploads HTTP/1.1");
|
||||
assertNonPayloadHeadersEqual(request, "Host: bucket." + url + "\n");
|
||||
assertPayloadEquals(request, null, "application/unknown", false);
|
||||
|
||||
assertResponseParserClassEquals(method, request, ParseSax.class);
|
||||
assertSaxResponseParserClassEquals(method, ListMultipartUploadsHandler.class);
|
||||
assertFallbackClassEquals(method, MapHttp4xxCodesToExceptions.class);
|
||||
|
||||
checkFilters(request);
|
||||
}
|
||||
|
||||
@ConfiguresHttpApi
|
||||
private static final class TestS3HttpApiModule extends S3HttpApiModule<S3Client> {
|
||||
|
||||
|
|
Loading…
Reference in New Issue