mirror of https://github.com/apache/jclouds.git
JCLOUDS-457: Added initiateMultipartUpload and uploadPart operations.
Now the Glacier client supports initiateMultipartUpload and uploadPart operations.
This commit is contained in:
parent
2efd8183ce
commit
248c855e48
|
@ -31,20 +31,26 @@ import javax.ws.rs.PathParam;
|
|||
|
||||
import org.jclouds.Fallbacks.NullOnNotFoundOr404;
|
||||
import org.jclouds.blobstore.attr.BlobScope;
|
||||
import org.jclouds.glacier.binders.BindContentRangeToHeaders;
|
||||
import org.jclouds.glacier.binders.BindDescriptionToHeaders;
|
||||
import org.jclouds.glacier.binders.BindHashesToHeaders;
|
||||
import org.jclouds.glacier.binders.BindPartSizeToHeaders;
|
||||
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.ParseMultipartUploadTreeHashHeader;
|
||||
import org.jclouds.glacier.functions.ParseVaultMetadataFromHttpContent;
|
||||
import org.jclouds.glacier.functions.ParseVaultMetadataListFromHttpContent;
|
||||
import org.jclouds.glacier.options.PaginationOptions;
|
||||
import org.jclouds.glacier.predicates.validators.DescriptionValidator;
|
||||
import org.jclouds.glacier.predicates.validators.PartSizeValidator;
|
||||
import org.jclouds.glacier.predicates.validators.PayloadValidator;
|
||||
import org.jclouds.glacier.predicates.validators.VaultNameValidator;
|
||||
import org.jclouds.glacier.reference.GlacierHeaders;
|
||||
import org.jclouds.glacier.util.ContentRange;
|
||||
import org.jclouds.io.Payload;
|
||||
import org.jclouds.rest.annotations.BinderParam;
|
||||
import org.jclouds.rest.annotations.Fallback;
|
||||
|
@ -146,4 +152,38 @@ public interface GlacierAsyncClient extends Closeable {
|
|||
@PathParam("vault") String vaultName,
|
||||
@PathParam("archive") String archiveId);
|
||||
|
||||
/**
|
||||
* @see GlacierClient#initiateMultipartUpload
|
||||
*/
|
||||
@Named("InitiateMultipartUpload")
|
||||
@POST
|
||||
@Path("/-/vaults/{vault}/multipart-uploads")
|
||||
@ResponseParser(ParseMultipartUploadIdHeader.class)
|
||||
ListenableFuture<String> initiateMultipartUpload(
|
||||
@ParamValidators(VaultNameValidator.class) @PathParam("vault") String vaultName,
|
||||
@ParamValidators(PartSizeValidator.class) @BinderParam(BindPartSizeToHeaders.class) long partSizeInMB,
|
||||
@ParamValidators(DescriptionValidator.class) @BinderParam(BindDescriptionToHeaders.class) String description);
|
||||
|
||||
/**
|
||||
* @see GlacierClient#initiateMultipartUpload
|
||||
*/
|
||||
@Named("InitiateMultipartUpload")
|
||||
@POST
|
||||
@Path("/-/vaults/{vault}/multipart-uploads")
|
||||
@ResponseParser(ParseMultipartUploadIdHeader.class)
|
||||
ListenableFuture<String> initiateMultipartUpload(
|
||||
@ParamValidators(VaultNameValidator.class) @PathParam("vault") String vaultName,
|
||||
@ParamValidators(PartSizeValidator.class) @BinderParam(BindPartSizeToHeaders.class) long partSizeInMB);
|
||||
|
||||
/**
|
||||
* @see GlacierClient#uploadPart
|
||||
*/
|
||||
@Named("UploadPart")
|
||||
@PUT
|
||||
@Path("/-/vaults/{vault}/multipart-uploads/{uploadId}")
|
||||
@ResponseParser(ParseMultipartUploadTreeHashHeader.class)
|
||||
ListenableFuture<String> uploadPart(@PathParam("vault") String vaultName,
|
||||
@PathParam("uploadId") String uploadId,
|
||||
@BinderParam(BindContentRangeToHeaders.class) ContentRange range,
|
||||
@ParamValidators(PayloadValidator.class) @BinderParam(BindHashesToHeaders.class) Payload payload);
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ import java.net.URI;
|
|||
import org.jclouds.glacier.domain.PaginatedVaultCollection;
|
||||
import org.jclouds.glacier.domain.VaultMetadata;
|
||||
import org.jclouds.glacier.options.PaginationOptions;
|
||||
import org.jclouds.glacier.util.ContentRange;
|
||||
import org.jclouds.io.Payload;
|
||||
|
||||
/**
|
||||
|
@ -112,4 +113,40 @@ public interface GlacierClient extends Closeable {
|
|||
* @see <a href="http://docs.aws.amazon.com/amazonglacier/latest/dev/api-archive-delete.html" />
|
||||
*/
|
||||
boolean deleteArchive(String vaultName, String archiveId);
|
||||
|
||||
/**
|
||||
* Starts a new multipart upload.
|
||||
*
|
||||
* @param vaultName
|
||||
* Name of the Vault where the archive is going to be stored.
|
||||
* @param partSizeInMB
|
||||
* Content size for each part.
|
||||
* @param description
|
||||
* The archive description.
|
||||
* @return The Multipart Upload Id.
|
||||
* @see <a href="http://docs.aws.amazon.com/amazonglacier/latest/dev/api-multipart-initiate-upload.html" />
|
||||
*/
|
||||
String initiateMultipartUpload(String vaultName, long partSizeInMB, String description);
|
||||
|
||||
/**
|
||||
* Starts a new multipart upload.
|
||||
*/
|
||||
String initiateMultipartUpload(String vaultName, long partSizeInMB);
|
||||
|
||||
/**
|
||||
* Uploads one of the multipart upload parts.
|
||||
*
|
||||
* @param vaultName
|
||||
* Name of the Vault where the archive is going to be stored.
|
||||
* @param uploadId
|
||||
* Multipart upload identifier.
|
||||
* @param range
|
||||
* The content range that this part is uploading.
|
||||
* @param payload
|
||||
* Content for this part.
|
||||
* @return Tree-hash of the payload calculated by Amazon. This hash needs to be stored to complete the multipart
|
||||
* upload.
|
||||
* @see <a href="http://docs.aws.amazon.com/amazonglacier/latest/dev/api-upload-part.html" />
|
||||
*/
|
||||
String uploadPart(String vaultName, String uploadId, ContentRange range, Payload payload);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* 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.binders;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import org.jclouds.glacier.util.ContentRange;
|
||||
import org.jclouds.http.HttpRequest;
|
||||
import org.jclouds.rest.Binder;
|
||||
|
||||
import com.google.common.net.HttpHeaders;
|
||||
|
||||
/**
|
||||
* Binds the ContentRange to the request headers.
|
||||
*/
|
||||
public class BindContentRangeToHeaders implements Binder {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <R extends HttpRequest> R bindToRequest(R request, Object input) {
|
||||
checkArgument(checkNotNull(input, "input") instanceof ContentRange, "This binder is only valid for Payload");
|
||||
checkNotNull(request, "request");
|
||||
ContentRange range = ContentRange.class.cast(input);
|
||||
return (R) request.toBuilder().addHeader(HttpHeaders.CONTENT_RANGE, range.buildHeader()).build();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* 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.binders;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import org.jclouds.glacier.reference.GlacierHeaders;
|
||||
import org.jclouds.http.HttpRequest;
|
||||
import org.jclouds.rest.Binder;
|
||||
|
||||
/**
|
||||
* Binds the Part size to the request headers.
|
||||
*/
|
||||
public class BindPartSizeToHeaders implements Binder {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <R extends HttpRequest> R bindToRequest(R request, Object input) {
|
||||
checkArgument(checkNotNull(input, "input") instanceof Long, "This binder is only valid for long");
|
||||
checkNotNull(request, "request");
|
||||
Long partSizeInMB = Long.class.cast(input);
|
||||
return (R) request.toBuilder()
|
||||
.replaceHeader(GlacierHeaders.PART_SIZE, Long.toString(partSizeInMB << 20))
|
||||
.build();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foimport org.jclouds.http.HttpException;
|
||||
import org.jclouds.http.HttpResponse;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
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.reference.GlacierHeaders;
|
||||
import org.jclouds.http.HttpException;
|
||||
import org.jclouds.http.HttpResponse;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
|
||||
/**
|
||||
* Parses the multipart upload id from the HttpResponse.
|
||||
*/
|
||||
public class ParseMultipartUploadIdHeader implements Function<HttpResponse, String> {
|
||||
@Override
|
||||
public String apply(HttpResponse from) {
|
||||
String id = from.getFirstHeaderOrNull(GlacierHeaders.MULTIPART_UPLOAD_ID);
|
||||
if (id == null)
|
||||
throw new HttpException("Did not receive Multipart upload Id");
|
||||
return id;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foimport org.jclouds.http.HttpException;
|
||||
import org.jclouds.http.HttpResponse;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
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.reference.GlacierHeaders;
|
||||
import org.jclouds.http.HttpException;
|
||||
import org.jclouds.http.HttpResponse;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
|
||||
/**
|
||||
* Parses the tree hash header from the HttpResponse.
|
||||
*/
|
||||
public class ParseMultipartUploadTreeHashHeader implements Function<HttpResponse, String> {
|
||||
@Override
|
||||
public String apply(HttpResponse from) {
|
||||
String id = from.getFirstHeaderOrNull(GlacierHeaders.TREE_HASH);
|
||||
if (id == null)
|
||||
throw new HttpException("Did not receive Tree hash");
|
||||
return id;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* 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.predicates.validators;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import org.jclouds.predicates.Validator;
|
||||
|
||||
/**
|
||||
* Validates the part size parameter used when initiating multipart uploads.
|
||||
*/
|
||||
public final class PartSizeValidator extends Validator<Long> {
|
||||
private static final int MIN_PART_SIZE = 1;
|
||||
private static final int MAX_PART_SIZE = 4096;
|
||||
|
||||
@Override
|
||||
public void validate(Long partSizeInMB) throws IllegalArgumentException {
|
||||
checkNotNull(partSizeInMB, "partSizeInMB");
|
||||
if (partSizeInMB < MIN_PART_SIZE || partSizeInMB > MAX_PART_SIZE || (partSizeInMB & (partSizeInMB - 1)) != 0)
|
||||
throw exception(partSizeInMB, "partSizeInMB must be a power of 2 between 1 and 4096.");
|
||||
}
|
||||
|
||||
protected static IllegalArgumentException exception(Long size, String reason) {
|
||||
return new IllegalArgumentException(
|
||||
String.format(
|
||||
"Part size '%s' doesn't match Glacier Multipart upload rules. "
|
||||
+ "Reason: %s. For more info, please refer to http://http://docs.aws.amazon.com/amazonglacier/latest/dev/api-multipart-initiate-upload.html.",
|
||||
size, reason));
|
||||
}
|
||||
}
|
|
@ -30,6 +30,8 @@ public final class GlacierHeaders {
|
|||
public static final String LINEAR_HASH = HEADER_PREFIX + "content-sha256";
|
||||
public static final String TREE_HASH = HEADER_PREFIX + "sha256-tree-hash";
|
||||
public static final String ARCHIVE_ID = HEADER_PREFIX + "archive-id";
|
||||
public static final String MULTIPART_UPLOAD_ID = HEADER_PREFIX + "multipart-upload-id";
|
||||
public static final String PART_SIZE = HEADER_PREFIX + "part-size";
|
||||
|
||||
private GlacierHeaders() {
|
||||
}
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* 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.util;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Strings.isNullOrEmpty;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
|
||||
/**
|
||||
* This class represents a range of bytes.
|
||||
*/
|
||||
public final class ContentRange {
|
||||
private final long from;
|
||||
private final long to;
|
||||
|
||||
private ContentRange(long from, long to) {
|
||||
checkArgument(from < to, "\"from\" should be lower than \"to\"");
|
||||
checkArgument(from >= 0 && to > 0, "\"from\" cannot be negative and \"to\" has to be positive");
|
||||
this.from = from;
|
||||
this.to = to;
|
||||
}
|
||||
|
||||
public static ContentRange fromString(String contentRangeString) {
|
||||
checkArgument(!isNullOrEmpty(contentRangeString) && contentRangeString.matches("[0-9]+-[0-9]+"),
|
||||
"The string should be two numbers separated by a hyphen (from-to)");
|
||||
String[] strings = contentRangeString.split("-", 2);
|
||||
long from = Long.parseLong(strings[0]);
|
||||
long to = Long.parseLong(strings[1]);
|
||||
return new ContentRange(from, to);
|
||||
}
|
||||
|
||||
public static ContentRange fromPartNumber(long partNumber, long partSizeInMB) {
|
||||
checkArgument(partNumber >= 0, "The part number cannot be negative");
|
||||
checkArgument(partSizeInMB > 0, "The part size has to be positive");
|
||||
long from = partNumber * (partSizeInMB << 20);
|
||||
long to = from + (partSizeInMB << 20) - 1;
|
||||
return new ContentRange(from, to);
|
||||
}
|
||||
|
||||
public static ContentRange build(long from, long to) {
|
||||
return new ContentRange(from, to);
|
||||
}
|
||||
|
||||
public long getFrom() {
|
||||
return from;
|
||||
}
|
||||
|
||||
public long getTo() {
|
||||
return to;
|
||||
}
|
||||
|
||||
public String buildHeader() {
|
||||
return "bytes " + from + "-" + to + "/*";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(this.from, this.to);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
ContentRange other = (ContentRange) obj;
|
||||
return Objects.equal(this.from, other.to) && Objects.equal(this.from, other.to);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return from + "-" + to;
|
||||
}
|
||||
}
|
|
@ -20,6 +20,7 @@ import static com.google.common.base.Charsets.UTF_8;
|
|||
import static com.google.common.util.concurrent.MoreExecutors.sameThreadExecutor;
|
||||
import static org.jclouds.Constants.PROPERTY_MAX_RETRIES;
|
||||
import static org.jclouds.Constants.PROPERTY_SO_TIMEOUT;
|
||||
import static org.jclouds.glacier.util.TestUtils.MiB;
|
||||
import static org.jclouds.glacier.util.TestUtils.buildPayload;
|
||||
import static org.jclouds.util.Strings2.urlEncode;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
@ -40,6 +41,10 @@ import org.jclouds.glacier.domain.PaginatedVaultCollection;
|
|||
import org.jclouds.glacier.domain.VaultMetadata;
|
||||
import org.jclouds.glacier.options.PaginationOptions;
|
||||
import org.jclouds.glacier.reference.GlacierHeaders;
|
||||
import org.jclouds.glacier.util.ContentRange;
|
||||
import org.jclouds.http.HttpResponseException;
|
||||
import org.jclouds.io.Payload;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.AfterTest;
|
||||
import org.testng.annotations.BeforeTest;
|
||||
import org.testng.annotations.Test;
|
||||
|
@ -76,6 +81,8 @@ public class GlacierClientMockTest {
|
|||
private static final String ARCHIVE_LOCATION = VAULT_LOCATION + "/archives/" + ARCHIVE_ID;
|
||||
private static final String TREEHASH = "beb0fe31a1c7ca8c6c04d574ea906e3f97b31fdca7571defb5b44dca89b5af60";
|
||||
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 Set<Module> modules = ImmutableSet.<Module> of(new ExecutorServiceModule(sameThreadExecutor(),
|
||||
sameThreadExecutor()));
|
||||
|
||||
|
@ -106,6 +113,7 @@ public class GlacierClientMockTest {
|
|||
@BeforeTest
|
||||
private void initServer() throws IOException {
|
||||
server = new MockWebServer();
|
||||
server.setBodyLimit(0);
|
||||
server.play();
|
||||
client = getGlacierClient(server.getUrl("/"));
|
||||
}
|
||||
|
@ -208,4 +216,59 @@ public class GlacierClientMockTest {
|
|||
assertTrue(client.deleteArchive(VAULT_NAME, ARCHIVE_ID));
|
||||
assertEquals(server.takeRequest().getRequestLine(), "DELETE /-/vaults/" + VAULT_NAME + "/archives/" + ARCHIVE_ID + " " + HTTP);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitiateMultipartUpload() throws InterruptedException {
|
||||
MockResponse mr = buildBaseResponse(201);
|
||||
mr.addHeader(HttpHeaders.LOCATION, MULTIPART_UPLOAD_LOCATION);
|
||||
mr.addHeader(GlacierHeaders.MULTIPART_UPLOAD_ID, MULTIPART_UPLOAD_ID);
|
||||
server.enqueue(mr);
|
||||
|
||||
assertEquals(client.initiateMultipartUpload(VAULT_NAME, 4, DESCRIPTION), MULTIPART_UPLOAD_ID);
|
||||
RecordedRequest request = server.takeRequest();
|
||||
assertEquals(request.getRequestLine(), "POST /-/vaults/" + VAULT_NAME + "/multipart-uploads " + HTTP);
|
||||
assertEquals(request.getHeader(GlacierHeaders.PART_SIZE), "4194304");
|
||||
assertEquals(request.getHeader(GlacierHeaders.ARCHIVE_DESCRIPTION), DESCRIPTION);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUploadPart() throws InterruptedException {
|
||||
MockResponse mr = buildBaseResponse(204);
|
||||
mr.addHeader(GlacierHeaders.TREE_HASH, TREEHASH);
|
||||
server.enqueue(mr);
|
||||
|
||||
assertEquals(
|
||||
client.uploadPart(VAULT_NAME, MULTIPART_UPLOAD_ID, ContentRange.fromPartNumber(0, 4), buildPayload(4 * MiB)),
|
||||
TREEHASH);
|
||||
RecordedRequest request = server.takeRequest();
|
||||
assertEquals(request.getRequestLine(),
|
||||
"PUT /-/vaults/" + VAULT_NAME + "/multipart-uploads/" + MULTIPART_UPLOAD_ID + " " + HTTP);
|
||||
assertEquals(request.getHeader(HttpHeaders.CONTENT_RANGE), "bytes 0-4194303/*");
|
||||
assertEquals(request.getHeader(HttpHeaders.CONTENT_LENGTH), "4194304");
|
||||
}
|
||||
|
||||
// TODO: Change size to 4096 when moving to JDK 7
|
||||
@Test
|
||||
public void testUploadPartMaxSize() throws InterruptedException {
|
||||
MockResponse mr = buildBaseResponse(204);
|
||||
mr.addHeader(GlacierHeaders.TREE_HASH, TREEHASH);
|
||||
server.enqueue(mr);
|
||||
|
||||
long size = 1024;
|
||||
ContentRange range = ContentRange.fromPartNumber(0, size);
|
||||
Payload payload = buildPayload(1);
|
||||
payload.getContentMetadata().setContentLength(size * MiB);
|
||||
try {
|
||||
/* The client.uploadPart call should throw an HttpResponseException since the payload is smaller than expected.
|
||||
* This trick makes the test way faster.
|
||||
*/
|
||||
client.uploadPart(VAULT_NAME, MULTIPART_UPLOAD_ID, range, payload);
|
||||
Assert.fail();
|
||||
} catch (HttpResponseException e) {
|
||||
}
|
||||
RecordedRequest request = server.takeRequest();
|
||||
assertEquals(request.getRequestLine(), "PUT /-/vaults/" + VAULT_NAME + "/multipart-uploads/" + MULTIPART_UPLOAD_ID + " " + HTTP);
|
||||
assertEquals(request.getHeader(HttpHeaders.CONTENT_RANGE), range.buildHeader());
|
||||
assertEquals(request.getHeader(HttpHeaders.CONTENT_LENGTH), payload.getContentMetadata().getContentLength().toString());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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.predicates.validators;
|
||||
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@Test(groups = "unit", testName = "PartSizeValidatorTest")
|
||||
public class PartSizeValidatorTest {
|
||||
private static final PartSizeValidator VALIDATOR = new PartSizeValidator();
|
||||
|
||||
public void testValidate() {
|
||||
VALIDATOR.validate(1L);
|
||||
VALIDATOR.validate(2L);
|
||||
VALIDATOR.validate(4L);
|
||||
VALIDATOR.validate(32L);
|
||||
VALIDATOR.validate(4096L);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testZero() {
|
||||
VALIDATOR.validate(0L);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testTooBig() {
|
||||
VALIDATOR.validate(8192L);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testNotPowerOfTwo() {
|
||||
VALIDATOR.validate(25L);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* 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.util;
|
||||
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
@Test(groups = "unit", testName = "ContentRangeTest")
|
||||
public class ContentRangeTest {
|
||||
@Test
|
||||
public void testContentRangeFromString() {
|
||||
ContentRange range = ContentRange.fromString("0-10");
|
||||
assertEquals(range.getFrom(), 0);
|
||||
assertEquals(range.getTo(), 10);
|
||||
range = ContentRange.fromString("1000-2000");
|
||||
assertEquals(range.getFrom(), 1000);
|
||||
assertEquals(range.getTo(), 2000);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testContentRangeFromStringWithoutTo() {
|
||||
ContentRange.fromString("-10");
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testContentRangeFromStringWithoutFrom() {
|
||||
ContentRange.fromString("10-");
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testContentRangeFromStringWithEmptyString() {
|
||||
ContentRange.fromString("");
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testContentRangeFromStringWithNullString() {
|
||||
ContentRange.fromString(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testContentRangeFromPartNumber() {
|
||||
ContentRange range = ContentRange.fromPartNumber(0, 4096);
|
||||
assertEquals(range.getFrom(), 0);
|
||||
assertEquals(range.getTo(), (4096L << 20) - 1);
|
||||
range = ContentRange.fromPartNumber(1, 4096);
|
||||
assertEquals(range.getFrom(), 4096L << 20);
|
||||
assertEquals(range.getTo(), 2 * (4096L << 20) - 1);
|
||||
range = ContentRange.fromPartNumber(2, 4096);
|
||||
assertEquals(range.getFrom(), 2 * (4096L << 20));
|
||||
assertEquals(range.getTo(), 3 * (4096L << 20) - 1);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testContentRangeFromPartNumberWithNegativePartNumber() {
|
||||
ContentRange.fromPartNumber(-1, 4096);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testContentRangeFromPartNumberWithZeroPartSize() {
|
||||
ContentRange.fromPartNumber(0, 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuildContentRange() {
|
||||
ContentRange range = ContentRange.build(0, 4096);
|
||||
assertEquals(range.getFrom(), 0);
|
||||
assertEquals(range.getTo(), 4096);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testBuildContentRangeWithTransposedValues() {
|
||||
ContentRange.build(50, 10);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testBuildContentRangeWithNegatives() {
|
||||
ContentRange.build(-100, -50);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = IllegalArgumentException.class)
|
||||
public void testBuildContentRangeWithZeroTo() {
|
||||
ContentRange.build(0, 0);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue