mirror of https://github.com/apache/jclouds.git
JCLOUDS-871: Azure Copy Blob support
API reference: http://msdn.microsoft.com/en-us/library/dd894037.aspx
This commit is contained in:
parent
75f6d76f1d
commit
5487400037
|
@ -24,6 +24,13 @@ package org.jclouds.azure.storage.reference;
|
||||||
public final class AzureStorageHeaders {
|
public final class AzureStorageHeaders {
|
||||||
|
|
||||||
public static final String USER_METADATA_PREFIX = "x-ms-meta-";
|
public static final String USER_METADATA_PREFIX = "x-ms-meta-";
|
||||||
|
|
||||||
|
public static final String COPY_SOURCE = "x-ms-copy-source";
|
||||||
|
public static final String COPY_SOURCE_IF_MODIFIED_SINCE = "x-ms-source-if-modified-since";
|
||||||
|
public static final String COPY_SOURCE_IF_UNMODIFIED_SINCE = "x-ms-source-if-unmodified-since";
|
||||||
|
public static final String COPY_SOURCE_IF_MATCH = "x-ms-source-if-match";
|
||||||
|
public static final String COPY_SOURCE_IF_NONE_MATCH = "x-ms-source-if-none-match";
|
||||||
|
|
||||||
public static final String REQUEST_ID = "x-ms-request-id";
|
public static final String REQUEST_ID = "x-ms-request-id";
|
||||||
public static final String VERSION = "x-ms-version";
|
public static final String VERSION = "x-ms-version";
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ import static org.jclouds.blobstore.BlobStoreFallbacks.NullOnContainerNotFound;
|
||||||
import static org.jclouds.blobstore.BlobStoreFallbacks.NullOnKeyNotFound;
|
import static org.jclouds.blobstore.BlobStoreFallbacks.NullOnKeyNotFound;
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
|
import java.net.URI;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
@ -57,6 +58,7 @@ import org.jclouds.azureblob.functions.ParseBlobFromHeadersAndHttpContent;
|
||||||
import org.jclouds.azureblob.functions.ParseBlobPropertiesFromHeaders;
|
import org.jclouds.azureblob.functions.ParseBlobPropertiesFromHeaders;
|
||||||
import org.jclouds.azureblob.functions.ParseContainerPropertiesFromHeaders;
|
import org.jclouds.azureblob.functions.ParseContainerPropertiesFromHeaders;
|
||||||
import org.jclouds.azureblob.functions.ParsePublicAccessHeader;
|
import org.jclouds.azureblob.functions.ParsePublicAccessHeader;
|
||||||
|
import org.jclouds.azureblob.options.CopyBlobOptions;
|
||||||
import org.jclouds.azureblob.options.CreateContainerOptions;
|
import org.jclouds.azureblob.options.CreateContainerOptions;
|
||||||
import org.jclouds.azureblob.options.ListBlobsOptions;
|
import org.jclouds.azureblob.options.ListBlobsOptions;
|
||||||
import org.jclouds.azureblob.predicates.validators.BlockIdValidator;
|
import org.jclouds.azureblob.predicates.validators.BlockIdValidator;
|
||||||
|
@ -442,4 +444,15 @@ public interface AzureBlobClient extends Closeable {
|
||||||
@PathParam("container") @ParamValidators(ContainerNameValidator.class) String container,
|
@PathParam("container") @ParamValidators(ContainerNameValidator.class) String container,
|
||||||
@PathParam("name") String name);
|
@PathParam("name") String name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws ContainerNotFoundException if the container is not present.
|
||||||
|
*/
|
||||||
|
@Named("CopyBlob")
|
||||||
|
@PUT
|
||||||
|
@Path("{toContainer}/{toName}")
|
||||||
|
@Headers(keys = AzureStorageHeaders.COPY_SOURCE, values = "{copySource}")
|
||||||
|
void copyBlob(
|
||||||
|
@PathParam("copySource") URI copySource,
|
||||||
|
@PathParam("toContainer") @ParamValidators(ContainerNameValidator.class) String toContainer, @PathParam("toName") String toName,
|
||||||
|
@BinderParam(BindAzureCopyOptionsToRequest.class) CopyBlobOptions options);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
/*
|
||||||
|
* 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.azureblob.binders;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.jclouds.azure.storage.reference.AzureStorageHeaders;
|
||||||
|
import org.jclouds.azureblob.options.CopyBlobOptions;
|
||||||
|
import org.jclouds.date.DateService;
|
||||||
|
import org.jclouds.date.internal.SimpleDateFormatDateService;
|
||||||
|
import org.jclouds.http.HttpRequest;
|
||||||
|
import org.jclouds.rest.Binder;
|
||||||
|
|
||||||
|
import com.google.common.base.Optional;
|
||||||
|
|
||||||
|
/** Binds options to a copyBlob request. */
|
||||||
|
public class BindAzureCopyOptionsToRequest implements Binder {
|
||||||
|
private static final DateService dateService = new SimpleDateFormatDateService();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <R extends HttpRequest> R bindToRequest(R request, Object input) {
|
||||||
|
HttpRequest.Builder builder = request.toBuilder();
|
||||||
|
CopyBlobOptions options = (CopyBlobOptions) input;
|
||||||
|
|
||||||
|
Optional<Map<String, String>> userMetadata = options.getUserMetadata();
|
||||||
|
if (userMetadata.isPresent()) {
|
||||||
|
for (Map.Entry<String, String> entry : userMetadata.get().entrySet()) {
|
||||||
|
builder.addHeader(AzureStorageHeaders.USER_METADATA_PREFIX + entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional<Date> ifModifiedSince = options.getIfModifiedSince();
|
||||||
|
if (ifModifiedSince.isPresent()) {
|
||||||
|
builder.addHeader(AzureStorageHeaders.COPY_SOURCE_IF_MODIFIED_SINCE, dateService.rfc822DateFormat(ifModifiedSince.get()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional<Date> ifUnmodifiedSince = options.getIfUnmodifiedSince();
|
||||||
|
if (ifUnmodifiedSince.isPresent()) {
|
||||||
|
builder.addHeader(AzureStorageHeaders.COPY_SOURCE_IF_UNMODIFIED_SINCE, dateService.rfc822DateFormat(ifUnmodifiedSince.get()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional<String> ifMatch = options.getIfMatch();
|
||||||
|
if (ifMatch.isPresent()) {
|
||||||
|
builder.addHeader(AzureStorageHeaders.COPY_SOURCE_IF_MATCH, ifMatch.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional<String> ifNoneMatch = options.getIfNoneMatch();
|
||||||
|
if (ifNoneMatch.isPresent()) {
|
||||||
|
builder.addHeader(AzureStorageHeaders.COPY_SOURCE_IF_NONE_MATCH, ifNoneMatch.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
return (R) builder.build();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.azureblob.options;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.google.common.base.Optional;
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
|
||||||
|
public final class CopyBlobOptions {
|
||||||
|
public static final CopyBlobOptions NONE = CopyBlobOptions.builder().build();
|
||||||
|
|
||||||
|
private final Optional<Map<String, String>> userMetadata;
|
||||||
|
private final Optional<Date> ifModifiedSince;
|
||||||
|
private final Optional<Date> ifUnmodifiedSince;
|
||||||
|
private final Optional<String> ifMatch;
|
||||||
|
private final Optional<String> ifNoneMatch;
|
||||||
|
|
||||||
|
private CopyBlobOptions(Map<String, String> userMetadata, Date ifModifiedSince, Date ifUnmodifiedSince,
|
||||||
|
String ifMatch, String ifNoneMatch) {
|
||||||
|
this.userMetadata = Optional.fromNullable(userMetadata);
|
||||||
|
this.ifModifiedSince = Optional.fromNullable(ifModifiedSince);
|
||||||
|
this.ifUnmodifiedSince = Optional.fromNullable(ifUnmodifiedSince);
|
||||||
|
this.ifMatch = Optional.fromNullable(ifMatch);
|
||||||
|
this.ifNoneMatch = Optional.fromNullable(ifNoneMatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Builder builder() {
|
||||||
|
return new Builder();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Map<String, String>> getUserMetadata() {
|
||||||
|
return userMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Date> getIfModifiedSince() {
|
||||||
|
return Optional.fromNullable(ifModifiedSince.isPresent() ? (Date) ifModifiedSince.get().clone() : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Date> getIfUnmodifiedSince() {
|
||||||
|
return Optional.fromNullable(ifUnmodifiedSince.isPresent() ? (Date) ifUnmodifiedSince.get().clone() : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<String> getIfMatch() {
|
||||||
|
return ifMatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<String> getIfNoneMatch() {
|
||||||
|
return ifNoneMatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Builder {
|
||||||
|
private Map<String, String> userMetadata;
|
||||||
|
private Date ifModifiedSince;
|
||||||
|
private Date ifUnmodifiedSince;
|
||||||
|
private String ifMatch;
|
||||||
|
private String ifNoneMatch;
|
||||||
|
|
||||||
|
Builder() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder overrideUserMetadata(Map<String, String> userMetadata) {
|
||||||
|
this.userMetadata = ImmutableMap.copyOf(checkNotNull(userMetadata, "userMetadata"));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder ifModifiedSince(Date ifModifiedSince) {
|
||||||
|
this.ifModifiedSince = (Date) checkNotNull(ifModifiedSince, "ifModifiedSince").clone();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder ifUnmodifiedSince(Date ifUnmodifiedSince) {
|
||||||
|
this.ifUnmodifiedSince = (Date) checkNotNull(ifUnmodifiedSince, "ifUnmodifiedSince").clone();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder ifMatch(String ifMatch) {
|
||||||
|
this.ifMatch = checkNotNull(ifMatch, "ifMatch");
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder ifNoneMatch(String ifNoneMatch) {
|
||||||
|
this.ifNoneMatch = checkNotNull(ifNoneMatch, "ifNoneMatch");
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CopyBlobOptions build() {
|
||||||
|
return new CopyBlobOptions(userMetadata, ifModifiedSince, ifUnmodifiedSince, ifMatch, ifNoneMatch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,6 +18,7 @@ package org.jclouds.azureblob;
|
||||||
|
|
||||||
import static com.google.common.io.BaseEncoding.base16;
|
import static com.google.common.io.BaseEncoding.base16;
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Fail.failBecauseExceptionWasNotThrown;
|
||||||
import static org.jclouds.azure.storage.options.ListOptions.Builder.includeMetadata;
|
import static org.jclouds.azure.storage.options.ListOptions.Builder.includeMetadata;
|
||||||
import static org.jclouds.azureblob.options.CreateContainerOptions.Builder.withMetadata;
|
import static org.jclouds.azureblob.options.CreateContainerOptions.Builder.withMetadata;
|
||||||
import static org.jclouds.azureblob.options.CreateContainerOptions.Builder.withPublicAccess;
|
import static org.jclouds.azureblob.options.CreateContainerOptions.Builder.withPublicAccess;
|
||||||
|
@ -29,6 +30,8 @@ import java.lang.reflect.UndeclaredThrowableException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.jclouds.azure.storage.AzureStorageResponseException;
|
import org.jclouds.azure.storage.AzureStorageResponseException;
|
||||||
|
@ -40,14 +43,17 @@ import org.jclouds.azureblob.domain.ContainerProperties;
|
||||||
import org.jclouds.azureblob.domain.ListBlobBlocksResponse;
|
import org.jclouds.azureblob.domain.ListBlobBlocksResponse;
|
||||||
import org.jclouds.azureblob.domain.ListBlobsResponse;
|
import org.jclouds.azureblob.domain.ListBlobsResponse;
|
||||||
import org.jclouds.azureblob.domain.PublicAccess;
|
import org.jclouds.azureblob.domain.PublicAccess;
|
||||||
|
import org.jclouds.azureblob.options.CopyBlobOptions;
|
||||||
import org.jclouds.azureblob.options.ListBlobsOptions;
|
import org.jclouds.azureblob.options.ListBlobsOptions;
|
||||||
import org.jclouds.blobstore.ContainerNotFoundException;
|
import org.jclouds.blobstore.ContainerNotFoundException;
|
||||||
import org.jclouds.blobstore.integration.internal.BaseBlobStoreIntegrationTest;
|
import org.jclouds.blobstore.integration.internal.BaseBlobStoreIntegrationTest;
|
||||||
import org.jclouds.http.HttpResponseException;
|
import org.jclouds.http.HttpResponseException;
|
||||||
import org.jclouds.http.options.GetOptions;
|
import org.jclouds.http.options.GetOptions;
|
||||||
|
import org.jclouds.io.ByteStreams2;
|
||||||
import org.jclouds.io.Payloads;
|
import org.jclouds.io.Payloads;
|
||||||
import org.jclouds.util.Strings2;
|
import org.jclouds.util.Strings2;
|
||||||
import org.jclouds.util.Throwables2;
|
import org.jclouds.util.Throwables2;
|
||||||
|
import org.jclouds.utils.TestUtils;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
import com.google.common.base.Charsets;
|
import com.google.common.base.Charsets;
|
||||||
|
@ -56,6 +62,7 @@ import com.google.common.collect.ImmutableMultimap;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.common.hash.Hashing;
|
import com.google.common.hash.Hashing;
|
||||||
import com.google.common.io.BaseEncoding;
|
import com.google.common.io.BaseEncoding;
|
||||||
|
import com.google.common.io.ByteSource;
|
||||||
|
|
||||||
@Test(groups = "live", singleThreaded = true)
|
@Test(groups = "live", singleThreaded = true)
|
||||||
public class AzureBlobClientLiveTest extends BaseBlobStoreIntegrationTest {
|
public class AzureBlobClientLiveTest extends BaseBlobStoreIntegrationTest {
|
||||||
|
@ -384,4 +391,157 @@ public class AzureBlobClientLiveTest extends BaseBlobStoreIntegrationTest {
|
||||||
client.setPublicAccessForContainer(blockContainer, access);
|
client.setPublicAccessForContainer(blockContainer, access);
|
||||||
assertThat(client.getPublicAccessForContainer(blockContainer)).isEqualTo(access);
|
assertThat(client.getPublicAccessForContainer(blockContainer)).isEqualTo(access);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(timeOut = 5 * 60 * 1000, dependsOnMethods = { "testCreateContainer" })
|
||||||
|
public void testCopyBlob() throws Exception {
|
||||||
|
ByteSource byteSource = TestUtils.randomByteSource().slice(0, 1024);
|
||||||
|
|
||||||
|
// create blob
|
||||||
|
AzureBlob object = getApi().newBlob();
|
||||||
|
object.getProperties().setName("from");
|
||||||
|
object.setPayload(byteSource.read());
|
||||||
|
getApi().putBlob(privateContainer, object);
|
||||||
|
|
||||||
|
// copy blob
|
||||||
|
URI copySource = view.getSigner().signGetBlob(privateContainer, "from").getEndpoint();
|
||||||
|
getApi().copyBlob(copySource, privateContainer, "to", CopyBlobOptions.NONE);
|
||||||
|
|
||||||
|
// ensure copied blob matches original
|
||||||
|
AzureBlob getBlob = getApi().getBlob(privateContainer, "to");
|
||||||
|
assertEquals(ByteStreams2.toByteArrayAndClose(getBlob.getPayload().openStream()), byteSource.read());
|
||||||
|
assertThat(getBlob.getProperties().getMetadata().isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(timeOut = 5 * 60 * 1000, dependsOnMethods = { "testCreateContainer" })
|
||||||
|
public void testCopyBlobReplaceMetadata() throws Exception {
|
||||||
|
ByteSource byteSource = TestUtils.randomByteSource().slice(0, 1024);
|
||||||
|
|
||||||
|
// create blob
|
||||||
|
AzureBlob object = getApi().newBlob();
|
||||||
|
object.getProperties().setName("from");
|
||||||
|
object.setPayload(byteSource.read());
|
||||||
|
getApi().putBlob(privateContainer, object);
|
||||||
|
|
||||||
|
// copy blob
|
||||||
|
URI copySource = view.getSigner().signGetBlob(privateContainer, "from").getEndpoint();
|
||||||
|
Map<String, String> newMetadata = ImmutableMap.of("foo", "bar");
|
||||||
|
getApi().copyBlob(copySource, privateContainer, "to", CopyBlobOptions.builder().overrideUserMetadata(newMetadata).build());
|
||||||
|
|
||||||
|
// ensure copied blob matches original
|
||||||
|
AzureBlob getBlob = getApi().getBlob(privateContainer, "to");
|
||||||
|
assertEquals(ByteStreams2.toByteArrayAndClose(getBlob.getPayload().openStream()), byteSource.read());
|
||||||
|
assertThat(getBlob.getProperties().getMetadata()).isEqualTo(newMetadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(timeOut = 5 * 60 * 1000, dependsOnMethods = { "testCreateContainer" })
|
||||||
|
public void testCopyBlobIfModifiedSince() throws Exception {
|
||||||
|
ByteSource byteSource = TestUtils.randomByteSource().slice(0, 1024);
|
||||||
|
|
||||||
|
// create blob
|
||||||
|
AzureBlob object = getApi().newBlob();
|
||||||
|
object.getProperties().setName("from");
|
||||||
|
object.setPayload(byteSource.read());
|
||||||
|
String eTag = getApi().putBlob(privateContainer, object);
|
||||||
|
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
Date before = new Date(now - 1000 * 1000);
|
||||||
|
Date after = new Date(now + 1000 * 1000);
|
||||||
|
URI copySource = view.getSigner().signGetBlob(privateContainer, "from").getEndpoint();
|
||||||
|
|
||||||
|
// failure case
|
||||||
|
try {
|
||||||
|
getApi().copyBlob(copySource, privateContainer, "to-if-modified-since", CopyBlobOptions.builder().ifModifiedSince(after).build());
|
||||||
|
failBecauseExceptionWasNotThrown(AzureStorageResponseException.class);
|
||||||
|
} catch (AzureStorageResponseException asre) {
|
||||||
|
assertThat(asre.getResponse().getStatusCode()).as("status code").isEqualTo(412);
|
||||||
|
}
|
||||||
|
|
||||||
|
// success case
|
||||||
|
getApi().copyBlob(copySource, privateContainer, "to-if-modified-since", CopyBlobOptions.builder().ifModifiedSince(before).build());
|
||||||
|
AzureBlob getBlob = getApi().getBlob(privateContainer, "to-if-modified-since");
|
||||||
|
assertEquals(ByteStreams2.toByteArrayAndClose(getBlob.getPayload().openStream()), byteSource.read());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(timeOut = 5 * 60 * 1000, dependsOnMethods = { "testCreateContainer" })
|
||||||
|
public void testCopyBlobIfUnmodifiedSince() throws Exception {
|
||||||
|
ByteSource byteSource = TestUtils.randomByteSource().slice(0, 1024);
|
||||||
|
|
||||||
|
// create blob
|
||||||
|
AzureBlob object = getApi().newBlob();
|
||||||
|
object.getProperties().setName("from");
|
||||||
|
object.setPayload(byteSource.read());
|
||||||
|
String eTag = getApi().putBlob(privateContainer, object);
|
||||||
|
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
Date before = new Date(now - 1000 * 1000);
|
||||||
|
Date after = new Date(now + 1000 * 1000);
|
||||||
|
URI copySource = view.getSigner().signGetBlob(privateContainer, "from").getEndpoint();
|
||||||
|
|
||||||
|
// failure case
|
||||||
|
try {
|
||||||
|
getApi().copyBlob(copySource, privateContainer, "to-if-unmodifed-since", CopyBlobOptions.builder().ifUnmodifiedSince(before).build());
|
||||||
|
failBecauseExceptionWasNotThrown(AzureStorageResponseException.class);
|
||||||
|
} catch (AzureStorageResponseException asre) {
|
||||||
|
assertThat(asre.getResponse().getStatusCode()).as("status code").isEqualTo(412);
|
||||||
|
}
|
||||||
|
|
||||||
|
// success case
|
||||||
|
getApi().copyBlob(copySource, privateContainer, "to-if-unmodifed-since", CopyBlobOptions.builder().ifUnmodifiedSince(after).build());
|
||||||
|
AzureBlob getBlob = getApi().getBlob(privateContainer, "to-if-unmodifed-since");
|
||||||
|
assertEquals(ByteStreams2.toByteArrayAndClose(getBlob.getPayload().openStream()), byteSource.read());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(timeOut = 5 * 60 * 1000, dependsOnMethods = { "testCreateContainer" })
|
||||||
|
public void testCopyBlobIfMatch() throws Exception {
|
||||||
|
ByteSource byteSource = TestUtils.randomByteSource().slice(0, 1024);
|
||||||
|
|
||||||
|
// create blob
|
||||||
|
AzureBlob object = getApi().newBlob();
|
||||||
|
object.getProperties().setName("from");
|
||||||
|
object.setPayload(byteSource.read());
|
||||||
|
String eTag = getApi().putBlob(privateContainer, object);
|
||||||
|
String fakeETag = "0x8CEB669D794AFE2";
|
||||||
|
|
||||||
|
URI copySource = view.getSigner().signGetBlob(privateContainer, "from").getEndpoint();
|
||||||
|
|
||||||
|
// failure case
|
||||||
|
try {
|
||||||
|
getApi().copyBlob(copySource, privateContainer, "to-if-match", CopyBlobOptions.builder().ifMatch(fakeETag).build());
|
||||||
|
failBecauseExceptionWasNotThrown(AzureStorageResponseException.class);
|
||||||
|
} catch (AzureStorageResponseException asre) {
|
||||||
|
assertThat(asre.getResponse().getStatusCode()).as("status code").isEqualTo(412);
|
||||||
|
}
|
||||||
|
|
||||||
|
// success case
|
||||||
|
getApi().copyBlob(copySource, privateContainer, "to-if-match", CopyBlobOptions.builder().ifMatch(eTag).build());
|
||||||
|
AzureBlob getBlob = getApi().getBlob(privateContainer, "to-if-match");
|
||||||
|
assertEquals(ByteStreams2.toByteArrayAndClose(getBlob.getPayload().openStream()), byteSource.read());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(timeOut = 5 * 60 * 1000, dependsOnMethods = { "testCreateContainer" })
|
||||||
|
public void testCopyBlobIfNoneMatch() throws Exception {
|
||||||
|
ByteSource byteSource = TestUtils.randomByteSource().slice(0, 1024);
|
||||||
|
|
||||||
|
// create blob
|
||||||
|
AzureBlob object = getApi().newBlob();
|
||||||
|
object.getProperties().setName("from");
|
||||||
|
object.setPayload(byteSource.read());
|
||||||
|
String eTag = getApi().putBlob(privateContainer, object);
|
||||||
|
String fakeETag = "0x8CEB669D794AFE2";
|
||||||
|
|
||||||
|
URI copySource = view.getSigner().signGetBlob(privateContainer, "from").getEndpoint();
|
||||||
|
|
||||||
|
// failure case
|
||||||
|
try {
|
||||||
|
getApi().copyBlob(copySource, privateContainer, "to-if-none-match", CopyBlobOptions.builder().ifNoneMatch(eTag).build());
|
||||||
|
failBecauseExceptionWasNotThrown(AzureStorageResponseException.class);
|
||||||
|
} catch (AzureStorageResponseException asre) {
|
||||||
|
assertThat(asre.getResponse().getStatusCode()).as("status code").isEqualTo(412);
|
||||||
|
}
|
||||||
|
|
||||||
|
// success case
|
||||||
|
getApi().copyBlob(copySource, privateContainer, "to-if-none-match", CopyBlobOptions.builder().ifNoneMatch(fakeETag).build());
|
||||||
|
AzureBlob getBlob = getApi().getBlob(privateContainer, "to-if-none-match");
|
||||||
|
assertEquals(ByteStreams2.toByteArrayAndClose(getBlob.getPayload().openStream()), byteSource.read());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,8 @@ import static org.jclouds.reflect.Reflection2.method;
|
||||||
import static org.testng.Assert.assertEquals;
|
import static org.testng.Assert.assertEquals;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.jclouds.Fallbacks.TrueOnNotFoundOr404;
|
import org.jclouds.Fallbacks.TrueOnNotFoundOr404;
|
||||||
|
@ -33,6 +35,7 @@ import org.jclouds.azureblob.domain.PublicAccess;
|
||||||
import org.jclouds.azureblob.functions.ParseBlobFromHeadersAndHttpContent;
|
import org.jclouds.azureblob.functions.ParseBlobFromHeadersAndHttpContent;
|
||||||
import org.jclouds.azureblob.functions.ParseContainerPropertiesFromHeaders;
|
import org.jclouds.azureblob.functions.ParseContainerPropertiesFromHeaders;
|
||||||
import org.jclouds.azureblob.functions.ParsePublicAccessHeader;
|
import org.jclouds.azureblob.functions.ParsePublicAccessHeader;
|
||||||
|
import org.jclouds.azureblob.options.CopyBlobOptions;
|
||||||
import org.jclouds.azureblob.options.CreateContainerOptions;
|
import org.jclouds.azureblob.options.CreateContainerOptions;
|
||||||
import org.jclouds.azureblob.options.ListBlobsOptions;
|
import org.jclouds.azureblob.options.ListBlobsOptions;
|
||||||
import org.jclouds.azureblob.xml.AccountNameEnumerationResultsHandler;
|
import org.jclouds.azureblob.xml.AccountNameEnumerationResultsHandler;
|
||||||
|
@ -304,6 +307,102 @@ public class AzureBlobClientTest extends BaseRestAnnotationProcessingTest<AzureB
|
||||||
assertFallbackClassEquals(method, null);
|
assertFallbackClassEquals(method, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testCopyBlob() throws Exception {
|
||||||
|
Invokable<?, ?> method = method(AzureBlobClient.class, "copyBlob", URI.class, String.class, String.class, CopyBlobOptions.class);
|
||||||
|
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of(
|
||||||
|
URI.create("https://identity.blob.core.windows.net/fromcontainer/fromblob"), "tocontainer", "toblob", CopyBlobOptions.NONE));
|
||||||
|
|
||||||
|
assertRequestLineEquals(request,
|
||||||
|
"PUT https://identity.blob.core.windows.net/tocontainer/toblob HTTP/1.1");
|
||||||
|
checkFilters(request);
|
||||||
|
assertNonPayloadHeadersEqual(request,
|
||||||
|
"x-ms-copy-source: https://identity.blob.core.windows.net/fromcontainer/fromblob\n" +
|
||||||
|
"x-ms-version: 2013-08-15\n");
|
||||||
|
assertPayloadEquals(request, null, null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCopyBlobOverwriteUserMetadata() throws Exception {
|
||||||
|
CopyBlobOptions options = CopyBlobOptions.builder().overrideUserMetadata(ImmutableMap.of("foo", "bar")).build();
|
||||||
|
Invokable<?, ?> method = method(AzureBlobClient.class, "copyBlob", URI.class, String.class, String.class, CopyBlobOptions.class);
|
||||||
|
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of(
|
||||||
|
URI.create("https://identity.blob.core.windows.net/fromcontainer/fromblob"), "tocontainer", "toblob", options));
|
||||||
|
|
||||||
|
assertRequestLineEquals(request,
|
||||||
|
"PUT https://identity.blob.core.windows.net/tocontainer/toblob HTTP/1.1");
|
||||||
|
checkFilters(request);
|
||||||
|
assertNonPayloadHeadersEqual(request,
|
||||||
|
"x-ms-copy-source: https://identity.blob.core.windows.net/fromcontainer/fromblob\n" +
|
||||||
|
"x-ms-meta-foo: bar\n" +
|
||||||
|
"x-ms-version: 2013-08-15\n");
|
||||||
|
assertPayloadEquals(request, null, null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCopyBlobIfModifiedSince() throws Exception {
|
||||||
|
CopyBlobOptions options = CopyBlobOptions.builder().ifModifiedSince(new Date(1000)).build();
|
||||||
|
Invokable<?, ?> method = method(AzureBlobClient.class, "copyBlob", URI.class, String.class, String.class, CopyBlobOptions.class);
|
||||||
|
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of(
|
||||||
|
URI.create("https://identity.blob.core.windows.net/fromcontainer/fromblob"), "tocontainer", "toblob", options));
|
||||||
|
|
||||||
|
assertRequestLineEquals(request,
|
||||||
|
"PUT https://identity.blob.core.windows.net/tocontainer/toblob HTTP/1.1");
|
||||||
|
checkFilters(request);
|
||||||
|
assertNonPayloadHeadersEqual(request,
|
||||||
|
"x-ms-copy-source: https://identity.blob.core.windows.net/fromcontainer/fromblob\n" +
|
||||||
|
"x-ms-source-if-modified-since: Thu, 01 Jan 1970 00:00:01 GMT\n" +
|
||||||
|
"x-ms-version: 2013-08-15\n");
|
||||||
|
assertPayloadEquals(request, null, null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCopyBlobIfUnmodifiedSince() throws Exception {
|
||||||
|
CopyBlobOptions options = CopyBlobOptions.builder().ifUnmodifiedSince(new Date(1000)).build();
|
||||||
|
Invokable<?, ?> method = method(AzureBlobClient.class, "copyBlob", URI.class, String.class, String.class, CopyBlobOptions.class);
|
||||||
|
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of(
|
||||||
|
URI.create("https://identity.blob.core.windows.net/fromcontainer/fromblob"), "tocontainer", "toblob", options));
|
||||||
|
|
||||||
|
assertRequestLineEquals(request,
|
||||||
|
"PUT https://identity.blob.core.windows.net/tocontainer/toblob HTTP/1.1");
|
||||||
|
checkFilters(request);
|
||||||
|
assertNonPayloadHeadersEqual(request,
|
||||||
|
"x-ms-copy-source: https://identity.blob.core.windows.net/fromcontainer/fromblob\n" +
|
||||||
|
"x-ms-source-if-unmodified-since: Thu, 01 Jan 1970 00:00:01 GMT\n" +
|
||||||
|
"x-ms-version: 2013-08-15\n");
|
||||||
|
assertPayloadEquals(request, null, null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCopyBlobIfMatch() throws Exception {
|
||||||
|
String eTag = "0x8CEB669D794AFE2";
|
||||||
|
CopyBlobOptions options = CopyBlobOptions.builder().ifMatch(eTag).build();
|
||||||
|
Invokable<?, ?> method = method(AzureBlobClient.class, "copyBlob", URI.class, String.class, String.class, CopyBlobOptions.class);
|
||||||
|
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of(
|
||||||
|
URI.create("https://identity.blob.core.windows.net/fromcontainer/fromblob"), "tocontainer", "toblob", options));
|
||||||
|
|
||||||
|
assertRequestLineEquals(request,
|
||||||
|
"PUT https://identity.blob.core.windows.net/tocontainer/toblob HTTP/1.1");
|
||||||
|
checkFilters(request);
|
||||||
|
assertNonPayloadHeadersEqual(request,
|
||||||
|
"x-ms-copy-source: https://identity.blob.core.windows.net/fromcontainer/fromblob\n" +
|
||||||
|
"x-ms-source-if-match: " + eTag + "\n" +
|
||||||
|
"x-ms-version: 2013-08-15\n");
|
||||||
|
assertPayloadEquals(request, null, null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCopyBlobIfNoneMatch() throws Exception {
|
||||||
|
String eTag = "0x8CEB669D794AFE2";
|
||||||
|
CopyBlobOptions options = CopyBlobOptions.builder().ifNoneMatch(eTag).build();
|
||||||
|
Invokable<?, ?> method = method(AzureBlobClient.class, "copyBlob", URI.class, String.class, String.class, CopyBlobOptions.class);
|
||||||
|
GeneratedHttpRequest request = processor.createRequest(method, ImmutableList.<Object> of(
|
||||||
|
URI.create("https://identity.blob.core.windows.net/fromcontainer/fromblob"), "tocontainer", "toblob", options));
|
||||||
|
|
||||||
|
assertRequestLineEquals(request,
|
||||||
|
"PUT https://identity.blob.core.windows.net/tocontainer/toblob HTTP/1.1");
|
||||||
|
checkFilters(request);
|
||||||
|
assertNonPayloadHeadersEqual(request,
|
||||||
|
"x-ms-copy-source: https://identity.blob.core.windows.net/fromcontainer/fromblob\n" +
|
||||||
|
"x-ms-source-if-none-match: " + eTag + "\n" +
|
||||||
|
"x-ms-version: 2013-08-15\n");
|
||||||
|
assertPayloadEquals(request, null, null, false);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void checkFilters(HttpRequest request) {
|
protected void checkFilters(HttpRequest request) {
|
||||||
assertEquals(request.getFilters().size(), 1);
|
assertEquals(request.getFilters().size(), 1);
|
||||||
|
|
Loading…
Reference in New Issue