JCLOUDS-391: Azure multipart putBlob user metadata

This commit is contained in:
Andrew Gaul 2014-12-19 15:48:49 -08:00
parent 787ce446cd
commit 1c781cc5fa
5 changed files with 111 additions and 7 deletions

View File

@ -43,6 +43,7 @@ import org.jclouds.azure.storage.filters.SharedKeyLiteAuthentication;
import org.jclouds.azure.storage.options.ListOptions;
import org.jclouds.azure.storage.reference.AzureStorageHeaders;
import org.jclouds.azureblob.binders.BindAzureBlobMetadataToRequest;
import org.jclouds.azureblob.binders.BindAzureBlobMetadataToMultipartRequest;
import org.jclouds.azureblob.binders.BindAzureBlocksToRequest;
import org.jclouds.azureblob.domain.AzureBlob;
import org.jclouds.azureblob.domain.BlobProperties;
@ -347,7 +348,10 @@ public interface AzureBlobClient extends Closeable {
* The Put Block List assembles a list of blocks previously uploaded with Put Block into a single
* blob. Blocks are either already committed to a blob or uncommitted. The blocks ids passed here
* are searched for first in the uncommitted block list; then committed using the "latest" strategy.
*
* @deprecated call putBlockList(String, AzureBlob, List<String>) instead
*/
@Deprecated
@Named("PutBlockList")
@PUT
@Path("{container}/{name}")
@ -357,6 +361,20 @@ public interface AzureBlobClient extends Closeable {
@PathParam("name") String name,
@BinderParam(BindAzureBlocksToRequest.class) List<String> blockIdList);
/**
* The Put Block List assembles a list of blocks previously uploaded with Put Block into a single
* blob. Blocks are either already committed to a blob or uncommitted. The blocks ids passed here
* are searched for first in the uncommitted block list; then committed using the "latest" strategy.
*/
@Named("PutBlockList")
@PUT
@Path("{container}/{name}")
@ResponseParser(ParseETagHeader.class)
@QueryParams(keys = { "comp" }, values = { "blocklist" })
String putBlockList(@PathParam("container") @ParamValidators(ContainerNameValidator.class) String container,
@PathParam("name") @ParamParser(BlobName.class) @BinderParam(BindAzureBlobMetadataToMultipartRequest.class) AzureBlob object,
@BinderParam(BindAzureBlocksToRequest.class) List<String> blockIdList);
@Named("GetBlockList")
@GET
@Path("{container}/{name}")

View File

@ -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.azureblob.binders;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import javax.inject.Inject;
import org.jclouds.azureblob.blobstore.functions.AzureBlobToBlob;
import org.jclouds.azureblob.domain.AzureBlob;
import org.jclouds.blobstore.binders.BindUserMetadataToHeadersWithPrefix;
import org.jclouds.http.HttpRequest;
import org.jclouds.rest.Binder;
public class BindAzureBlobMetadataToMultipartRequest implements Binder {
private final AzureBlobToBlob azureBlob2Blob;
private final BindUserMetadataToHeadersWithPrefix blobBinder;
@Inject
BindAzureBlobMetadataToMultipartRequest(AzureBlobToBlob azureBlob2Blob, BindUserMetadataToHeadersWithPrefix blobBinder) {
this.azureBlob2Blob = azureBlob2Blob;
this.blobBinder = blobBinder;
}
@SuppressWarnings("unchecked")
@Override
public <R extends HttpRequest> R bindToRequest(R request, Object input) {
checkArgument(checkNotNull(input, "input") instanceof AzureBlob, "this binder is only valid for AzureBlobs!");
checkNotNull(request, "request");
AzureBlob blob = AzureBlob.class.cast(input);
checkArgument(blob.getPayload().getContentMetadata().getContentLength() != null
&& blob.getPayload().getContentMetadata().getContentLength() >= 0, "size must be set");
return blobBinder.bindToRequest(request, azureBlob2Blob.apply(blob));
}
}

View File

@ -21,6 +21,7 @@ import com.google.common.hash.Hashing;
import com.google.common.io.BaseEncoding;
import com.google.inject.Inject;
import org.jclouds.azureblob.AzureBlobClient;
import org.jclouds.azureblob.blobstore.functions.BlobToAzureBlob;
import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.reference.BlobStoreConstants;
import org.jclouds.io.Payload;
@ -46,11 +47,13 @@ public class AzureBlobBlockUploadStrategy implements MultipartUploadStrategy {
private final AzureBlobClient client;
private final PayloadSlicer slicer;
private final BlobToAzureBlob blobToAzureBlob;
@Inject
public AzureBlobBlockUploadStrategy(AzureBlobClient client, PayloadSlicer slicer) {
this.client = checkNotNull(client, "client");
this.slicer = checkNotNull(slicer, "slicer");
AzureBlobBlockUploadStrategy(AzureBlobClient client, PayloadSlicer slicer, BlobToAzureBlob blobToAzureBlob) {
this.client = client;
this.slicer = slicer;
this.blobToAzureBlob = blobToAzureBlob;
}
@Override
@ -74,6 +77,6 @@ public class AzureBlobBlockUploadStrategy implements MultipartUploadStrategy {
}
checkState(bytesWritten == length, "Wrote %s bytes, but we wanted to write %s bytes", bytesWritten, length);
return client.putBlockList(container, blobName, blockIds);
return client.putBlockList(container, blobToAzureBlob.apply(blob), blockIds);
}
}

View File

@ -18,13 +18,16 @@ package org.jclouds.azureblob.blobstore.integration;
import java.io.File;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import com.google.common.collect.ImmutableMap;
import com.google.common.io.ByteSource;
import com.google.common.io.Files;
import org.jclouds.azureblob.blobstore.strategy.MultipartUploadStrategy;
import org.jclouds.blobstore.BlobStore;
import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.domain.BlobMetadata;
import org.jclouds.blobstore.integration.internal.BaseBlobIntegrationTest;
import org.jclouds.blobstore.options.PutOptions;
import org.jclouds.io.ByteStreams2;
@ -133,4 +136,27 @@ public class AzureBlobIntegrationLiveTest extends BaseBlobIntegrationTest {
returnContainer(containerName);
}
}
public void testMultipartUserMetadata() throws Exception {
BlobStore blobStore = view.getBlobStore();
String containerName = getContainerName();
String blobName = "const.txt";
ByteSource byteSource = TestUtils.randomByteSource().slice(0, MultipartUploadStrategy.MAX_BLOCK_SIZE + 1);
Map<String, String> userMetadata = ImmutableMap.of("foo", "bar");
blobStore.createContainerInLocation(null, containerName);
try {
Blob blob = blobStore.blobBuilder(blobName)
.payload(byteSource)
.contentLength(byteSource.size())
.userMetadata(userMetadata)
.build();
blobStore.putBlob(containerName, blob, PutOptions.Builder.multipart());
BlobMetadata blobMetadata = blobStore.blobMetadata(containerName, blobName);
assertThat(blobMetadata.getUserMetadata()).isEqualTo(userMetadata);
} finally {
returnContainer(containerName);
}
}
}

