From fc3a2d74dc5f3daf5d8c660e6fba571b55bda585 Mon Sep 17 00:00:00 2001 From: Marco Ziccardi Date: Fri, 9 Oct 2015 12:04:53 +0200 Subject: [PATCH 1/6] Add batch get, update, delete methods to storage - Update StorageRpc.BatchRequest to check for null to* lists - Add storage.get, storage.update and storage.delete methods - Add unit tests to StorageImplTest - Add integration tests to ITStorageTest --- .../com/google/gcloud/spi/StorageRpc.java | 11 +- .../com/google/gcloud/storage/Storage.java | 33 +++++ .../google/gcloud/storage/StorageImpl.java | 62 +++++++++ .../google/gcloud/storage/ITStorageTest.java | 99 ++++++++++++++ .../gcloud/storage/StorageImplTest.java | 129 ++++++++++++++++++ 5 files changed, 331 insertions(+), 3 deletions(-) diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/spi/StorageRpc.java b/gcloud-java-storage/src/main/java/com/google/gcloud/spi/StorageRpc.java index b82acae9cd39..b7ac99bf909e 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/spi/StorageRpc.java +++ b/gcloud-java-storage/src/main/java/com/google/gcloud/spi/StorageRpc.java @@ -16,6 +16,8 @@ package com.google.gcloud.spi; +import static com.google.common.base.MoreObjects.firstNonNull; + import com.google.api.services.storage.model.Bucket; import com.google.api.services.storage.model.StorageObject; import com.google.common.collect.ImmutableList; @@ -106,9 +108,12 @@ class BatchRequest { public BatchRequest(Iterable>> toDelete, Iterable>> toUpdate, Iterable>> toGet) { - this.toDelete = ImmutableList.copyOf(toDelete); - this.toUpdate = ImmutableList.copyOf(toUpdate); - this.toGet = ImmutableList.copyOf(toGet); + this.toDelete = ImmutableList.copyOf( + firstNonNull(toDelete, ImmutableList.>>of())); + this.toUpdate = ImmutableList.copyOf( + firstNonNull(toUpdate, ImmutableList.>>of())); + this.toGet = ImmutableList.copyOf( + firstNonNull(toGet, ImmutableList.>>of())); } } diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Storage.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Storage.java index 2ce25a8190f3..8b43410bc6a3 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Storage.java +++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Storage.java @@ -638,4 +638,37 @@ public static Builder builder() { * @see Signed-URLs */ URL signUrl(BlobInfo blobInfo, long expirationTimeInSeconds, SignUrlOption... options); + + /** + * Gets the requested blobs. A batch request is used to perform this call. + * + * @param blobInfo1 first blob to get + * @param blobInfo2 second blob to get + * @param blobInfos other blobs to get + * @return a list of {@code BlobInfo} objects. If a blob does not exist the corresponding item in + * the list is {@code null}. + */ + List get(BlobInfo blobInfo1, BlobInfo blobInfo2, BlobInfo... blobInfos); + + /** + * Updates the requested blobs. A batch request is used to perform this call. + * + * @param blobInfo1 first blob to update + * @param blobInfo2 second blob to update + * @param blobInfos other blobs to update + * @return a list of {@code BlobInfo} objects. If a blob does not exist the corresponding item in + * the list is {@code null}. + */ + List update(BlobInfo blobInfo1, BlobInfo blobInfo2, BlobInfo... blobInfos); + + /** + * Deletes the requested blobs. A batch request is used to perform this call. + * + * @param blobInfo1 first blob to delete + * @param blobInfo2 second blob to delete + * @param blobInfos other blobs to delete + * @return a list of booleans. If a blob has been deleted the corresponding item in the list is + * {@code true}. If deletion failed the item is {@code false}. + */ + List delete(BlobInfo blobInfo1, BlobInfo blobInfo2, BlobInfo... blobInfos); } diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageImpl.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageImpl.java index 0dd4a428a02d..1ec39e3f8aca 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageImpl.java +++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageImpl.java @@ -577,6 +577,68 @@ public URL signUrl(BlobInfo blobInfo, long expiration, SignUrlOption... options) } } + @Override + public List get(BlobInfo blobInfo1, BlobInfo blobInfo2, BlobInfo... blobInfos) { + List>> toGet = + Lists.newArrayListWithCapacity(blobInfos.length + 2); + toGet.add(Tuple.>of(blobInfo1.toPb(), optionMap())); + toGet.add(Tuple.>of(blobInfo2.toPb(), optionMap())); + for (BlobInfo blobInfo : blobInfos) { + toGet.add(Tuple.>of(blobInfo.toPb(), optionMap())); + } + StorageRpc.BatchResponse response = + storageRpc.batch(new StorageRpc.BatchRequest(null, null, toGet)); + return transformBatchResult(toGet, response.gets, BlobInfo.FROM_PB_FUNCTION, (BlobInfo) null); + } + + @Override + public List update(BlobInfo blobInfo1, BlobInfo blobInfo2, BlobInfo... blobInfos) { + List>> toUpdate = + Lists.newArrayListWithCapacity(blobInfos.length + 2); + toUpdate.add(Tuple.>of(blobInfo1.toPb(), optionMap())); + toUpdate.add(Tuple.>of(blobInfo2.toPb(), optionMap())); + for (BlobInfo blobInfo : blobInfos) { + toUpdate + .add(Tuple.>of(blobInfo.toPb(), optionMap())); + } + StorageRpc.BatchResponse response = + storageRpc.batch(new StorageRpc.BatchRequest(null, toUpdate, null)); + return transformBatchResult(toUpdate, response.updates, BlobInfo.FROM_PB_FUNCTION, + (BlobInfo) null); + } + + @Override + public List delete(BlobInfo blobInfo1, BlobInfo blobInfo2, BlobInfo... blobInfos) { + List>> toDelete = + Lists.newArrayListWithCapacity(blobInfos.length + 2); + toDelete.add(Tuple.>of(blobInfo1.toPb(), optionMap())); + toDelete.add(Tuple.>of(blobInfo2.toPb(), optionMap())); + for (BlobInfo blobInfo : blobInfos) { + toDelete + .add(Tuple.>of(blobInfo.toPb(), optionMap())); + } + StorageRpc.BatchResponse response = + storageRpc.batch(new StorageRpc.BatchRequest(toDelete, null, null)); + return transformBatchResult(toDelete, response.deletes, Functions.identity(), + Boolean.FALSE); + } + + private List transformBatchResult( + Iterable>> request, + Map> results, Function transform, + O errorValue) { + List response = Lists.newArrayListWithCapacity(results.size()); + for (Tuple tuple : request) { + Tuple result = results.get(tuple.x()); + if (result.x() != null) { + response.add(transform.apply(result.x())); + } else { + response.add(errorValue); + } + } + return response; + } + private Map optionMap(Long generation, Long metaGeneration, Iterable options) { return optionMap(generation, metaGeneration, options, false); diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/ITStorageTest.java b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/ITStorageTest.java index 4c3ff1fa9a7c..2ca36c8ec1e3 100644 --- a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/ITStorageTest.java +++ b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/ITStorageTest.java @@ -20,6 +20,7 @@ import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -36,6 +37,7 @@ import java.util.Arrays; import java.util.Calendar; import java.util.Iterator; +import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -464,4 +466,101 @@ public void testPostSignedUrl() throws IOException { assertEquals(blob.name(), remoteBlob.name()); assertTrue(storage.delete(bucket, blobName)); } + + @Test + public void testGetBlobs() { + String sourceBlobName1 = "test-get-blobs-1"; + String sourceBlobName2 = "test-get-blobs-2"; + BlobInfo sourceBlob1 = BlobInfo.of(bucket, sourceBlobName1); + BlobInfo sourceBlob2 = BlobInfo.of(bucket, sourceBlobName2); + assertNotNull(storage.create(sourceBlob1)); + assertNotNull(storage.create(sourceBlob2)); + List remoteInfos = storage.get(sourceBlob1, sourceBlob2); + assertEquals(sourceBlob1.bucket(), remoteInfos.get(0).bucket()); + assertEquals(sourceBlob1.name(), remoteInfos.get(0).name()); + assertEquals(sourceBlob2.bucket(), remoteInfos.get(1).bucket()); + assertEquals(sourceBlob2.name(), remoteInfos.get(1).name()); + assertTrue(storage.delete(bucket, sourceBlobName1)); + assertTrue(storage.delete(bucket, sourceBlobName2)); + } + + @Test + public void testGetBlobsFail() { + String sourceBlobName1 = "test-get-blobs-fail-1"; + String sourceBlobName2 = "test-get-blobs-fail-2"; + BlobInfo sourceBlob1 = BlobInfo.of(bucket, sourceBlobName1); + BlobInfo sourceBlob2 = BlobInfo.of(bucket, sourceBlobName2); + assertNotNull(storage.create(sourceBlob1)); + List remoteBlobs = storage.get(sourceBlob1, sourceBlob2); + assertEquals(sourceBlob1.bucket(), remoteBlobs.get(0).bucket()); + assertEquals(sourceBlob1.name(), remoteBlobs.get(0).name()); + assertNull(remoteBlobs.get(1)); + assertTrue(storage.delete(bucket, sourceBlobName1)); + } + + @Test + public void testDeleteBlobs() { + String sourceBlobName1 = "test-delete-blobs-1"; + String sourceBlobName2 = "test-delete-blobs-2"; + BlobInfo sourceBlob1 = BlobInfo.of(bucket, sourceBlobName1); + BlobInfo sourceBlob2 = BlobInfo.of(bucket, sourceBlobName2); + assertNotNull(storage.create(sourceBlob1)); + assertNotNull(storage.create(sourceBlob2)); + List deleteStatus = storage.delete(sourceBlob1, sourceBlob2); + assertTrue(deleteStatus.get(0)); + assertTrue(deleteStatus.get(1)); + } + + @Test + public void testDeleteBlobsFail() { + String sourceBlobName1 = "test-delete-blobs-fail-1"; + String sourceBlobName2 = "test-delete-blobs-fail-2"; + BlobInfo sourceBlob1 = BlobInfo.of(bucket, sourceBlobName1); + BlobInfo sourceBlob2 = BlobInfo.of(bucket, sourceBlobName2); + assertNotNull(storage.create(sourceBlob1)); + List deleteStatus = storage.delete(sourceBlob1, sourceBlob2); + assertTrue(deleteStatus.get(0)); + assertTrue(!deleteStatus.get(1)); + } + + @Test + public void testUpdateBlobs() { + String sourceBlobName1 = "test-update-blobs-1"; + String sourceBlobName2 = "test-update-blobs-2"; + BlobInfo sourceBlob1 = BlobInfo.of(bucket, sourceBlobName1); + BlobInfo sourceBlob2 = BlobInfo.of(bucket, sourceBlobName2); + BlobInfo remoteBlob1 = storage.create(sourceBlob1); + BlobInfo remoteBlob2 = storage.create(sourceBlob2); + assertNotNull(remoteBlob1); + assertNotNull(remoteBlob2); + List updatedBlobs = storage.update( + remoteBlob1.toBuilder().contentType(CONTENT_TYPE).build(), + remoteBlob2.toBuilder().contentType(CONTENT_TYPE).build()); + assertEquals(sourceBlob1.bucket(), updatedBlobs.get(0).bucket()); + assertEquals(sourceBlob1.name(), updatedBlobs.get(0).name()); + assertEquals(CONTENT_TYPE, updatedBlobs.get(0).contentType()); + assertEquals(sourceBlob2.bucket(), updatedBlobs.get(1).bucket()); + assertEquals(sourceBlob2.name(), updatedBlobs.get(1).name()); + assertEquals(CONTENT_TYPE, updatedBlobs.get(1).contentType()); + assertTrue(storage.delete(bucket, sourceBlobName1)); + assertTrue(storage.delete(bucket, sourceBlobName2)); + } + + @Test + public void testUpdateBlobsFail() { + String sourceBlobName1 = "test-update-blobs-fail-1"; + String sourceBlobName2 = "test-update-blobs-fail-2"; + BlobInfo sourceBlob1 = BlobInfo.of(bucket, sourceBlobName1); + BlobInfo sourceBlob2 = BlobInfo.of(bucket, sourceBlobName2); + BlobInfo remoteBlob1 = storage.create(sourceBlob1); + assertNotNull(remoteBlob1); + List updatedBlobs = storage.update( + remoteBlob1.toBuilder().contentType(CONTENT_TYPE).build(), + sourceBlob2.toBuilder().contentType(CONTENT_TYPE).build()); + assertEquals(sourceBlob1.bucket(), updatedBlobs.get(0).bucket()); + assertEquals(sourceBlob1.name(), updatedBlobs.get(0).name()); + assertEquals(CONTENT_TYPE, updatedBlobs.get(0).contentType()); + assertNull(updatedBlobs.get(1)); + assertTrue(storage.delete(bucket, sourceBlobName1)); + } } diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/StorageImplTest.java b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/StorageImplTest.java index 8c55f2cc4e44..21b877ba148a 100644 --- a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/StorageImplTest.java +++ b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/StorageImplTest.java @@ -857,6 +857,135 @@ public void testSignUrlWithOptions() throws NoSuchAlgorithmException, InvalidKey EasyMock.verify(credentialsMock); } + @Test + public void testGetAll() { + BlobInfo blobInfo1 = BlobInfo.of(BUCKET_NAME1, BLOB_NAME1); + BlobInfo blobInfo2 = BlobInfo.of(BUCKET_NAME1, BLOB_NAME2); + StorageObject storageObject1 = blobInfo1.toPb(); + StorageObject storageObject2 = blobInfo2.toPb(); + List toGet = ImmutableList.of(storageObject1, storageObject2); + + Map> deleteResult = ImmutableMap.of(); + Map> updateResult = ImmutableMap.of(); + Map> getResult = Maps.toMap(toGet, + new Function>() { + @Override + public Tuple apply(StorageObject f) { + return Tuple.of(f, null); + } + }); + StorageRpc.BatchResponse res = + new StorageRpc.BatchResponse(deleteResult, updateResult, getResult); + + EasyMock.expect(optionsMock.storageRpc()).andReturn(storageRpcMock); + Capture capturedBatchRequest = Capture.newInstance(); + EasyMock.expect(storageRpcMock.batch(EasyMock.capture(capturedBatchRequest))).andReturn(res); + EasyMock.replay(optionsMock, storageRpcMock); + storage = StorageFactory.instance().get(optionsMock); + List resultBlobs = storage.get(blobInfo1, blobInfo2); + + // Verify captured StorageRpc.BatchRequest + List>> capturedToGet = + capturedBatchRequest.getValue().toGet; + assertTrue(capturedBatchRequest.getValue().toDelete.isEmpty()); + assertTrue(capturedBatchRequest.getValue().toUpdate.isEmpty()); + for (int i = 0; i < capturedToGet.size(); i++) { + assertEquals(toGet.get(i), capturedToGet.get(i).x()); + assertTrue(capturedToGet.get(i).y().isEmpty()); + } + + // Verify result + for (int i = 0; i < resultBlobs.size(); i++) { + assertEquals(toGet.get(i), resultBlobs.get(i).toPb()); + } + } + + @Test + public void testUpdateAll() { + BlobInfo blobInfo1 = BlobInfo.builder(BUCKET_NAME1, BLOB_NAME1).contentType("type").build(); + BlobInfo blobInfo2 = BlobInfo.builder(BUCKET_NAME1, BLOB_NAME2).contentType("type").build(); + StorageObject storageObject1 = blobInfo1.toPb(); + StorageObject storageObject2 = blobInfo2.toPb(); + List toUpdate = ImmutableList.of(storageObject1, storageObject2); + + Map> deleteResult = ImmutableMap.of(); + Map> getResult = ImmutableMap.of(); + Map> updateResult = Maps.toMap(toUpdate, + new Function>() { + @Override + public Tuple apply(StorageObject f) { + return Tuple.of(f, null); + } + }); + StorageRpc.BatchResponse res = + new StorageRpc.BatchResponse(deleteResult, updateResult, getResult); + + EasyMock.expect(optionsMock.storageRpc()).andReturn(storageRpcMock); + Capture capturedBatchRequest = Capture.newInstance(); + EasyMock.expect(storageRpcMock.batch(EasyMock.capture(capturedBatchRequest))).andReturn(res); + EasyMock.replay(optionsMock, storageRpcMock); + storage = StorageFactory.instance().get(optionsMock); + List resultBlobs = storage.update(blobInfo1, blobInfo2); + + // Verify captured StorageRpc.BatchRequest + List>> capturedToUpdate = + capturedBatchRequest.getValue().toUpdate; + assertTrue(capturedBatchRequest.getValue().toDelete.isEmpty()); + assertTrue(capturedBatchRequest.getValue().toGet.isEmpty()); + for (int i = 0; i < capturedToUpdate.size(); i++) { + assertEquals(toUpdate.get(i), capturedToUpdate.get(i).x()); + assertTrue(capturedToUpdate.get(i).y().isEmpty()); + } + + // Verify result + for (int i = 0; i < resultBlobs.size(); i++) { + assertEquals(toUpdate.get(i), resultBlobs.get(i).toPb()); + } + } + + @Test + public void testDeleteAll() { + BlobInfo blobInfo1 = BlobInfo.builder(BUCKET_NAME1, BLOB_NAME1).contentType("type").build(); + BlobInfo blobInfo2 = BlobInfo.builder(BUCKET_NAME1, BLOB_NAME2).contentType("type").build(); + StorageObject storageObject1 = blobInfo1.toPb(); + StorageObject storageObject2 = blobInfo2.toPb(); + List toUpdate = ImmutableList.of(storageObject1, storageObject2); + + Map> updateResult = ImmutableMap.of(); + Map> getResult = ImmutableMap.of(); + Map> deleteResult = Maps.toMap(toUpdate, + new Function>() { + @Override + public Tuple apply(StorageObject f) { + return Tuple.of(true, null); + } + }); + StorageRpc.BatchResponse res = + new StorageRpc.BatchResponse(deleteResult, updateResult, getResult); + + EasyMock.expect(optionsMock.storageRpc()).andReturn(storageRpcMock); + Capture capturedBatchRequest = Capture.newInstance(); + EasyMock.expect(storageRpcMock.batch(EasyMock.capture(capturedBatchRequest))).andReturn(res); + EasyMock.replay(optionsMock, storageRpcMock); + storage = StorageFactory.instance().get(optionsMock); + List deleteResults = storage.delete(blobInfo1, blobInfo2); + + // Verify captured StorageRpc.BatchRequest + List>> capturedToDelete = + capturedBatchRequest.getValue().toDelete; + assertTrue(capturedBatchRequest.getValue().toUpdate.isEmpty()); + assertTrue(capturedBatchRequest.getValue().toGet.isEmpty()); + for (int i = 0; i < capturedToDelete.size(); i++) { + assertEquals(toUpdate.get(i), capturedToDelete.get(i).x()); + assertTrue(capturedToDelete.get(i).y().isEmpty()); + } + + // Verify result + for (Boolean deleteStatus : deleteResults) { + assertTrue(deleteStatus); + } + } + @Test public void testRetryableException() { BlobInfo blob = BlobInfo.of(BUCKET_NAME1, BLOB_NAME1); From b1918ca205078c37b249d4367d3d8b584e922ca0 Mon Sep 17 00:00:00 2001 From: Marco Ziccardi Date: Fri, 9 Oct 2015 14:43:38 +0200 Subject: [PATCH 2/6] Add batch get update and delete to Blob - add delete, update and get methods to Blob class - add tests to BlobTest --- .../java/com/google/gcloud/storage/Blob.java | 89 ++++++++++++ .../com/google/gcloud/storage/BlobTest.java | 131 ++++++++++++++++++ 2 files changed, 220 insertions(+) diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Blob.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Blob.java index e0863d23a70c..00396d4d2c2e 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Blob.java +++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Blob.java @@ -20,12 +20,17 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.gcloud.storage.Blob.BlobSourceOption.convert; +import com.google.common.base.Function; +import com.google.common.collect.Lists; import com.google.gcloud.spi.StorageRpc; import com.google.gcloud.storage.Storage.BlobTargetOption; import com.google.gcloud.storage.Storage.CopyRequest; import com.google.gcloud.storage.Storage.SignUrlOption; import java.net.URL; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; import java.util.Objects; /** @@ -256,4 +261,88 @@ public URL signUrl(long expirationTimeInSeconds, SignUrlOption... options) { public Storage storage() { return storage; } + + /** + * Gets the requested blobs. If {@code infos.length == 0} an empty list is returned. If + * {@code infos.length > 1} a batch request is used to fetch blobs. + * + * @param storage the storage service used to issue the request + * @param infos the blobs to get + * @return a list of {@code Blob} objects. If a blob does not exist the corresponding item in the + * list is {@code null}. + */ + public static List get(final Storage storage, BlobInfo... infos) { + checkNotNull(storage); + checkNotNull(infos); + int length = infos.length; + switch (length) { + case 0: + return Collections.emptyList(); + case 1: + return Collections.singletonList( + new Blob(storage, storage.get(infos[0].bucket(), infos[0].name()))); + default: + return Lists.transform( + storage.get(infos[0], infos[1], Arrays.copyOfRange(infos, 2, length)), + new Function() { + @Override + public Blob apply(BlobInfo f) { + return f != null ? new Blob(storage, f) : null; + } + }); + } + } + + /** + * Updates the requested blobs. If {@code infos.length == 0} an empty list is returned. If + * {@code infos.length > 1} a batch request is used to update blobs. + * + * @param storage the storage service used to issue the request + * @param infos the blobs to update + * @return a list of {@code Blob} objects. If a blob does not exist the corresponding item in the + * list is {@code null}. + */ + public static List update(final Storage storage, BlobInfo... infos) { + checkNotNull(storage); + checkNotNull(infos); + int length = infos.length; + switch (length) { + case 0: + return Collections.emptyList(); + case 1: + return Collections.singletonList(new Blob(storage, storage.update(infos[0]))); + default: + return Lists.transform( + storage.update(infos[0], infos[1], Arrays.copyOfRange(infos, 2, length)), + new Function() { + @Override + public Blob apply(BlobInfo f) { + return f != null ? new Blob(storage, f) : null; + } + }); + } + } + + /** + * Deletes the requested blobs. If {@code infos.length == 0} an empty list is returned. If + * {@code infos.length > 1} a batch request is used to delete blobs. + * + * @param storage the storage service used to issue the request + * @param infos the blobs to delete + * @return a list of booleans. If a blob has been deleted the corresponding item in the list is + * {@code true}. If deletion failed the item is {@code false}. + */ + public static List delete(Storage storage, BlobInfo... infos) { + checkNotNull(storage); + checkNotNull(infos); + int length = infos.length; + switch (length) { + case 0: + return Collections.emptyList(); + case 1: + return Collections.singletonList(storage.delete(infos[0].bucket(), infos[0].name())); + default: + return storage.delete(infos[0], infos[1], Arrays.copyOfRange(infos, 2, length)); + } + } } diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobTest.java b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobTest.java index 4923b8b772e8..5da9b945d885 100644 --- a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobTest.java +++ b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobTest.java @@ -25,19 +25,25 @@ import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; +import com.google.api.client.util.Lists; import com.google.gcloud.storage.Storage.CopyRequest; import org.easymock.Capture; import org.junit.After; import org.junit.Before; import org.junit.Test; import java.net.URL; +import java.util.Arrays; +import java.util.List; public class BlobTest { private static final BlobInfo BLOB_INFO = BlobInfo.of("b", "n"); + private static final BlobInfo[] BLOB_INFO_ARRAY = {BlobInfo.of("b1", "n1"), + BlobInfo.of("b2", "n2"), BlobInfo.of("b3", "n3")}; private Storage storage; private Blob blob; @@ -159,4 +165,129 @@ public void testSignUrl() throws Exception { replay(storage); assertEquals(url, blob.signUrl(100)); } + + @Test + public void testGetNone() throws Exception { + replay(storage); + assertTrue(Blob.get(storage).isEmpty()); + } + + @Test + public void testGetOne() throws Exception { + expect(storage.get(BLOB_INFO.bucket(), BLOB_INFO.name())).andReturn(BLOB_INFO); + replay(storage); + List result = Blob.get(storage, BLOB_INFO); + assertEquals(1, result.size()); + assertEquals(BLOB_INFO, result.get(0).info()); + } + + @Test + public void testGetSome() throws Exception { + List blobInfoList = Arrays.asList(BLOB_INFO_ARRAY); + expect(storage.get(BLOB_INFO_ARRAY[0], BLOB_INFO_ARRAY[1], + Arrays.copyOfRange(BLOB_INFO_ARRAY, 2, BLOB_INFO_ARRAY.length))).andReturn(blobInfoList); + replay(storage); + List result = Blob.get(storage, BLOB_INFO_ARRAY); + assertEquals(blobInfoList.size(), result.size()); + for (int i = 0; i < blobInfoList.size(); i++) { + assertEquals(blobInfoList.get(i), result.get(i).info()); + } + } + + @Test + public void testGetSomeNull() throws Exception { + List blobInfoList = Arrays.asList(BLOB_INFO_ARRAY[0], null, BLOB_INFO_ARRAY[2]); + expect(storage.get(BLOB_INFO_ARRAY[0], BLOB_INFO_ARRAY[1], + Arrays.copyOfRange(BLOB_INFO_ARRAY, 2, BLOB_INFO_ARRAY.length))).andReturn(blobInfoList); + replay(storage); + List result = Blob.get(storage, BLOB_INFO_ARRAY); + assertEquals(blobInfoList.size(), result.size()); + for (int i = 0; i < blobInfoList.size(); i++) { + if (blobInfoList.get(i) != null) { + assertEquals(blobInfoList.get(i), result.get(i).info()); + } else { + assertNull(result.get(i)); + } + } + } + + @Test + public void testUpdateNone() throws Exception { + replay(storage); + assertTrue(Blob.update(storage).isEmpty()); + } + + @Test + public void testUpdateOne() throws Exception { + BlobInfo updatedBlob = BLOB_INFO.toBuilder().contentType("content").build(); + expect(storage.update(BLOB_INFO)).andReturn(updatedBlob); + replay(storage); + List result = Blob.update(storage, BLOB_INFO); + assertEquals(1, result.size()); + assertEquals(updatedBlob, result.get(0).info()); + } + + @Test + public void testUpdateSome() throws Exception { + List blobInfoList = Lists.newArrayListWithCapacity(BLOB_INFO_ARRAY.length); + for (BlobInfo info : BLOB_INFO_ARRAY) { + blobInfoList.add(info.toBuilder().contentType("content").build()); + } + expect(storage.update(BLOB_INFO_ARRAY[0], BLOB_INFO_ARRAY[1], + Arrays.copyOfRange(BLOB_INFO_ARRAY, 2, BLOB_INFO_ARRAY.length))).andReturn(blobInfoList); + replay(storage); + List result = Blob.update(storage, BLOB_INFO_ARRAY); + assertEquals(blobInfoList.size(), result.size()); + for (int i = 0; i < blobInfoList.size(); i++) { + assertEquals(blobInfoList.get(i), result.get(i).info()); + } + } + + @Test + public void testUpdateSomeNull() throws Exception { + List blobInfoList = Arrays.asList( + BLOB_INFO_ARRAY[0].toBuilder().contentType("content").build(), null, + BLOB_INFO_ARRAY[2].toBuilder().contentType("content").build()); + expect(storage.update(BLOB_INFO_ARRAY[0], BLOB_INFO_ARRAY[1], + Arrays.copyOfRange(BLOB_INFO_ARRAY, 2, BLOB_INFO_ARRAY.length))).andReturn(blobInfoList); + replay(storage); + List result = Blob.update(storage, BLOB_INFO_ARRAY); + assertEquals(blobInfoList.size(), result.size()); + for (int i = 0; i < blobInfoList.size(); i++) { + if (blobInfoList.get(i) != null) { + assertEquals(blobInfoList.get(i), result.get(i).info()); + } else { + assertNull(result.get(i)); + } + } + } + + @Test + public void testDeleteNone() throws Exception { + replay(storage); + assertTrue(Blob.delete(storage).isEmpty()); + } + + @Test + public void testDeleteOne() throws Exception { + expect(storage.delete(BLOB_INFO.bucket(), BLOB_INFO.name())).andReturn(true); + replay(storage); + List result = Blob.delete(storage, BLOB_INFO); + assertEquals(1, result.size()); + assertTrue(result.get(0)); + } + + @Test + public void testDeleteSome() throws Exception { + List deleleResultList = Arrays.asList(true, true, true); + expect(storage.delete(BLOB_INFO_ARRAY[0], BLOB_INFO_ARRAY[1], + Arrays.copyOfRange(BLOB_INFO_ARRAY, 2, BLOB_INFO_ARRAY.length))) + .andReturn(deleleResultList); + replay(storage); + List result = Blob.delete(storage, BLOB_INFO_ARRAY); + assertEquals(deleleResultList.size(), result.size()); + for (int i = 0; i < deleleResultList.size(); i++) { + assertEquals(deleleResultList.get(i), result.get(i)); + } + } } From c552edebd9bcd349366d45875a9e9d8d35db2514 Mon Sep 17 00:00:00 2001 From: Marco Ziccardi Date: Mon, 12 Oct 2015 07:38:21 +0200 Subject: [PATCH 3/6] Rename getAll in Bucket to get, return immutable list --- .../java/com/google/gcloud/storage/Bucket.java | 16 +++++++++++----- .../com/google/gcloud/storage/BucketTest.java | 2 +- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Bucket.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Bucket.java index d7cac447d051..95cc5f262e03 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Bucket.java +++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Bucket.java @@ -25,6 +25,7 @@ import com.google.gcloud.storage.Storage.BucketTargetOption; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Objects; @@ -146,13 +147,18 @@ public Blob get(String blob, BlobSourceOption... options) { /** * Returns a list of requested blobs in this bucket. Blobs that do not exist are null. * - * @param blobNames names of the requested blobs + * @param blobName1 first blob to get + * @param blobName2 second blob to get + * @param blobNames other blobs to get + * @return an immutable list of {@code Blob} objects. * @throws StorageException upon failure */ - public List getAll(String... blobNames) { + public List get(String blobName1, String blobName2, String... blobNames) { BatchRequest.Builder batch = BatchRequest.builder(); - for (String blobName : blobNames) { - batch.get(info.name(), blobName); + batch.get(info.name(), blobName1); + batch.get(info.name(), blobName2); + for (String name : blobNames) { + batch.get(info.name(), name); } List blobs = new ArrayList<>(blobNames.length); BatchResponse response = storage.apply(batch.build()); @@ -160,7 +166,7 @@ public List getAll(String... blobNames) { BlobInfo blobInfo = result.get(); blobs.add(blobInfo != null ? new Blob(storage, blobInfo) : null); } - return blobs; + return Collections.unmodifiableList(blobs); } /** diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BucketTest.java b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BucketTest.java index 9b8546ecc27d..5fddc23691d6 100644 --- a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BucketTest.java +++ b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BucketTest.java @@ -143,7 +143,7 @@ public void testGetAll() throws Exception { new BatchResponse(Collections.EMPTY_LIST, Collections.EMPTY_LIST, batchResultList); expect(storage.apply(capture(capturedBatchRequest))).andReturn(response); replay(storage); - List blobs = bucket.getAll("n1", "n2", "n3"); + List blobs = bucket.get("n1", "n2", "n3"); Set blobInfoSet = capturedBatchRequest.getValue().toGet().keySet(); assertEquals(batchResultList.size(), blobInfoSet.size()); for (BlobInfo info : BLOB_INFO_RESULTS) { From 5bf23b65940772264658c065afbc565398bce47f Mon Sep 17 00:00:00 2001 From: Marco Ziccardi Date: Mon, 12 Oct 2015 09:45:38 +0200 Subject: [PATCH 4/6] Refactor Blob/Storage batch get, update and delete - Storage get, update and delete now use batch in service layer - Blob/Storage get, update and delete always return immutable list - Update javadocs with info on list immutability and access denied error --- .../java/com/google/gcloud/storage/Blob.java | 24 ++++--- .../com/google/gcloud/storage/Storage.java | 13 ++-- .../google/gcloud/storage/StorageImpl.java | 67 +++++++------------ .../gcloud/storage/StorageImplTest.java | 4 +- 4 files changed, 48 insertions(+), 60 deletions(-) diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Blob.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Blob.java index 00396d4d2c2e..503bd47fa9ba 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Blob.java +++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Blob.java @@ -268,8 +268,8 @@ public Storage storage() { * * @param storage the storage service used to issue the request * @param infos the blobs to get - * @return a list of {@code Blob} objects. If a blob does not exist the corresponding item in the - * list is {@code null}. + * @return an immutable list of {@code Blob} objects. If a blob does not exist or access to it has + * been denied the corresponding item in the list is {@code null}. */ public static List get(final Storage storage, BlobInfo... infos) { checkNotNull(storage); @@ -282,14 +282,14 @@ public static List get(final Storage storage, BlobInfo... infos) { return Collections.singletonList( new Blob(storage, storage.get(infos[0].bucket(), infos[0].name()))); default: - return Lists.transform( + return Collections.unmodifiableList(Lists.transform( storage.get(infos[0], infos[1], Arrays.copyOfRange(infos, 2, length)), new Function() { @Override public Blob apply(BlobInfo f) { return f != null ? new Blob(storage, f) : null; } - }); + })); } } @@ -299,8 +299,8 @@ public Blob apply(BlobInfo f) { * * @param storage the storage service used to issue the request * @param infos the blobs to update - * @return a list of {@code Blob} objects. If a blob does not exist the corresponding item in the - * list is {@code null}. + * @return an immutable list of {@code Blob} objects. If a blob does not exist or access to it has + * been denied the corresponding item in the list is {@code null}. */ public static List update(final Storage storage, BlobInfo... infos) { checkNotNull(storage); @@ -312,14 +312,14 @@ public static List update(final Storage storage, BlobInfo... infos) { case 1: return Collections.singletonList(new Blob(storage, storage.update(infos[0]))); default: - return Lists.transform( + return Collections.unmodifiableList(Lists.transform( storage.update(infos[0], infos[1], Arrays.copyOfRange(infos, 2, length)), new Function() { @Override public Blob apply(BlobInfo f) { return f != null ? new Blob(storage, f) : null; } - }); + })); } } @@ -329,8 +329,9 @@ public Blob apply(BlobInfo f) { * * @param storage the storage service used to issue the request * @param infos the blobs to delete - * @return a list of booleans. If a blob has been deleted the corresponding item in the list is - * {@code true}. If deletion failed the item is {@code false}. + * @return an immutable list of booleans. If a blob has been deleted the corresponding item in the + * list is {@code true}. If deletion failed or access to the resource was denied the item is + * {@code false}. */ public static List delete(Storage storage, BlobInfo... infos) { checkNotNull(storage); @@ -342,7 +343,8 @@ public static List delete(Storage storage, BlobInfo... infos) { case 1: return Collections.singletonList(storage.delete(infos[0].bucket(), infos[0].name())); default: - return storage.delete(infos[0], infos[1], Arrays.copyOfRange(infos, 2, length)); + return Collections.unmodifiableList( + storage.delete(infos[0], infos[1], Arrays.copyOfRange(infos, 2, length))); } } } diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Storage.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Storage.java index 8b43410bc6a3..076c1b9de16e 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Storage.java +++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Storage.java @@ -645,8 +645,8 @@ public static Builder builder() { * @param blobInfo1 first blob to get * @param blobInfo2 second blob to get * @param blobInfos other blobs to get - * @return a list of {@code BlobInfo} objects. If a blob does not exist the corresponding item in - * the list is {@code null}. + * @return an immutable list of {@code BlobInfo} objects. If a blob does not exist or access to it + * has been denied the corresponding item in the list is {@code null}. */ List get(BlobInfo blobInfo1, BlobInfo blobInfo2, BlobInfo... blobInfos); @@ -656,8 +656,8 @@ public static Builder builder() { * @param blobInfo1 first blob to update * @param blobInfo2 second blob to update * @param blobInfos other blobs to update - * @return a list of {@code BlobInfo} objects. If a blob does not exist the corresponding item in - * the list is {@code null}. + * @return an immutable list of {@code BlobInfo} objects. If a blob does not exist or access to it + * has been denied the corresponding item in the list is {@code null}. */ List update(BlobInfo blobInfo1, BlobInfo blobInfo2, BlobInfo... blobInfos); @@ -667,8 +667,9 @@ public static Builder builder() { * @param blobInfo1 first blob to delete * @param blobInfo2 second blob to delete * @param blobInfos other blobs to delete - * @return a list of booleans. If a blob has been deleted the corresponding item in the list is - * {@code true}. If deletion failed the item is {@code false}. + * @return an immutable list of booleans. If a blob has been deleted the corresponding item in the + * list is {@code true}. If deletion failed or access to the resource was denied the item is + * {@code false}. */ List delete(BlobInfo blobInfo1, BlobInfo blobInfo2, BlobInfo... blobInfos); } diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageImpl.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageImpl.java index 1ec39e3f8aca..ea5daab052f6 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageImpl.java +++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageImpl.java @@ -62,6 +62,7 @@ import java.security.Signature; import java.security.SignatureException; import java.util.Arrays; +import java.util.Collections; import java.util.EnumMap; import java.util.List; import java.util.Map; @@ -579,64 +580,48 @@ public URL signUrl(BlobInfo blobInfo, long expiration, SignUrlOption... options) @Override public List get(BlobInfo blobInfo1, BlobInfo blobInfo2, BlobInfo... blobInfos) { - List>> toGet = - Lists.newArrayListWithCapacity(blobInfos.length + 2); - toGet.add(Tuple.>of(blobInfo1.toPb(), optionMap())); - toGet.add(Tuple.>of(blobInfo2.toPb(), optionMap())); + BatchRequest.Builder requestBuilder = BatchRequest.builder(); + requestBuilder.get(blobInfo1.bucket(), blobInfo1.name()); + requestBuilder.get(blobInfo2.bucket(), blobInfo2.name()); for (BlobInfo blobInfo : blobInfos) { - toGet.add(Tuple.>of(blobInfo.toPb(), optionMap())); + requestBuilder.get(blobInfo.bucket(), blobInfo.name()); } - StorageRpc.BatchResponse response = - storageRpc.batch(new StorageRpc.BatchRequest(null, null, toGet)); - return transformBatchResult(toGet, response.gets, BlobInfo.FROM_PB_FUNCTION, (BlobInfo) null); + BatchResponse response = apply(requestBuilder.build()); + return Collections.unmodifiableList(transformResultList(response.gets(), null)); } @Override public List update(BlobInfo blobInfo1, BlobInfo blobInfo2, BlobInfo... blobInfos) { - List>> toUpdate = - Lists.newArrayListWithCapacity(blobInfos.length + 2); - toUpdate.add(Tuple.>of(blobInfo1.toPb(), optionMap())); - toUpdate.add(Tuple.>of(blobInfo2.toPb(), optionMap())); + BatchRequest.Builder requestBuilder = BatchRequest.builder(); + requestBuilder.update(blobInfo1); + requestBuilder.update(blobInfo2); for (BlobInfo blobInfo : blobInfos) { - toUpdate - .add(Tuple.>of(blobInfo.toPb(), optionMap())); + requestBuilder.update(blobInfo); } - StorageRpc.BatchResponse response = - storageRpc.batch(new StorageRpc.BatchRequest(null, toUpdate, null)); - return transformBatchResult(toUpdate, response.updates, BlobInfo.FROM_PB_FUNCTION, - (BlobInfo) null); + BatchResponse response = apply(requestBuilder.build()); + return Collections.unmodifiableList(transformResultList(response.updates(), null)); } @Override public List delete(BlobInfo blobInfo1, BlobInfo blobInfo2, BlobInfo... blobInfos) { - List>> toDelete = - Lists.newArrayListWithCapacity(blobInfos.length + 2); - toDelete.add(Tuple.>of(blobInfo1.toPb(), optionMap())); - toDelete.add(Tuple.>of(blobInfo2.toPb(), optionMap())); + BatchRequest.Builder requestBuilder = BatchRequest.builder(); + requestBuilder.delete(blobInfo1.bucket(), blobInfo1.name()); + requestBuilder.delete(blobInfo2.bucket(), blobInfo2.name()); for (BlobInfo blobInfo : blobInfos) { - toDelete - .add(Tuple.>of(blobInfo.toPb(), optionMap())); + requestBuilder.delete(blobInfo.bucket(), blobInfo.name()); } - StorageRpc.BatchResponse response = - storageRpc.batch(new StorageRpc.BatchRequest(toDelete, null, null)); - return transformBatchResult(toDelete, response.deletes, Functions.identity(), - Boolean.FALSE); + BatchResponse response = apply(requestBuilder.build()); + return Collections.unmodifiableList(transformResultList(response.deletes(), Boolean.FALSE)); } - private List transformBatchResult( - Iterable>> request, - Map> results, Function transform, - O errorValue) { - List response = Lists.newArrayListWithCapacity(results.size()); - for (Tuple tuple : request) { - Tuple result = results.get(tuple.x()); - if (result.x() != null) { - response.add(transform.apply(result.x())); - } else { - response.add(errorValue); + private static List transformResultList( + List> results, final T errorValue) { + return Lists.transform(results, new Function, T>() { + @Override + public T apply(BatchResponse.Result f) { + return f.failed() ? errorValue : f.get(); } - } - return response; + }); } private Map optionMap(Long generation, Long metaGeneration, diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/StorageImplTest.java b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/StorageImplTest.java index 21b877ba148a..fa0daa976eb0 100644 --- a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/StorageImplTest.java +++ b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/StorageImplTest.java @@ -945,8 +945,8 @@ public Tuple apply(StorageObject f) { @Test public void testDeleteAll() { - BlobInfo blobInfo1 = BlobInfo.builder(BUCKET_NAME1, BLOB_NAME1).contentType("type").build(); - BlobInfo blobInfo2 = BlobInfo.builder(BUCKET_NAME1, BLOB_NAME2).contentType("type").build(); + BlobInfo blobInfo1 = BlobInfo.builder(BUCKET_NAME1, BLOB_NAME1).build(); + BlobInfo blobInfo2 = BlobInfo.builder(BUCKET_NAME1, BLOB_NAME2).build(); StorageObject storageObject1 = blobInfo1.toPb(); StorageObject storageObject2 = blobInfo2.toPb(); List toUpdate = ImmutableList.of(storageObject1, storageObject2); From f1c6b04fe2701dfd9f05ab24dad117db5bf046a0 Mon Sep 17 00:00:00 2001 From: Marco Ziccardi Date: Mon, 12 Oct 2015 18:38:00 +0200 Subject: [PATCH 5/6] Add storage.update(blobInfo) and update batch methods params --- .../java/com/google/gcloud/storage/Blob.java | 9 +++---- .../com/google/gcloud/storage/Storage.java | 26 ++++++++++--------- .../google/gcloud/storage/StorageImpl.java | 17 ++++++------ .../com/google/gcloud/storage/BlobTest.java | 18 +++++-------- 4 files changed, 31 insertions(+), 39 deletions(-) diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Blob.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Blob.java index 503bd47fa9ba..4cd2ee777e72 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Blob.java +++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Blob.java @@ -282,8 +282,7 @@ public static List get(final Storage storage, BlobInfo... infos) { return Collections.singletonList( new Blob(storage, storage.get(infos[0].bucket(), infos[0].name()))); default: - return Collections.unmodifiableList(Lists.transform( - storage.get(infos[0], infos[1], Arrays.copyOfRange(infos, 2, length)), + return Collections.unmodifiableList(Lists.transform(storage.get(infos), new Function() { @Override public Blob apply(BlobInfo f) { @@ -312,8 +311,7 @@ public static List update(final Storage storage, BlobInfo... infos) { case 1: return Collections.singletonList(new Blob(storage, storage.update(infos[0]))); default: - return Collections.unmodifiableList(Lists.transform( - storage.update(infos[0], infos[1], Arrays.copyOfRange(infos, 2, length)), + return Collections.unmodifiableList(Lists.transform(storage.update(infos), new Function() { @Override public Blob apply(BlobInfo f) { @@ -343,8 +341,7 @@ public static List delete(Storage storage, BlobInfo... infos) { case 1: return Collections.singletonList(storage.delete(infos[0].bucket(), infos[0].name())); default: - return Collections.unmodifiableList( - storage.delete(infos[0], infos[1], Arrays.copyOfRange(infos, 2, length))); + return Collections.unmodifiableList(storage.delete(infos)); } } } diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Storage.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Storage.java index 076c1b9de16e..56a4f97b9975 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Storage.java +++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Storage.java @@ -563,6 +563,14 @@ public static Builder builder() { */ BlobInfo update(BlobInfo blobInfo, BlobTargetOption... options); + /** + * Update blob information. + * + * @return the updated blob + * @throws StorageException upon failure + */ + BlobInfo update(BlobInfo blobInfo); + /** * Delete the requested bucket. * @@ -642,34 +650,28 @@ public static Builder builder() { /** * Gets the requested blobs. A batch request is used to perform this call. * - * @param blobInfo1 first blob to get - * @param blobInfo2 second blob to get - * @param blobInfos other blobs to get + * @param blobInfos blobs to get * @return an immutable list of {@code BlobInfo} objects. If a blob does not exist or access to it * has been denied the corresponding item in the list is {@code null}. */ - List get(BlobInfo blobInfo1, BlobInfo blobInfo2, BlobInfo... blobInfos); + List get(BlobInfo... blobInfos); /** * Updates the requested blobs. A batch request is used to perform this call. * - * @param blobInfo1 first blob to update - * @param blobInfo2 second blob to update - * @param blobInfos other blobs to update + * @param blobInfos blobs to update * @return an immutable list of {@code BlobInfo} objects. If a blob does not exist or access to it * has been denied the corresponding item in the list is {@code null}. */ - List update(BlobInfo blobInfo1, BlobInfo blobInfo2, BlobInfo... blobInfos); + List update(BlobInfo... blobInfos); /** * Deletes the requested blobs. A batch request is used to perform this call. * - * @param blobInfo1 first blob to delete - * @param blobInfo2 second blob to delete - * @param blobInfos other blobs to delete + * @param blobInfos blobs to delete * @return an immutable list of booleans. If a blob has been deleted the corresponding item in the * list is {@code true}. If deletion failed or access to the resource was denied the item is * {@code false}. */ - List delete(BlobInfo blobInfo1, BlobInfo blobInfo2, BlobInfo... blobInfos); + List delete(BlobInfo... blobInfos); } diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageImpl.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageImpl.java index ea5daab052f6..6e32220746ca 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageImpl.java +++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageImpl.java @@ -349,6 +349,11 @@ public StorageObject call() { } } + @Override + public BlobInfo update(BlobInfo blobInfo) { + return update(blobInfo, new BlobTargetOption[0]); + } + @Override public boolean delete(String bucket, BucketSourceOption... options) { final com.google.api.services.storage.model.Bucket bucketPb = BucketInfo.of(bucket).toPb(); @@ -579,10 +584,8 @@ public URL signUrl(BlobInfo blobInfo, long expiration, SignUrlOption... options) } @Override - public List get(BlobInfo blobInfo1, BlobInfo blobInfo2, BlobInfo... blobInfos) { + public List get(BlobInfo... blobInfos) { BatchRequest.Builder requestBuilder = BatchRequest.builder(); - requestBuilder.get(blobInfo1.bucket(), blobInfo1.name()); - requestBuilder.get(blobInfo2.bucket(), blobInfo2.name()); for (BlobInfo blobInfo : blobInfos) { requestBuilder.get(blobInfo.bucket(), blobInfo.name()); } @@ -591,10 +594,8 @@ public List get(BlobInfo blobInfo1, BlobInfo blobInfo2, BlobInfo... bl } @Override - public List update(BlobInfo blobInfo1, BlobInfo blobInfo2, BlobInfo... blobInfos) { + public List update(BlobInfo... blobInfos) { BatchRequest.Builder requestBuilder = BatchRequest.builder(); - requestBuilder.update(blobInfo1); - requestBuilder.update(blobInfo2); for (BlobInfo blobInfo : blobInfos) { requestBuilder.update(blobInfo); } @@ -603,10 +604,8 @@ public List update(BlobInfo blobInfo1, BlobInfo blobInfo2, BlobInfo... } @Override - public List delete(BlobInfo blobInfo1, BlobInfo blobInfo2, BlobInfo... blobInfos) { + public List delete(BlobInfo... blobInfos) { BatchRequest.Builder requestBuilder = BatchRequest.builder(); - requestBuilder.delete(blobInfo1.bucket(), blobInfo1.name()); - requestBuilder.delete(blobInfo2.bucket(), blobInfo2.name()); for (BlobInfo blobInfo : blobInfos) { requestBuilder.delete(blobInfo.bucket(), blobInfo.name()); } diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobTest.java b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobTest.java index 5da9b945d885..77b8bb614872 100644 --- a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobTest.java +++ b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobTest.java @@ -100,7 +100,7 @@ public void testReload() throws Exception { @Test public void testUpdate() throws Exception { BlobInfo updatedInfo = BLOB_INFO.toBuilder().cacheControl("c").build(); - expect(storage.update(updatedInfo)).andReturn(updatedInfo); + expect(storage.update(updatedInfo, new Storage.BlobTargetOption[0])).andReturn(updatedInfo); replay(storage); Blob updatedBlob = blob.update(updatedInfo); assertSame(storage, blob.storage()); @@ -184,8 +184,7 @@ public void testGetOne() throws Exception { @Test public void testGetSome() throws Exception { List blobInfoList = Arrays.asList(BLOB_INFO_ARRAY); - expect(storage.get(BLOB_INFO_ARRAY[0], BLOB_INFO_ARRAY[1], - Arrays.copyOfRange(BLOB_INFO_ARRAY, 2, BLOB_INFO_ARRAY.length))).andReturn(blobInfoList); + expect(storage.get(BLOB_INFO_ARRAY)).andReturn(blobInfoList); replay(storage); List result = Blob.get(storage, BLOB_INFO_ARRAY); assertEquals(blobInfoList.size(), result.size()); @@ -197,8 +196,7 @@ public void testGetSome() throws Exception { @Test public void testGetSomeNull() throws Exception { List blobInfoList = Arrays.asList(BLOB_INFO_ARRAY[0], null, BLOB_INFO_ARRAY[2]); - expect(storage.get(BLOB_INFO_ARRAY[0], BLOB_INFO_ARRAY[1], - Arrays.copyOfRange(BLOB_INFO_ARRAY, 2, BLOB_INFO_ARRAY.length))).andReturn(blobInfoList); + expect(storage.get(BLOB_INFO_ARRAY)).andReturn(blobInfoList); replay(storage); List result = Blob.get(storage, BLOB_INFO_ARRAY); assertEquals(blobInfoList.size(), result.size()); @@ -233,8 +231,7 @@ public void testUpdateSome() throws Exception { for (BlobInfo info : BLOB_INFO_ARRAY) { blobInfoList.add(info.toBuilder().contentType("content").build()); } - expect(storage.update(BLOB_INFO_ARRAY[0], BLOB_INFO_ARRAY[1], - Arrays.copyOfRange(BLOB_INFO_ARRAY, 2, BLOB_INFO_ARRAY.length))).andReturn(blobInfoList); + expect(storage.update(BLOB_INFO_ARRAY)).andReturn(blobInfoList); replay(storage); List result = Blob.update(storage, BLOB_INFO_ARRAY); assertEquals(blobInfoList.size(), result.size()); @@ -248,8 +245,7 @@ public void testUpdateSomeNull() throws Exception { List blobInfoList = Arrays.asList( BLOB_INFO_ARRAY[0].toBuilder().contentType("content").build(), null, BLOB_INFO_ARRAY[2].toBuilder().contentType("content").build()); - expect(storage.update(BLOB_INFO_ARRAY[0], BLOB_INFO_ARRAY[1], - Arrays.copyOfRange(BLOB_INFO_ARRAY, 2, BLOB_INFO_ARRAY.length))).andReturn(blobInfoList); + expect(storage.update(BLOB_INFO_ARRAY)).andReturn(blobInfoList); replay(storage); List result = Blob.update(storage, BLOB_INFO_ARRAY); assertEquals(blobInfoList.size(), result.size()); @@ -280,9 +276,7 @@ public void testDeleteOne() throws Exception { @Test public void testDeleteSome() throws Exception { List deleleResultList = Arrays.asList(true, true, true); - expect(storage.delete(BLOB_INFO_ARRAY[0], BLOB_INFO_ARRAY[1], - Arrays.copyOfRange(BLOB_INFO_ARRAY, 2, BLOB_INFO_ARRAY.length))) - .andReturn(deleleResultList); + expect(storage.delete(BLOB_INFO_ARRAY)).andReturn(deleleResultList); replay(storage); List result = Blob.delete(storage, BLOB_INFO_ARRAY); assertEquals(deleleResultList.size(), result.size()); From f2b6adc735e6b96d1ba0422393e0c0c121118764 Mon Sep 17 00:00:00 2001 From: Marco Ziccardi Date: Mon, 12 Oct 2015 23:23:55 +0200 Subject: [PATCH 6/6] Remove case for 1 item array in Blob batch methods, add @throws to docs --- .../java/com/google/gcloud/storage/Blob.java | 61 ++++++++----------- .../com/google/gcloud/storage/Storage.java | 3 + .../com/google/gcloud/storage/BlobTest.java | 28 --------- 3 files changed, 27 insertions(+), 65 deletions(-) diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Blob.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Blob.java index 4cd2ee777e72..189ae2ad3065 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Blob.java +++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Blob.java @@ -270,26 +270,21 @@ public Storage storage() { * @param infos the blobs to get * @return an immutable list of {@code Blob} objects. If a blob does not exist or access to it has * been denied the corresponding item in the list is {@code null}. + * @throws StorageException upon failure */ public static List get(final Storage storage, BlobInfo... infos) { checkNotNull(storage); checkNotNull(infos); - int length = infos.length; - switch (length) { - case 0: - return Collections.emptyList(); - case 1: - return Collections.singletonList( - new Blob(storage, storage.get(infos[0].bucket(), infos[0].name()))); - default: - return Collections.unmodifiableList(Lists.transform(storage.get(infos), - new Function() { - @Override - public Blob apply(BlobInfo f) { - return f != null ? new Blob(storage, f) : null; - } - })); + if (infos.length == 0) { + return Collections.emptyList(); } + return Collections.unmodifiableList(Lists.transform(storage.get(infos), + new Function() { + @Override + public Blob apply(BlobInfo f) { + return f != null ? new Blob(storage, f) : null; + } + })); } /** @@ -300,25 +295,21 @@ public Blob apply(BlobInfo f) { * @param infos the blobs to update * @return an immutable list of {@code Blob} objects. If a blob does not exist or access to it has * been denied the corresponding item in the list is {@code null}. + * @throws StorageException upon failure */ public static List update(final Storage storage, BlobInfo... infos) { checkNotNull(storage); checkNotNull(infos); - int length = infos.length; - switch (length) { - case 0: - return Collections.emptyList(); - case 1: - return Collections.singletonList(new Blob(storage, storage.update(infos[0]))); - default: - return Collections.unmodifiableList(Lists.transform(storage.update(infos), - new Function() { - @Override - public Blob apply(BlobInfo f) { - return f != null ? new Blob(storage, f) : null; - } - })); + if (infos.length == 0) { + return Collections.emptyList(); } + return Collections.unmodifiableList(Lists.transform(storage.update(infos), + new Function() { + @Override + public Blob apply(BlobInfo f) { + return f != null ? new Blob(storage, f) : null; + } + })); } /** @@ -330,18 +321,14 @@ public Blob apply(BlobInfo f) { * @return an immutable list of booleans. If a blob has been deleted the corresponding item in the * list is {@code true}. If deletion failed or access to the resource was denied the item is * {@code false}. + * @throws StorageException upon failure */ public static List delete(Storage storage, BlobInfo... infos) { checkNotNull(storage); checkNotNull(infos); - int length = infos.length; - switch (length) { - case 0: - return Collections.emptyList(); - case 1: - return Collections.singletonList(storage.delete(infos[0].bucket(), infos[0].name())); - default: - return Collections.unmodifiableList(storage.delete(infos)); + if (infos.length == 0) { + return Collections.emptyList(); } + return storage.delete(infos); } } diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Storage.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Storage.java index 56a4f97b9975..a980f0ed3fbf 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Storage.java +++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Storage.java @@ -653,6 +653,7 @@ public static Builder builder() { * @param blobInfos blobs to get * @return an immutable list of {@code BlobInfo} objects. If a blob does not exist or access to it * has been denied the corresponding item in the list is {@code null}. + * @throws StorageException upon failure */ List get(BlobInfo... blobInfos); @@ -662,6 +663,7 @@ public static Builder builder() { * @param blobInfos blobs to update * @return an immutable list of {@code BlobInfo} objects. If a blob does not exist or access to it * has been denied the corresponding item in the list is {@code null}. + * @throws StorageException upon failure */ List update(BlobInfo... blobInfos); @@ -672,6 +674,7 @@ public static Builder builder() { * @return an immutable list of booleans. If a blob has been deleted the corresponding item in the * list is {@code true}. If deletion failed or access to the resource was denied the item is * {@code false}. + * @throws StorageException upon failure */ List delete(BlobInfo... blobInfos); } diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobTest.java b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobTest.java index 77b8bb614872..ece24eeacd1e 100644 --- a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobTest.java +++ b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobTest.java @@ -172,15 +172,6 @@ public void testGetNone() throws Exception { assertTrue(Blob.get(storage).isEmpty()); } - @Test - public void testGetOne() throws Exception { - expect(storage.get(BLOB_INFO.bucket(), BLOB_INFO.name())).andReturn(BLOB_INFO); - replay(storage); - List result = Blob.get(storage, BLOB_INFO); - assertEquals(1, result.size()); - assertEquals(BLOB_INFO, result.get(0).info()); - } - @Test public void testGetSome() throws Exception { List blobInfoList = Arrays.asList(BLOB_INFO_ARRAY); @@ -215,16 +206,6 @@ public void testUpdateNone() throws Exception { assertTrue(Blob.update(storage).isEmpty()); } - @Test - public void testUpdateOne() throws Exception { - BlobInfo updatedBlob = BLOB_INFO.toBuilder().contentType("content").build(); - expect(storage.update(BLOB_INFO)).andReturn(updatedBlob); - replay(storage); - List result = Blob.update(storage, BLOB_INFO); - assertEquals(1, result.size()); - assertEquals(updatedBlob, result.get(0).info()); - } - @Test public void testUpdateSome() throws Exception { List blobInfoList = Lists.newArrayListWithCapacity(BLOB_INFO_ARRAY.length); @@ -264,15 +245,6 @@ public void testDeleteNone() throws Exception { assertTrue(Blob.delete(storage).isEmpty()); } - @Test - public void testDeleteOne() throws Exception { - expect(storage.delete(BLOB_INFO.bucket(), BLOB_INFO.name())).andReturn(true); - replay(storage); - List result = Blob.delete(storage, BLOB_INFO); - assertEquals(1, result.size()); - assertTrue(result.get(0)); - } - @Test public void testDeleteSome() throws Exception { List deleleResultList = Arrays.asList(true, true, true);