View File

@ -22,6 +22,8 @@ import com.google.common.collect.ImmutableList;
import com.google.common.io.ByteSource;
import org.easymock.EasyMock;
import org.jclouds.azureblob.AzureBlobClient;
import org.jclouds.azureblob.blobstore.functions.BlobToAzureBlob;
import org.jclouds.azureblob.domain.AzureBlob;
import org.jclouds.blobstore.domain.Blob;
import org.jclouds.blobstore.domain.MutableBlobMetadata;
import org.jclouds.blobstore.domain.internal.BlobImpl;
@ -54,6 +56,7 @@ public class AzureBlobBlockUploadStrategyTest {
byte[] blobData = "ABCD".getBytes(Charsets.UTF_8);
AzureBlobClient client = createMock(AzureBlobClient.class);
PayloadSlicer slicer = createMock(PayloadSlicer.class);
BlobToAzureBlob blobToAzureBlob = createMock(BlobToAzureBlob.class);
MutableBlobMetadata metadata = new MutableBlobMetadataImpl();
MutableContentMetadata contentMetadata = new BaseMutableContentMetadata();
contentMetadata.setContentLength((long)blobData.length);
@ -76,9 +79,9 @@ public class AzureBlobBlockUploadStrategyTest {
client.putBlock(eq(container), eq(blobName), anyObject(String.class), eq(payloads.get(1)));
client.putBlock(eq(container), eq(blobName), anyObject(String.class), eq(payloads.get(2)));
client.putBlock(eq(container), eq(blobName), anyObject(String.class), eq(payloads.get(3)));
expect(client.putBlockList(eq(container), eq(blobName), EasyMock.<List<String>>anyObject())).andReturn("Fake ETAG");
expect(client.putBlockList(eq(container), anyObject(AzureBlob.class), EasyMock.<List<String>>anyObject())).andReturn("Fake ETAG");
AzureBlobBlockUploadStrategy strat = new AzureBlobBlockUploadStrategy(client, slicer);
AzureBlobBlockUploadStrategy strat = new AzureBlobBlockUploadStrategy(client, slicer, blobToAzureBlob);
replay(slicer, client);
String etag = strat.execute(container, blob);
assertEquals(etag, "Fake ETAG");
@ -93,6 +96,7 @@ public class AzureBlobBlockUploadStrategyTest {
AzureBlobClient client = createNiceMock(AzureBlobClient.class);
PayloadSlicer slicer = createNiceMock(PayloadSlicer.class);
BlobToAzureBlob blobToAzureBlob = createMock(BlobToAzureBlob.class);
MutableBlobMetadata metadata = new MutableBlobMetadataImpl();
MutableContentMetadata contentMetadata = new BaseMutableContentMetadata();
@ -105,7 +109,7 @@ public class AzureBlobBlockUploadStrategyTest {
payload.setContentMetadata(contentMetadata);
blob.setPayload(payload);
AzureBlobBlockUploadStrategy strat = new AzureBlobBlockUploadStrategy(client, slicer);
AzureBlobBlockUploadStrategy strat = new AzureBlobBlockUploadStrategy(client, slicer, blobToAzureBlob);
strat.execute(container, blob);
}