From af171147208aeade287e2cf81794f922bf147b1a Mon Sep 17 00:00:00 2001 From: aozarov Date: Thu, 9 Jul 2015 16:09:42 -0700 Subject: [PATCH 01/12] initial work on functional blob/bucket --- .../java/com/google/gcloud/storage/Blob.java | 195 ++++++++++++++++++ .../com/google/gcloud/storage/BlobInfo.java | 58 +++--- .../com/google/gcloud/storage/Bucket.java | 89 ++++++++ .../com/google/gcloud/storage/BucketInfo.java | 157 +++++++------- .../com/google/gcloud/storage/Storage.java | 43 ++-- .../com/google/gcloud/storage/BlobTest.java | 131 ++++++++++++ 6 files changed, 539 insertions(+), 134 deletions(-) create mode 100644 gcloud-java-storage/src/main/java/com/google/gcloud/storage/Blob.java create mode 100644 gcloud-java-storage/src/main/java/com/google/gcloud/storage/Bucket.java create mode 100644 gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobTest.java 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 new file mode 100644 index 000000000000..40ffd8235ba0 --- /dev/null +++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Blob.java @@ -0,0 +1,195 @@ +/* + * + * * Copyright 2015 Google Inc. All Rights Reserved. * * Licensed 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 com.google.gcloud.storage; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.gcloud.storage.Blob.BlobSourceOption.convert; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +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.Objects; + + +/** + * A Google cloud storage object. + */ +public final class Blob { + + private final Storage storage; + private BlobInfo info; + + public static class BlobSourceOption extends Option { + + private static final long serialVersionUID = 214616862061934846L; + + private BlobSourceOption(StorageRpc.Option rpcOption) { + super(rpcOption, null); + } + + private Storage.BlobSourceOption convert(BlobInfo blobInfo) { + switch (rpcOption()) { + case IF_GENERATION_MATCH: + return Storage.BlobSourceOption.generationMatch(blobInfo.generation()); + case IF_GENERATION_NOT_MATCH: + return Storage.BlobSourceOption.generationNotMatch(blobInfo.generation()); + case IF_METAGENERATION_MATCH: + return Storage.BlobSourceOption.metagenerationMatch(blobInfo.metageneration()); + case IF_METAGENERATION_NOT_MATCH: + return Storage.BlobSourceOption.metagenerationNotMatch(blobInfo.metageneration()); + default: + throw new AssertionError("Unexpected enum value"); + } + } + + public static BlobSourceOption generationMatch() { + return new BlobSourceOption(StorageRpc.Option.IF_GENERATION_MATCH); + } + + public static BlobSourceOption generationNotMatch() { + return new BlobSourceOption(StorageRpc.Option.IF_GENERATION_NOT_MATCH); + } + + public static BlobSourceOption metagenerationMatch() { + return new BlobSourceOption(StorageRpc.Option.IF_METAGENERATION_MATCH); + } + + public static BlobSourceOption metagenerationNotMatch() { + return new BlobSourceOption(StorageRpc.Option.IF_METAGENERATION_NOT_MATCH); + } + + static Storage.BlobSourceOption[] convert(BlobInfo blobInfo, BlobSourceOption... options) { + Storage.BlobSourceOption[] convertedOptions = new Storage.BlobSourceOption[options.length]; + int index = 0; + for (BlobSourceOption option : options) { + convertedOptions[index++] = option.convert(blobInfo); + } + return convertedOptions; + } + } + + public Blob(Storage storage, BlobInfo info) { + this.storage = checkNotNull(storage); + this.info = checkNotNull(info); + } + + public BlobInfo info() { + return info; + } + + /** + * Returns true if this blob exists. + * + * @throws StorageException upon failure + */ + public boolean exists() { + return storage.get(info.bucket(), info.name()) != null; + } + + /** + * Returns the blob's content. + * + * @throws StorageException upon failure + */ + public byte[] content(Storage.BlobSourceOption... options) { + return storage.readAllBytes(info.bucket(), info.name(), options); + } + + /** + * Updates the blob's information. Bucket or blob's name cannot be changed by this method. If you + * want to rename the blob or move it to a different bucket use the {@link #copyTo} and + * {@link #delete} operations. + * + * @throws StorageException upon failure + */ + public void update(BlobInfo blobInfo, BlobTargetOption... options) { + checkArgument(Objects.equals(blobInfo.bucket(), info.bucket()), "Bucket name must match"); + checkArgument(Objects.equals(blobInfo.name(), info.name()), "Blob name must match"); + info = storage.update(blobInfo, options); + } + + /** + * Deletes this blob. + * + * @return true if bucket was deleted + * @throws StorageException upon failure + */ + public boolean delete(BlobSourceOption... options) { + return storage.delete(info.bucket(), info.name(), convert(info, options)); + } + + /** + * Send a copy request. + * + * @return the copied blob. + * @throws StorageException upon failure + */ + public Blob copyTo(BlobInfo target, BlobSourceOption... options) { + return copyTo(target, ImmutableList.copyOf(options), ImmutableList.of()); + } + + /** + * Send a copy request. + * + * @return the copied blob. + * @throws StorageException upon failure + */ + public Blob copyTo(BlobInfo target, Iterable sourceOptions, + Iterable targetOptions) { + CopyRequest copyRequest = + CopyRequest.builder().source(info.bucket(), info.name()) + .sourceOptions(convert(info, Iterables.toArray(sourceOptions, BlobSourceOption.class))) + .target(target).targetOptions(targetOptions).build(); + return new Blob(storage, storage.copy(copyRequest)); + } + + /** + * Returns a channel for reading this blob's content. + * + * @throws StorageException upon failure + */ + public BlobReadChannel reader(BlobSourceOption... options) { + return storage.reader(info.bucket(), info.name(), convert(info, options)); + } + + /** + * Returns a channel for writing to this blob. + * + * @throws StorageException upon failure + */ + public BlobWriteChannel writer(BlobTargetOption... options) { + return storage.writer(info, options); + } + + /** + * Generates a signed URL for this blob. If you want to allow access to for a fixed amount of time + * for this blob, you can use this method to generate a URL that is only valid within a certain + * time period. This is particularly useful if you don't want publicly accessible blobs, but don't + * want to require users to explicitly log in. + * + * @param expirationTimeInSeconds the signed URL expiration (using epoch time) + * @see Signed-URLs + */ + public URL signUrl(long expirationTimeInSeconds, SignUrlOption... options) { + return storage.signUrl(info, expirationTimeInSeconds, options); + } + + public Storage storage() { + return storage; + } +} diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobInfo.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobInfo.java index 3ef01af8d1f3..68255f991187 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobInfo.java +++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobInfo.java @@ -1,17 +1,12 @@ /* - * Copyright 2015 Google Inc. All Rights Reserved. * - * Licensed 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. + * * Copyright 2015 Google Inc. All Rights Reserved. * * Licensed 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 com.google.gcloud.storage; @@ -37,14 +32,13 @@ import java.util.Objects; /** - * A Google Storage object. + * Google Storage object metadata. * - * @see Concepts and Terminology + * @see Concepts and + * Terminology */ public final class BlobInfo implements Serializable { - private static final long serialVersionUID = 2228487739943277159L; - static final Function FROM_PB_FUNCTION = new Function() { @Override @@ -52,7 +46,6 @@ public BlobInfo apply(StorageObject pb) { return BlobInfo.fromPb(pb); } }; - static final Function TO_PB_FUNCTION = new Function() { @Override @@ -60,7 +53,7 @@ public StorageObject apply(BlobInfo blobInfo) { return blobInfo.toPb(); } }; - + private static final long serialVersionUID = 2228487739943277159L; private final String bucket; private final String id; private final String name; @@ -378,18 +371,6 @@ public String toString() { .toString(); } - public static BlobInfo of(String bucket, String name) { - return builder(bucket, name).build(); - } - - public static Builder builder(BucketInfo bucketInfo, String name) { - return builder(bucketInfo.name(), name); - } - - public static Builder builder(String bucket, String name) { - return new Builder().bucket(bucket).name(name); - } - @Override public int hashCode() { return Objects.hash(bucket, name); @@ -397,10 +378,7 @@ public int hashCode() { @Override public boolean equals(Object obj) { - if (!(obj instanceof BlobInfo)) { - return false; - } - return Objects.equals(toPb(), ((BlobInfo) obj).toPb()); + return obj instanceof BlobInfo && Objects.equals(toPb(), ((BlobInfo) obj).toPb()); } StorageObject toPb() { @@ -445,6 +423,18 @@ public ObjectAccessControl apply(Acl acl) { return storageObject; } + public static BlobInfo of(String bucket, String name) { + return builder(bucket, name).build(); + } + + public static Builder builder(BucketInfo bucketInfo, String name) { + return builder(bucketInfo.name(), name); + } + + public static Builder builder(String bucket, String name) { + return new Builder().bucket(bucket).name(name); + } + static BlobInfo fromPb(StorageObject storageObject) { Builder builder = new Builder() .bucket(storageObject.getBucket()) 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 new file mode 100644 index 000000000000..5c4d037f21c1 --- /dev/null +++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Bucket.java @@ -0,0 +1,89 @@ +/* + * + * * Copyright 2015 Google Inc. All Rights Reserved. * * Licensed 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 com.google.gcloud.storage; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.gcloud.storage.Storage.BlobSourceOption; +import com.google.gcloud.storage.Storage.BucketSourceOption; +import com.google.gcloud.storage.Storage.BucketTargetOption; + +import java.util.List; +import java.util.Objects; + +/** + * A Google cloud storage bucket. + */ +public final class Bucket { + + private final Storage storage; + private BucketInfo info; + + public Bucket(Storage storage, BucketInfo info) { + this.storage = checkNotNull(storage); + this.info = checkNotNull(info); + } + + /** + * Returns true if this bucket exists. + * + * @throws StorageException upon failure + */ + public boolean exists() { + return storage.get(info.name()) != null; + } + + /** + * Update the bucket's information. Bucket's name cannot be changed. + * + * @throws StorageException upon failure + */ + public void update(BucketInfo bucketInfo, BucketTargetOption... options) { + checkArgument(Objects.equals(bucketInfo.name(), info.name()), "Bucket name must match"); + info = storage.update(bucketInfo, options); + } + + /** + * Delete this bucket. + * + * @return true if bucket was deleted + * @throws StorageException upon failure + */ + public boolean delete(BucketSourceOption... options) { + return storage.delete(info.name(), options); + } + + public ListResult list(Storage.BlobListOption... options) { + return storage.list(info.name(), options); + } + + public BlobInfo get(String blob, BlobSourceOption... options) { + return null; + } + + public List get(String... blob) { + // todo + return null; + } + + /* + * BlobInfo create(BlobInfo blobInfo, byte[] content, BlobTargetOption... options) { + * + * } + */ + + + public Storage storage() { + return storage; + } +} diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BucketInfo.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BucketInfo.java index 28110e0a0292..b9c6b03f9dd3 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BucketInfo.java +++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BucketInfo.java @@ -1,17 +1,12 @@ /* - * Copyright 2015 Google Inc. All Rights Reserved. * - * Licensed 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. + * * Copyright 2015 Google Inc. All Rights Reserved. * * Licensed 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 com.google.gcloud.storage; @@ -44,15 +39,28 @@ import java.util.Objects; /** - * A Google Storage bucket. + * Google Storage bucket metadata; * * @see Concepts and - * Terminology + * Terminology */ public final class BucketInfo implements Serializable { + static final Function FROM_PB_FUNCTION = + new Function() { + @Override + public BucketInfo apply(com.google.api.services.storage.model.Bucket pb) { + return BucketInfo.fromPb(pb); + } + }; + static final Function TO_PB_FUNCTION = + new Function() { + @Override + public com.google.api.services.storage.model.Bucket apply(BucketInfo bucketInfo) { + return bucketInfo.toPb(); + } + }; private static final long serialVersionUID = -3946094202176916586L; - private final String id; private final String name; private final Acl.Entity owner; @@ -70,27 +78,11 @@ public final class BucketInfo implements Serializable { private final Location location; private final StorageClass storageClass; - static final Function FROM_PB_FUNCTION = - new Function() { - @Override - public BucketInfo apply(com.google.api.services.storage.model.Bucket pb) { - return BucketInfo.fromPb(pb); - } - }; - - static final Function TO_PB_FUNCTION = - new Function() { - @Override - public com.google.api.services.storage.model.Bucket apply(BucketInfo bucketInfo) { - return bucketInfo.toPb(); - } - }; - public static abstract class DeleteRule implements Serializable { private static final long serialVersionUID = 3137971668395933033L; - private final Type type; private static final String SUPPORTED_ACTION = "Delete"; + private final Type type; public enum Type { AGE, CREATE_BEFORE, NUM_NEWER_VERSIONS, IS_LIVE, UNKNOWN @@ -287,18 +279,19 @@ public enum Option { } } - static { - ImmutableMap.Builder map = ImmutableMap.builder(); - for (Option option : Option.values()) { - map.put(option.name(), option); - } - STRING_TO_OPTION = map.build(); - } - private StorageClass(String value) { this.value = checkNotNull(value); } + @Override + public String toString() { + return value(); + } + + public String value() { + return value; + } + public static StorageClass standard() { return Option.STANDARD.storageClass; } @@ -312,13 +305,12 @@ public static StorageClass of(String value) { return option == null ? new StorageClass(value) : option.storageClass; } - @Override - public String toString() { - return value(); - } - - public String value() { - return value; + static { + ImmutableMap.Builder map = ImmutableMap.builder(); + for (Option option : Option.values()) { + map.put(option.name(), option); + } + STRING_TO_OPTION = map.build(); } } @@ -340,35 +332,10 @@ public enum Option { } } - static { - ImmutableMap.Builder map = ImmutableMap.builder(); - for (Option option : Option.values()) { - map.put(option.name(), option); - } - STRING_TO_OPTION = map.build(); - } - private Location(String value) { this.value = checkNotNull(value); } - public static Location us() { - return Option.US.location; - } - - public static Location eu() { - return Option.EU.location; - } - - public static Location asia() { - return Option.ASIA.location; - } - - public static Location of(String value) { - Option option = STRING_TO_OPTION.get(value.toUpperCase()); - return option == null ? new Location(value) : option.location; - } - @Override public int hashCode() { return Objects.hash(value); @@ -394,6 +361,31 @@ public String toString() { public String value() { return value; } + + public static Location us() { + return Option.US.location; + } + + public static Location eu() { + return Option.EU.location; + } + + public static Location asia() { + return Option.ASIA.location; + } + + public static Location of(String value) { + Option option = STRING_TO_OPTION.get(value.toUpperCase()); + return option == null ? new Location(value) : option.location; + } + + static { + ImmutableMap.Builder map = ImmutableMap.builder(); + for (Option option : Option.values()) { + map.put(option.name(), option); + } + STRING_TO_OPTION = map.build(); + } } public final static class Builder { @@ -613,10 +605,7 @@ public int hashCode() { @Override public boolean equals(Object obj) { - if (!(obj instanceof BucketInfo)) { - return false; - } - return Objects.equals(toPb(), ((BucketInfo) obj).toPb()); + return obj instanceof BucketInfo && Objects.equals(toPb(), ((BucketInfo) obj).toPb()); } @Override @@ -626,14 +615,6 @@ public String toString() { .toString(); } - public static BucketInfo of(String name) { - return builder(name).build(); - } - - public static Builder builder(String name) { - return new Builder().name(name); - } - com.google.api.services.storage.model.Bucket toPb() { com.google.api.services.storage.model.Bucket bucketPb = new com.google.api.services.storage.model.Bucket(); @@ -697,6 +678,14 @@ public Rule apply(DeleteRule deleteRule) { return bucketPb; } + public static BucketInfo of(String name) { + return builder(name).build(); + } + + public static Builder builder(String name) { + return new Builder().name(name); + } + static BucketInfo fromPb(com.google.api.services.storage.model.Bucket bucketPb) { Builder builder = new Builder() .name(bucketPb.getName()) 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 e3cfbc195860..5951a88aa114 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 @@ -1,17 +1,12 @@ /* - * Copyright 2015 Google Inc. All Rights Reserved. * - * Licensed 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. + * * Copyright 2015 Google Inc. All Rights Reserved. * * Licensed 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 com.google.gcloud.storage; @@ -20,6 +15,7 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; import com.google.gcloud.AuthCredentials.ServiceAccountAuthCredentials; import com.google.gcloud.Service; import com.google.gcloud.spi.StorageRpc; @@ -223,7 +219,7 @@ class SignUrlOption implements Serializable { private final Object value; enum Option { - HTTP_METHOD, CONTENT_TYPE, MD5, SERVICE_ACCOUNT_CRED; + HTTP_METHOD, CONTENT_TYPE, MD5, SERVICE_ACCOUNT_CRED } private SignUrlOption(Option option, Object value) { @@ -310,8 +306,8 @@ public Long generation() { public static class Builder { private final List sourceBlobs = new LinkedList<>(); - private BlobInfo target; private final Set targetOptions = new LinkedHashSet<>(); + private BlobInfo target; public Builder addSource(Iterable blobs) { for (String blob : blobs) { @@ -342,6 +338,11 @@ public Builder targetOptions(BlobTargetOption... options) { return this; } + public Builder targetOptions(Iterable options) { + Iterables.addAll(targetOptions, options); + return this; + } + public ComposeRequest build() { checkArgument(!sourceBlobs.isEmpty()); checkNotNull(target); @@ -392,11 +393,11 @@ class CopyRequest implements Serializable { public static class Builder { + private final Set sourceOptions = new LinkedHashSet<>(); + private final Set targetOptions = new LinkedHashSet<>(); private String sourceBucket; private String sourceBlob; - private final Set sourceOptions = new LinkedHashSet<>(); private BlobInfo target; - private final Set targetOptions = new LinkedHashSet<>(); public Builder source(String bucket, String blob) { this.sourceBucket = bucket; @@ -409,6 +410,11 @@ public Builder sourceOptions(BlobSourceOption... options) { return this; } + public Builder sourceOptions(Iterable options) { + Iterables.addAll(sourceOptions, options); + return this; + } + public Builder target(BlobInfo target) { this.target = target; return this; @@ -419,6 +425,11 @@ public Builder targetOptions(BlobTargetOption... options) { return this; } + public Builder targetOptions(Iterable options) { + Iterables.addAll(targetOptions, options); + return this; + } + public CopyRequest build() { checkNotNull(sourceBucket); checkNotNull(sourceBlob); 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 new file mode 100644 index 000000000000..0b50de1682a0 --- /dev/null +++ b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobTest.java @@ -0,0 +1,131 @@ +/* + * + * * Copyright 2015 Google Inc. All Rights Reserved. * * Licensed 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 com.google.gcloud.storage; + +import static org.easymock.EasyMock.capture; +import static org.easymock.EasyMock.createMock; +import static org.easymock.EasyMock.createStrictMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.verify; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +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; + +public class BlobTest { + + private Storage storage; + private Blob blob; + private BlobInfo blobInfo = BlobInfo.of("b", "n"); + + @Before + public void setUp() throws Exception { + storage = createStrictMock(Storage.class); + blob = new Blob(storage, blobInfo); + } + + @After + public void tearDown() throws Exception { + verify(storage); + } + + @Test + public void testInfo() throws Exception { + assertEquals(blobInfo, blob.info()); + replay(storage); + } + + @Test + public void testExists_True() throws Exception { + expect(storage.get(blobInfo.bucket(), blobInfo.name())).andReturn(blobInfo); + replay(storage); + assertTrue(blob.exists()); + } + + @Test + public void testExists_False() throws Exception { + expect(storage.get(blobInfo.bucket(), blobInfo.name())).andReturn(null); + replay(storage); + assertFalse(blob.exists()); + } + + @Test + public void testContent() throws Exception { + byte[] content = {1, 2}; + expect(storage.readAllBytes(blobInfo.bucket(), blobInfo.name())).andReturn(content); + replay(storage); + assertArrayEquals(content, blob.content()); + } + + @Test + public void testUpdate() throws Exception { + BlobInfo updatedInfo = blobInfo.toBuilder().cacheControl("c").build(); + expect(storage.update(updatedInfo)).andReturn(updatedInfo); + replay(storage); + blob.update(updatedInfo); + assertSame(storage, blob.storage()); + assertEquals(updatedInfo, blob.info()); + } + + @Test + public void testDelete() throws Exception { + expect(storage.delete(blobInfo.bucket(), blobInfo.name())).andReturn(true); + replay(storage); + assertTrue(blob.delete()); + } + + @Test + public void testCopyTo() throws Exception { + BlobInfo target = BlobInfo.of("bt", "nt"); + Capture capturedCopyRequest = Capture.newInstance(); + expect(storage.copy(capture(capturedCopyRequest))).andReturn(target); + replay(storage); + Blob targetBlob = blob.copyTo(target); + assertEquals(target, targetBlob.info()); + assertSame(storage, targetBlob.storage()); + } + + @Test + public void testReader() throws Exception { + BlobReadChannel channel = createMock(BlobReadChannel.class); + expect(storage.reader(blobInfo.bucket(), blobInfo.name())).andReturn(channel); + replay(storage); + assertSame(channel, blob.reader()); + } + + @Test + public void testWriter() throws Exception { + BlobWriteChannel channel = createMock(BlobWriteChannel.class); + expect(storage.writer(blobInfo)).andReturn(channel); + replay(storage); + assertSame(channel, blob.writer()); + } + + @Test + public void testSignUrl() throws Exception { + URL url = new URL("http://localhost:123/bla"); + expect(storage.signUrl(blobInfo, 100)).andReturn(url); + replay(storage); + assertEquals(url, blob.signUrl(100)); + } +} From 21fc90c0504823c779a707ba08a2cad39f66295d Mon Sep 17 00:00:00 2001 From: aozarov Date: Thu, 9 Jul 2015 16:16:42 -0700 Subject: [PATCH 02/12] update copyright --- .../java/com/google/gcloud/storage/Blob.java | 20 ++++++++++++------- .../com/google/gcloud/storage/BlobInfo.java | 19 +++++++++++------- .../com/google/gcloud/storage/Bucket.java | 19 +++++++++++------- .../com/google/gcloud/storage/BucketInfo.java | 19 +++++++++++------- .../com/google/gcloud/storage/Storage.java | 19 +++++++++++------- .../com/google/gcloud/storage/BlobTest.java | 19 +++++++++++------- 6 files changed, 73 insertions(+), 42 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 40ffd8235ba0..dd83ee84182a 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 @@ -1,12 +1,18 @@ + /* + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed 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 * - * * Copyright 2015 Google Inc. All Rights Reserved. * * Licensed 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. + * 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 com.google.gcloud.storage; diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobInfo.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobInfo.java index 68255f991187..7536f96e20f8 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobInfo.java +++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobInfo.java @@ -1,12 +1,17 @@ /* + * Copyright 2015 Google Inc. All Rights Reserved. * - * * Copyright 2015 Google Inc. All Rights Reserved. * * Licensed 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. + * Licensed 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 com.google.gcloud.storage; 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 5c4d037f21c1..fd93a5bda6ea 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 @@ -1,12 +1,17 @@ /* + * Copyright 2015 Google Inc. All Rights Reserved. * - * * Copyright 2015 Google Inc. All Rights Reserved. * * Licensed 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. + * Licensed 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 com.google.gcloud.storage; diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BucketInfo.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BucketInfo.java index b9c6b03f9dd3..3f35fbc20e3a 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BucketInfo.java +++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BucketInfo.java @@ -1,12 +1,17 @@ /* + * Copyright 2015 Google Inc. All Rights Reserved. * - * * Copyright 2015 Google Inc. All Rights Reserved. * * Licensed 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. + * Licensed 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 com.google.gcloud.storage; 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 5951a88aa114..590c8d9d74d4 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 @@ -1,12 +1,17 @@ /* + * Copyright 2015 Google Inc. All Rights Reserved. * - * * Copyright 2015 Google Inc. All Rights Reserved. * * Licensed 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. + * Licensed 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 com.google.gcloud.storage; 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 0b50de1682a0..e49c5fde96c6 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 @@ -1,12 +1,17 @@ /* + * Copyright 2015 Google Inc. All Rights Reserved. * - * * Copyright 2015 Google Inc. All Rights Reserved. * * Licensed 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. + * Licensed 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 com.google.gcloud.storage; From d8fa7bbdec65710681b2d1470533146f34d0595a Mon Sep 17 00:00:00 2001 From: Marco Ziccardi Date: Tue, 22 Sep 2015 16:58:52 +0200 Subject: [PATCH 03/12] Split ListResult into interface and implementation. Add BlobListResult for paginated list of Blobs. Add tests. --- .../google/gcloud/storage/BaseListResult.java | 77 +++++++++++++++ .../google/gcloud/storage/BlobListResult.java | 77 +++++++++++++++ .../com/google/gcloud/storage/ListResult.java | 57 ++--------- .../google/gcloud/storage/StorageImpl.java | 6 +- ...esultTest.java => BaseListResultTest.java} | 12 +-- .../gcloud/storage/BlobListResultTest.java | 94 +++++++++++++++++++ .../gcloud/storage/SerializationTest.java | 4 +- 7 files changed, 268 insertions(+), 59 deletions(-) create mode 100644 gcloud-java-storage/src/main/java/com/google/gcloud/storage/BaseListResult.java create mode 100644 gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobListResult.java rename gcloud-java-storage/src/test/java/com/google/gcloud/storage/{ListResultTest.java => BaseListResultTest.java} (75%) create mode 100644 gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobListResultTest.java diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BaseListResult.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BaseListResult.java new file mode 100644 index 000000000000..fdcd84705555 --- /dev/null +++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BaseListResult.java @@ -0,0 +1,77 @@ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed 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 com.google.gcloud.storage; + +import java.io.Serializable; +import java.util.Collections; +import java.util.Iterator; +import java.util.Objects; + +/** + * Base implementation for Google Cloud storage list result. + */ +public class BaseListResult implements ListResult, Serializable { + + private static final long serialVersionUID = -6937287874908527950L; + + private final String cursor; + private final Iterable results; + private final NextPageFetcher pageFetcher; + + public interface NextPageFetcher extends Serializable { + ListResult nextPage(); + } + + public BaseListResult(NextPageFetcher pageFetcher, String cursor, Iterable results) { + this.pageFetcher = pageFetcher; + this.cursor = cursor; + this.results = results; + } + + @Override + public String nextPageCursor() { + return cursor; + } + + @Override + public ListResult nextPage() { + if (cursor == null || pageFetcher == null) { + return null; + } + return pageFetcher.nextPage(); + } + + @Override + public Iterator iterator() { + return results == null ? Collections.emptyIterator() : results.iterator(); + } + + @Override + public int hashCode() { + return Objects.hash(cursor, results); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof BaseListResult)) { + return false; + } + BaseListResult other = (BaseListResult) obj; + return Objects.equals(cursor, other.cursor) + && Objects.equals(results, other.results); + } +} diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobListResult.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobListResult.java new file mode 100644 index 000000000000..b4f94bc4b0e7 --- /dev/null +++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobListResult.java @@ -0,0 +1,77 @@ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed 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 com.google.gcloud.storage; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Objects; + +/** + * Implementation of a paginated list of Google Cloud storage {@code Blob}. + */ +public class BlobListResult implements ListResult { + + private final ListResult infoList; + private final transient Storage storage; + private transient List results; + + public BlobListResult(Storage storage, ListResult infoList) { + this.storage = storage; + this.infoList = infoList; + this.results = null; + } + + @Override + public String nextPageCursor() { + return infoList.nextPageCursor(); + } + + @Override + public ListResult nextPage() { + ListResult nextPageInfoList = infoList.nextPage(); + if (nextPageInfoList == null) { + return null; + } + return new BlobListResult(storage, nextPageInfoList); + } + + @Override + public Iterator iterator() { + if (results == null) { + this.results = new LinkedList<>(); + for (Iterator it = infoList.iterator(); it.hasNext();) { + results.add(new Blob(storage, it.next())); + } + } + return results.iterator(); + } + + @Override + public int hashCode() { + return Objects.hash(infoList); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof BlobListResult)) { + return false; + } + BlobListResult other = (BlobListResult) obj; + return Objects.equals(infoList, other.infoList); + } +} diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/ListResult.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/ListResult.java index 267226c5535f..008ad5164a0a 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/ListResult.java +++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/ListResult.java @@ -16,67 +16,28 @@ package com.google.gcloud.storage; - -import java.io.Serializable; -import java.util.Collections; import java.util.Iterator; -import java.util.Objects; /** - * Google Cloud storage list result. + * Interface for Google Cloud storage list result. */ -public final class ListResult implements Iterable, Serializable { - - private static final long serialVersionUID = -6937287874908527950L; - - private final String cursor; - private final Iterable results; - private final NextPageFetcher pageFetcher; - - public interface NextPageFetcher extends Serializable { - ListResult nextPage(); - } - - public ListResult(NextPageFetcher pageFetcher, String cursor, Iterable results) { - this.pageFetcher = pageFetcher; - this.cursor = cursor; - this.results = results; - } +public interface ListResult extends Iterable { /** * Returns the cursor for the nextPage or {@code null} if no more results. + * + * @return the string cursor for next page */ - public String nextPageCursor() { - return cursor; - } + public String nextPageCursor(); /** * Returns the results of the nextPage or {@code null} if no more result. + * + * @return the {@code ListResult} object for the next page */ - public ListResult nextPage() { - if (cursor == null || pageFetcher == null) { - return null; - } - return pageFetcher.nextPage(); - } - - @Override - public Iterator iterator() { - return results == null ? Collections.emptyIterator() : results.iterator(); - } + public ListResult nextPage(); @Override - public int hashCode() { - return Objects.hash(cursor, results); - } + public Iterator iterator(); - @Override - public boolean equals(Object obj) { - if (!(obj instanceof ListResult)) { - return false; - } - ListResult other = (ListResult) obj; - return Objects.equals(cursor, other.cursor) - && Objects.equals(results, other.results); - } } 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 f59c6c670969..be3bf1fa5d26 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 @@ -165,7 +165,7 @@ public StorageObject call() { } private static abstract class BasePageFetcher - implements ListResult.NextPageFetcher { + implements BaseListResult.NextPageFetcher { private static final long serialVersionUID = 8236329004030295223L; protected final Map requestOptions; @@ -234,7 +234,7 @@ public Tuple> cal } }, serviceOptions.retryParams(), EXCEPTION_HANDLER); String cursor = result.x(); - return new ListResult<>(new BucketPageFetcher(serviceOptions, cursor, optionsMap), cursor, + return new BaseListResult<>(new BucketPageFetcher(serviceOptions, cursor, optionsMap), cursor, Iterables.transform(result.y(), new Function() { @Override @@ -259,7 +259,7 @@ public Tuple> call() { } }, serviceOptions.retryParams(), EXCEPTION_HANDLER); String cursor = result.x(); - return new ListResult<>(new BlobPageFetcher(bucket, serviceOptions, cursor, optionsMap), cursor, + return new BaseListResult<>(new BlobPageFetcher(bucket, serviceOptions, cursor, optionsMap), cursor, Iterables.transform(result.y(), new Function() { @Override diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/ListResultTest.java b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BaseListResultTest.java similarity index 75% rename from gcloud-java-storage/src/test/java/com/google/gcloud/storage/ListResultTest.java rename to gcloud-java-storage/src/test/java/com/google/gcloud/storage/BaseListResultTest.java index c64bc0f98324..4c22edbc35c8 100644 --- a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/ListResultTest.java +++ b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BaseListResultTest.java @@ -24,21 +24,21 @@ import java.util.Collections; -public class ListResultTest { +public class BaseListResultTest { @Test public void testListResult() throws Exception { ImmutableList values = ImmutableList.of("1", "2"); - final ListResult nextResult = - new ListResult<>(null, "c", Collections.emptyList()); - ListResult.NextPageFetcher fetcher = new ListResult.NextPageFetcher() { + final BaseListResult nextResult = + new BaseListResult<>(null, "c", Collections.emptyList()); + BaseListResult.NextPageFetcher fetcher = new BaseListResult.NextPageFetcher() { @Override - public ListResult nextPage() { + public BaseListResult nextPage() { return nextResult; } }; - ListResult result = new ListResult<>(fetcher, "c", values); + BaseListResult result = new BaseListResult<>(fetcher, "c", values); assertEquals(nextResult, result.nextPage()); assertEquals("c", result.nextPageCursor()); assertEquals(values, ImmutableList.copyOf(result.iterator())); diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobListResultTest.java b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobListResultTest.java new file mode 100644 index 000000000000..9cbe5b3fea5a --- /dev/null +++ b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobListResultTest.java @@ -0,0 +1,94 @@ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed 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 com.google.gcloud.storage; + +import static org.easymock.EasyMock.createStrictMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.verify; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +import com.google.common.collect.ImmutableList; +import java.util.Iterator; + +import org.junit.Before; +import org.junit.Test; + +public class BlobListResultTest { + + private static final Iterable FIRST_PAGE_RESULTS = ImmutableList.of( + BlobInfo.of("b1", "n1"), + BlobInfo.of("b2", "n2")); + + private static final Iterable SECOND_PAGE_RESULTS = ImmutableList.of( + BlobInfo.of("b1", "n1"), + BlobInfo.of("b2", "n2")); + + private BaseListResult firstPage; + private BaseListResult secondPage; + private Storage storage; + private BlobListResult blobListResult; + + @Before + public void setUp() throws Exception { + firstPage = createStrictMock(BaseListResult.class); + secondPage = createStrictMock(BaseListResult.class); + storage = createStrictMock(Storage.class); + blobListResult = new BlobListResult(storage, firstPage); + } + + @Test + public void testListResult() throws Exception { + expect(firstPage.iterator()).andReturn(FIRST_PAGE_RESULTS.iterator()); + replay(firstPage); + Iterator firstPageIterator = FIRST_PAGE_RESULTS.iterator(); + Iterator blobListIterator = blobListResult.iterator(); + while (blobListIterator.hasNext() && firstPageIterator.hasNext()) { + assertEquals(firstPageIterator.next(), blobListIterator.next().info()); + } + assertFalse(blobListIterator.hasNext()); + assertFalse(firstPageIterator.hasNext()); + verify(firstPage); + } + + @Test + public void testCursor() throws Exception { + expect(firstPage.nextPageCursor()).andReturn("c"); + replay(firstPage); + assertEquals("c", blobListResult.nextPageCursor()); + verify(firstPage); + } + + @Test + public void testNextPage() throws Exception { + expect(firstPage.nextPage()).andReturn(secondPage); + expect(secondPage.iterator()).andReturn(SECOND_PAGE_RESULTS.iterator()); + replay(firstPage); + replay(secondPage); + ListResult nextPageResult = blobListResult.nextPage(); + Iterator secondPageIterator = SECOND_PAGE_RESULTS.iterator(); + Iterator blobListIterator = nextPageResult.iterator(); + while (blobListIterator.hasNext() && secondPageIterator.hasNext()) { + assertEquals(secondPageIterator.next(), blobListIterator.next().info()); + } + assertFalse(blobListIterator.hasNext()); + assertFalse(secondPageIterator.hasNext()); + verify(firstPage); + verify(secondPage); + } +} diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/SerializationTest.java b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/SerializationTest.java index 81342e4b5748..cd02cfbd801d 100644 --- a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/SerializationTest.java +++ b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/SerializationTest.java @@ -50,8 +50,8 @@ public class SerializationTest { Collections.singletonList(BatchResponse.Result.of(true)), Collections.>emptyList(), Collections.>emptyList()); - private static final ListResult LIST_RESULT = - new ListResult<>(null, "c", Collections.singletonList(BlobInfo.of("b", "n"))); + private static final BaseListResult LIST_RESULT = + new BaseListResult<>(null, "c", Collections.singletonList(BlobInfo.of("b", "n"))); private static final Storage.BlobListOption BLOB_LIST_OPTIONS = Storage.BlobListOption.maxResults(100); private static final Storage.BlobSourceOption BLOB_SOURCE_OPTIONS = From 44cefb06a9f1a2393f0191fe93e34b9776ced00d Mon Sep 17 00:00:00 2001 From: Marco Ziccardi Date: Tue, 22 Sep 2015 17:00:03 +0200 Subject: [PATCH 04/12] Blob and Bucket extended with more functionalities, added tests --- .../java/com/google/gcloud/storage/Blob.java | 73 ++++++-- .../com/google/gcloud/storage/Bucket.java | 103 +++++++++-- .../com/google/gcloud/storage/BlobTest.java | 5 +- .../com/google/gcloud/storage/BucketTest.java | 160 ++++++++++++++++++ 4 files changed, 313 insertions(+), 28 deletions(-) create mode 100644 gcloud-java-storage/src/test/java/com/google/gcloud/storage/BucketTest.java 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 dd83ee84182a..d52b15eae8c2 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 @@ -1,4 +1,3 @@ - /* * Copyright 2015 Google Inc. All Rights Reserved. * @@ -31,7 +30,6 @@ import java.net.URL; import java.util.Objects; - /** * A Google cloud storage object. */ @@ -89,18 +87,44 @@ static Storage.BlobSourceOption[] convert(BlobInfo blobInfo, BlobSourceOption... } } + /** + * Construct a {@code Blob} object for the provided {@code BlobInfo}. The storage service is used + * to issue requests. + * + * @param storage the storage service used for issuing requests + * @param info blob's info + */ public Blob(Storage storage, BlobInfo info) { this.storage = checkNotNull(storage); this.info = checkNotNull(info); } + /** + * Construct a {@code Blob} object for the provided bucket and blob names. The storage service is + * used to issue requests. + * + * @param storage the storage service used for issuing requests + * @param bucket bucket's name + * @param blob blob's name + */ + public Blob(Storage storage, String bucket, String blob) { + this.storage = checkNotNull(storage); + this.info = BlobInfo.of(bucket, blob); + } + + /** + * Get the blobs's information. + * + * @return a {@code BlobInfo} object for this blob + */ public BlobInfo info() { return info; } /** - * Returns true if this blob exists. + * Check if this blob exists. * + * @return true if this blob exists, false otherwise * @throws StorageException upon failure */ public boolean exists() { @@ -108,8 +132,10 @@ public boolean exists() { } /** - * Returns the blob's content. + * Return this blob's content. * + * @param options blob read options + * @return the blob's content * @throws StorageException upon failure */ public byte[] content(Storage.BlobSourceOption... options) { @@ -117,10 +143,12 @@ public byte[] content(Storage.BlobSourceOption... options) { } /** - * Updates the blob's information. Bucket or blob's name cannot be changed by this method. If you + * Update the blob's information. Bucket or blob's name cannot be changed by this method. If you * want to rename the blob or move it to a different bucket use the {@link #copyTo} and * {@link #delete} operations. * + * @param blobInfo new blob's information. Bucket and blob names must match the current ones + * @param options update options * @throws StorageException upon failure */ public void update(BlobInfo blobInfo, BlobTargetOption... options) { @@ -130,9 +158,10 @@ public void update(BlobInfo blobInfo, BlobTargetOption... options) { } /** - * Deletes this blob. + * Delete this blob. * - * @return true if bucket was deleted + * @param options blob delete options + * @return true if blob was deleted * @throws StorageException upon failure */ public boolean delete(BlobSourceOption... options) { @@ -140,9 +169,11 @@ public boolean delete(BlobSourceOption... options) { } /** - * Send a copy request. + * Copy this blob. * - * @return the copied blob. + * @param target target blob + * @param options source blob options + * @return the copied blob * @throws StorageException upon failure */ public Blob copyTo(BlobInfo target, BlobSourceOption... options) { @@ -150,9 +181,12 @@ public Blob copyTo(BlobInfo target, BlobSourceOption... options) { } /** - * Send a copy request. + * Copy this blob. * - * @return the copied blob. + * @param target target blob + * @param sourceOptions source blob options + * @param targetOptions target blob options + * @return the copied blob * @throws StorageException upon failure */ public Blob copyTo(BlobInfo target, Iterable sourceOptions, @@ -165,8 +199,10 @@ public Blob copyTo(BlobInfo target, Iterable sourceOptions, } /** - * Returns a channel for reading this blob's content. + * Return a channel for reading this blob's content. * + * @param options blob read options + * @return a {@code BlobReadChannel} object to read this blob * @throws StorageException upon failure */ public BlobReadChannel reader(BlobSourceOption... options) { @@ -174,8 +210,10 @@ public BlobReadChannel reader(BlobSourceOption... options) { } /** - * Returns a channel for writing to this blob. + * Return a channel for writing to this blob. * + * @param options target blob options + * @return a {@code BlobWriteChannel} object for writing to this blob * @throws StorageException upon failure */ public BlobWriteChannel writer(BlobTargetOption... options) { @@ -183,18 +221,25 @@ public BlobWriteChannel writer(BlobTargetOption... options) { } /** - * Generates a signed URL for this blob. If you want to allow access to for a fixed amount of time + * Generate a signed URL for this blob. If you want to allow access to for a fixed amount of time * for this blob, you can use this method to generate a URL that is only valid within a certain * time period. This is particularly useful if you don't want publicly accessible blobs, but don't * want to require users to explicitly log in. * * @param expirationTimeInSeconds the signed URL expiration (using epoch time) + * @param options signed url options + * @return a signed URL for this bucket and the specified options * @see Signed-URLs */ public URL signUrl(long expirationTimeInSeconds, SignUrlOption... options) { return storage.signUrl(info, expirationTimeInSeconds, options); } + /** + * Get this blobs's {@code Storage} object. + * + * @return the storage service used by this blob to issue requests + */ public Storage storage() { return storage; } 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 fd93a5bda6ea..56d27c3c621f 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 @@ -20,8 +20,10 @@ import static com.google.common.base.Preconditions.checkNotNull; import com.google.gcloud.storage.Storage.BlobSourceOption; +import com.google.gcloud.storage.Storage.BlobTargetOption; import com.google.gcloud.storage.Storage.BucketSourceOption; import com.google.gcloud.storage.Storage.BucketTargetOption; +import java.util.LinkedList; import java.util.List; import java.util.Objects; @@ -34,14 +36,43 @@ public final class Bucket { private final Storage storage; private BucketInfo info; + /** + * Construct a {@code Bucket} object for the provided {@code BucketInfo}. The storage service is + * used to issue requests. + * + * @param storage the storage service used for issuing requests + * @param info bucket's info + */ public Bucket(Storage storage, BucketInfo info) { this.storage = checkNotNull(storage); this.info = checkNotNull(info); } /** - * Returns true if this bucket exists. + * Construct a {@code Bucket} object for the provided name. The storage service is used to issue + * requests. + * + * @param storage the storage service used for issuing requests + * @param bucket bucket's name + */ + public Bucket(Storage storage, String bucket) { + this.storage = checkNotNull(storage); + this.info = BucketInfo.of(bucket); + } + + /** + * Get the bucket's information. + * + * @return a {@code BucketInfo} object for this bucket + */ + public BucketInfo info() { + return info; + } + + /** + * Check if this bucket exists. * + * @return true if this bucket exists, false otherwise * @throws StorageException upon failure */ public boolean exists() { @@ -51,6 +82,8 @@ public boolean exists() { /** * Update the bucket's information. Bucket's name cannot be changed. * + * @param bucketInfo new bucket's information. Name must match the one of the current bucket + * @param options update options * @throws StorageException upon failure */ public void update(BucketInfo bucketInfo, BucketTargetOption... options) { @@ -61,6 +94,7 @@ public void update(BucketInfo bucketInfo, BucketTargetOption... options) { /** * Delete this bucket. * + * @param options bucket delete options * @return true if bucket was deleted * @throws StorageException upon failure */ @@ -68,26 +102,69 @@ public boolean delete(BucketSourceOption... options) { return storage.delete(info.name(), options); } - public ListResult list(Storage.BlobListOption... options) { - return storage.list(info.name(), options); + /** + * List blobs in this bucket. + * + * @param options options for listing blobs + * @return the paginated list of {@code Blob} objects in this bucket + * @throws StorageException upon failure + */ + public ListResult list(Storage.BlobListOption... options) { + return new BlobListResult(storage, storage.list(info.name(), options)); } - public BlobInfo get(String blob, BlobSourceOption... options) { - return null; + /** + * Return the requested blob in this bucket or {@code null} if not found. + * + * @param blob name of the requested blob + * @param options blob search options + * @return the requested blob in this bucket or {@code null} if not found + * @throws StorageException upon failure + */ + public Blob get(String blob, BlobSourceOption... options) { + return new Blob(storage, storage.get(info.name(), blob, options)); } - public List get(String... blob) { - // todo - return null; + /** + * Return a list of requested blobs in this bucket. Blobs that do not exist are null. + * + * @param blobNames names of the requested blobs + * @return a list containing the requested blobs in this bucket + * @throws StorageException upon failure + */ + public List getAll(String... blobNames) { + BatchRequest.Builder batch = BatchRequest.builder(); + for (String blobName : blobNames) { + batch.get(info.name(), blobName); + } + List blobs = new LinkedList<>(); + BatchResponse response = storage.apply(batch.build()); + for (BatchResponse.Result result : response.gets()) { + BlobInfo blobInfo = result.get(); + blobs.add(blobInfo != null ? new Blob(storage, blobInfo) : null); + } + return blobs; } - /* - * BlobInfo create(BlobInfo blobInfo, byte[] content, BlobTargetOption... options) { - * - * } + /** + * Create a new blob in this bucket. + * + * @param blob a blob name + * @param content the blob content + * @param options options for blob creation + * @return a complete blob information. + * @throws StorageException upon failure */ + Blob create(String blob, byte[] content, BlobTargetOption... options) { + BlobInfo blobInfo = BlobInfo.of(info.name(), blob); + return new Blob(storage, storage.create(blobInfo, content, options)); + } - + /** + * Get this bucket's {@code Storage} object. + * + * @return the storage service used by this bucket to issue requests + */ public Storage storage() { return storage; } 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 e49c5fde96c6..40ce864368df 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 @@ -41,7 +41,7 @@ public class BlobTest { private Storage storage; private Blob blob; - private BlobInfo blobInfo = BlobInfo.of("b", "n"); + private final BlobInfo blobInfo = BlobInfo.of("b", "n"); @Before public void setUp() throws Exception { @@ -107,6 +107,9 @@ public void testCopyTo() throws Exception { replay(storage); Blob targetBlob = blob.copyTo(target); assertEquals(target, targetBlob.info()); + assertEquals(capturedCopyRequest.getValue().sourceBlob(), blob.info().name()); + assertEquals(capturedCopyRequest.getValue().sourceBucket(), blob.info().bucket()); + assertEquals(capturedCopyRequest.getValue().target(), target); assertSame(storage, targetBlob.storage()); } 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 new file mode 100644 index 000000000000..4b23af708636 --- /dev/null +++ b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BucketTest.java @@ -0,0 +1,160 @@ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed 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 com.google.gcloud.storage; + +import static org.easymock.EasyMock.capture; +import static org.easymock.EasyMock.createStrictMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.verify; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +import com.google.common.collect.ImmutableList; +import com.google.gcloud.storage.BatchResponse.Result; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import org.easymock.Capture; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class BucketTest { + + private static final Iterable BLOB_INFO_RESULTS = ImmutableList.of( + BlobInfo.of("b", "n1"), + BlobInfo.of("b", "n2"), + BlobInfo.of("b", "n3")); + + private Storage storage; + private Bucket bucket; + private final BucketInfo bucketInfo = BucketInfo.of("b"); + + @Before + public void setUp() throws Exception { + storage = createStrictMock(Storage.class); + bucket = new Bucket(storage, bucketInfo); + } + + @After + public void tearDown() throws Exception { + verify(storage); + } + + @Test + public void testInfo() throws Exception { + assertEquals(bucketInfo, bucket.info()); + replay(storage); + } + + @Test + public void testExists_True() throws Exception { + expect(storage.get(bucketInfo.name())).andReturn(bucketInfo); + replay(storage); + assertTrue(bucket.exists()); + } + + @Test + public void testExists_False() throws Exception { + expect(storage.get(bucketInfo.name())).andReturn(null); + replay(storage); + assertFalse(bucket.exists()); + } + + @Test + public void testUpdate() throws Exception { + BucketInfo updatedInfo = bucketInfo.toBuilder().notFoundPage("p").build(); + expect(storage.update(updatedInfo)).andReturn(updatedInfo); + replay(storage); + bucket.update(updatedInfo); + assertSame(storage, bucket.storage()); + assertEquals(updatedInfo, bucket.info()); + } + + @Test + public void testDelete() throws Exception { + expect(storage.delete(bucketInfo.name())).andReturn(true); + replay(storage); + assertTrue(bucket.delete()); + } + + @Test + public void testList() throws Exception { + BaseListResult blobInfoResult = new BaseListResult<>(null, "c", BLOB_INFO_RESULTS); + expect(storage.list(bucketInfo.name())).andReturn(blobInfoResult); + replay(storage); + ListResult blobResult = bucket.list(); + Iterator blobInfoIterator = blobInfoResult.iterator(); + Iterator blobIterator = blobResult.iterator(); + while (blobInfoIterator.hasNext() && blobIterator.hasNext()) { + assertEquals(blobInfoIterator.next(), blobIterator.next().info()); + } + assertFalse(blobInfoIterator.hasNext()); + assertFalse(blobIterator.hasNext()); + assertEquals(blobInfoResult.nextPageCursor(), blobResult.nextPageCursor()); + } + + @Test + public void testGet() throws Exception { + BlobInfo info = BlobInfo.of("b", "n"); + expect(storage.get(bucketInfo.name(), "n")).andReturn(info); + replay(storage); + Blob blob = bucket.get("n"); + assertEquals(info, blob.info()); + } + + @Test + public void testGetAll() throws Exception { + Capture capturedBatchRequest = Capture.newInstance(); + List> batchResultList = new LinkedList<>(); + for (BlobInfo info : BLOB_INFO_RESULTS) { + batchResultList.add(new Result<>(info)); + } + BatchResponse response = + 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"); + Set blobInfoSet = capturedBatchRequest.getValue().toGet().keySet(); + assertEquals(batchResultList.size(), blobInfoSet.size()); + for (BlobInfo info : BLOB_INFO_RESULTS) { + assertTrue(blobInfoSet.contains(info)); + } + Iterator blobIterator = blobs.iterator(); + Iterator> batchResultIterator = response.gets().iterator(); + while (batchResultIterator.hasNext() && blobIterator.hasNext()) { + assertEquals(batchResultIterator.next().get(), blobIterator.next().info()); + } + assertFalse(batchResultIterator.hasNext()); + assertFalse(blobIterator.hasNext()); + } + + @Test + public void testCreate() throws Exception { + BlobInfo info = BlobInfo.of("b", "n"); + byte[] content = {0xD, 0xE, 0xA, 0xD}; + expect(storage.create(info, content)).andReturn(info); + replay(storage); + Blob blob = bucket.create("n", content); + assertEquals(info, blob.info()); + } +} From c1f018c35c46b9d9e2416fc0a8c542d972d78518 Mon Sep 17 00:00:00 2001 From: Marco Ziccardi Date: Wed, 23 Sep 2015 11:11:04 +0200 Subject: [PATCH 05/12] Avoid storing Iterable in BlobListResult --- .../google/gcloud/storage/BlobListResult.java | 44 +++++++++++++------ .../com/google/gcloud/storage/ListResult.java | 17 ++----- 2 files changed, 34 insertions(+), 27 deletions(-) diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobListResult.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobListResult.java index b4f94bc4b0e7..9bc53fd94a9b 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobListResult.java +++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobListResult.java @@ -16,9 +16,9 @@ package com.google.gcloud.storage; +import static com.google.common.base.Preconditions.checkNotNull; + import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; import java.util.Objects; /** @@ -27,13 +27,35 @@ public class BlobListResult implements ListResult { private final ListResult infoList; - private final transient Storage storage; - private transient List results; + private final Storage storage; + + private class BlobListIterator implements Iterator { + + private final Iterator blobInfoIterator; + + public BlobListIterator() { + this.blobInfoIterator = infoList.iterator(); + } + + @Override + public boolean hasNext() { + return blobInfoIterator.hasNext(); + } + + @Override + public Blob next() { + return new Blob(storage, blobInfoIterator.next()); + } + + @Override + public void remove() { + blobInfoIterator.remove(); + } + } public BlobListResult(Storage storage, ListResult infoList) { - this.storage = storage; - this.infoList = infoList; - this.results = null; + this.storage = checkNotNull(storage); + this.infoList = checkNotNull(infoList); } @Override @@ -52,13 +74,7 @@ public ListResult nextPage() { @Override public Iterator iterator() { - if (results == null) { - this.results = new LinkedList<>(); - for (Iterator it = infoList.iterator(); it.hasNext();) { - results.add(new Blob(storage, it.next())); - } - } - return results.iterator(); + return new BlobListIterator(); } @Override diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/ListResult.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/ListResult.java index 008ad5164a0a..a07d6edff745 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/ListResult.java +++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/ListResult.java @@ -16,28 +16,19 @@ package com.google.gcloud.storage; -import java.util.Iterator; - /** * Interface for Google Cloud storage list result. */ public interface ListResult extends Iterable { /** - * Returns the cursor for the nextPage or {@code null} if no more results. - * - * @return the string cursor for next page + * Return the cursor for the nextPage or {@code null} if no more results. */ - public String nextPageCursor(); + String nextPageCursor(); /** - * Returns the results of the nextPage or {@code null} if no more result. - * - * @return the {@code ListResult} object for the next page + * Return the results of the nextPage or {@code null} if no more result. */ - public ListResult nextPage(); - - @Override - public Iterator iterator(); + ListResult nextPage(); } From 8591284926cec97e1d58b2d6e24b07d098fda171 Mon Sep 17 00:00:00 2001 From: Marco Ziccardi Date: Wed, 23 Sep 2015 16:12:56 +0200 Subject: [PATCH 06/12] Remove redundant return from Blob and Bucket javadoc --- .../java/com/google/gcloud/storage/Blob.java | 17 +++++------------ .../java/com/google/gcloud/storage/Bucket.java | 15 ++++----------- 2 files changed, 9 insertions(+), 23 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 d52b15eae8c2..ebad11150663 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 @@ -109,13 +109,11 @@ public Blob(Storage storage, BlobInfo info) { */ public Blob(Storage storage, String bucket, String blob) { this.storage = checkNotNull(storage); - this.info = BlobInfo.of(bucket, blob); + this.info = BlobInfo.of(checkNotNull(bucket), checkNotNull(blob)); } /** - * Get the blobs's information. - * - * @return a {@code BlobInfo} object for this blob + * Return the blob's information. */ public BlobInfo info() { return info; @@ -135,7 +133,6 @@ public boolean exists() { * Return this blob's content. * * @param options blob read options - * @return the blob's content * @throws StorageException upon failure */ public byte[] content(Storage.BlobSourceOption... options) { @@ -199,10 +196,9 @@ public Blob copyTo(BlobInfo target, Iterable sourceOptions, } /** - * Return a channel for reading this blob's content. + * Return a {@code BlobReadChannel} object for reading this blob's content. * * @param options blob read options - * @return a {@code BlobReadChannel} object to read this blob * @throws StorageException upon failure */ public BlobReadChannel reader(BlobSourceOption... options) { @@ -210,10 +206,9 @@ public BlobReadChannel reader(BlobSourceOption... options) { } /** - * Return a channel for writing to this blob. + * Return a {@code BlobWriteChannel} object for writing to this blob. * * @param options target blob options - * @return a {@code BlobWriteChannel} object for writing to this blob * @throws StorageException upon failure */ public BlobWriteChannel writer(BlobTargetOption... options) { @@ -236,9 +231,7 @@ public URL signUrl(long expirationTimeInSeconds, SignUrlOption... options) { } /** - * Get this blobs's {@code Storage} object. - * - * @return the storage service used by this blob to issue requests + * Return the blob's {@code Storage} object used to issue requests. */ public Storage storage() { return storage; 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 56d27c3c621f..77c16c106e57 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 @@ -57,13 +57,11 @@ public Bucket(Storage storage, BucketInfo info) { */ public Bucket(Storage storage, String bucket) { this.storage = checkNotNull(storage); - this.info = BucketInfo.of(bucket); + this.info = BucketInfo.of(checkNotNull(bucket)); } /** - * Get the bucket's information. - * - * @return a {@code BucketInfo} object for this bucket + * Return the bucket's information. */ public BucketInfo info() { return info; @@ -103,10 +101,9 @@ public boolean delete(BucketSourceOption... options) { } /** - * List blobs in this bucket. + * Return the paginated list of {@code Blob} in this bucket. * * @param options options for listing blobs - * @return the paginated list of {@code Blob} objects in this bucket * @throws StorageException upon failure */ public ListResult list(Storage.BlobListOption... options) { @@ -118,7 +115,6 @@ public ListResult list(Storage.BlobListOption... options) { * * @param blob name of the requested blob * @param options blob search options - * @return the requested blob in this bucket or {@code null} if not found * @throws StorageException upon failure */ public Blob get(String blob, BlobSourceOption... options) { @@ -129,7 +125,6 @@ public Blob get(String blob, BlobSourceOption... options) { * Return a list of requested blobs in this bucket. Blobs that do not exist are null. * * @param blobNames names of the requested blobs - * @return a list containing the requested blobs in this bucket * @throws StorageException upon failure */ public List getAll(String... blobNames) { @@ -161,9 +156,7 @@ Blob create(String blob, byte[] content, BlobTargetOption... options) { } /** - * Get this bucket's {@code Storage} object. - * - * @return the storage service used by this bucket to issue requests + * Return the bucket's {@code Storage} object used to issue requests. */ public Storage storage() { return storage; From a10b74b73c5f5d5a2ed337503ddd4351f95d2a64 Mon Sep 17 00:00:00 2001 From: Marco Ziccardi Date: Thu, 24 Sep 2015 10:51:19 +0200 Subject: [PATCH 07/12] Make Blob.copyTo accept string parameters --- .../java/com/google/gcloud/storage/Blob.java | 25 +++++------ .../com/google/gcloud/storage/BlobTest.java | 43 ++++++++++++------- 2 files changed, 39 insertions(+), 29 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 ebad11150663..afe2d1ab8375 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,8 +20,6 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.gcloud.storage.Blob.BlobSourceOption.convert; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Iterables; import com.google.gcloud.spi.StorageRpc; import com.google.gcloud.storage.Storage.BlobTargetOption; import com.google.gcloud.storage.Storage.CopyRequest; @@ -166,32 +164,31 @@ public boolean delete(BlobSourceOption... options) { } /** - * Copy this blob. + * Copy this blob to the target bucket, preserving its name. Possibly update metadata. * - * @param target target blob + * @param targetBucket target bucket's name * @param options source blob options * @return the copied blob * @throws StorageException upon failure */ - public Blob copyTo(BlobInfo target, BlobSourceOption... options) { - return copyTo(target, ImmutableList.copyOf(options), ImmutableList.of()); + public Blob copyTo(String targetBucket, BlobSourceOption... options) { + return copyTo(targetBucket, info.name(), options); } /** - * Copy this blob. + * Copy this blob to the target bucket with a new name. Possibly update metadata. * - * @param target target blob - * @param sourceOptions source blob options - * @param targetOptions target blob options + * @param targetBucket target bucket's name + * @param targetBlob target blob's name + * @param options source blob options * @return the copied blob * @throws StorageException upon failure */ - public Blob copyTo(BlobInfo target, Iterable sourceOptions, - Iterable targetOptions) { + public Blob copyTo(String targetBucket, String targetBlob, BlobSourceOption... options) { + BlobInfo updatedInfo = info.toBuilder().bucket(targetBucket).name(targetBlob).build(); CopyRequest copyRequest = CopyRequest.builder().source(info.bucket(), info.name()) - .sourceOptions(convert(info, Iterables.toArray(sourceOptions, BlobSourceOption.class))) - .target(target).targetOptions(targetOptions).build(); + .sourceOptions(convert(info, options)).target(updatedInfo).build(); return new Blob(storage, storage.copy(copyRequest)); } 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 40ce864368df..a46d4d92d96c 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 @@ -29,24 +29,23 @@ import static org.junit.Assert.assertTrue; 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; public class BlobTest { + private static final BlobInfo BLOB_INFO = BlobInfo.of("b", "n"); + private Storage storage; private Blob blob; - private final BlobInfo blobInfo = BlobInfo.of("b", "n"); @Before public void setUp() throws Exception { storage = createStrictMock(Storage.class); - blob = new Blob(storage, blobInfo); + blob = new Blob(storage, BLOB_INFO); } @After @@ -56,20 +55,20 @@ public void tearDown() throws Exception { @Test public void testInfo() throws Exception { - assertEquals(blobInfo, blob.info()); + assertEquals(BLOB_INFO, blob.info()); replay(storage); } @Test public void testExists_True() throws Exception { - expect(storage.get(blobInfo.bucket(), blobInfo.name())).andReturn(blobInfo); + expect(storage.get(BLOB_INFO.bucket(), BLOB_INFO.name())).andReturn(BLOB_INFO); replay(storage); assertTrue(blob.exists()); } @Test public void testExists_False() throws Exception { - expect(storage.get(blobInfo.bucket(), blobInfo.name())).andReturn(null); + expect(storage.get(BLOB_INFO.bucket(), BLOB_INFO.name())).andReturn(null); replay(storage); assertFalse(blob.exists()); } @@ -77,14 +76,14 @@ public void testExists_False() throws Exception { @Test public void testContent() throws Exception { byte[] content = {1, 2}; - expect(storage.readAllBytes(blobInfo.bucket(), blobInfo.name())).andReturn(content); + expect(storage.readAllBytes(BLOB_INFO.bucket(), BLOB_INFO.name())).andReturn(content); replay(storage); assertArrayEquals(content, blob.content()); } @Test public void testUpdate() throws Exception { - BlobInfo updatedInfo = blobInfo.toBuilder().cacheControl("c").build(); + BlobInfo updatedInfo = BLOB_INFO.toBuilder().cacheControl("c").build(); expect(storage.update(updatedInfo)).andReturn(updatedInfo); replay(storage); blob.update(updatedInfo); @@ -94,18 +93,32 @@ public void testUpdate() throws Exception { @Test public void testDelete() throws Exception { - expect(storage.delete(blobInfo.bucket(), blobInfo.name())).andReturn(true); + expect(storage.delete(BLOB_INFO.bucket(), BLOB_INFO.name())).andReturn(true); replay(storage); assertTrue(blob.delete()); } + @Test + public void testCopyToBucket() throws Exception { + BlobInfo target = BLOB_INFO.toBuilder().bucket("bt").build(); + Capture capturedCopyRequest = Capture.newInstance(); + expect(storage.copy(capture(capturedCopyRequest))).andReturn(target); + replay(storage); + Blob targetBlob = blob.copyTo("bt"); + assertEquals(target, targetBlob.info()); + assertEquals(capturedCopyRequest.getValue().sourceBlob(), blob.info().name()); + assertEquals(capturedCopyRequest.getValue().sourceBucket(), blob.info().bucket()); + assertEquals(capturedCopyRequest.getValue().target(), target); + assertSame(storage, targetBlob.storage()); + } + @Test public void testCopyTo() throws Exception { - BlobInfo target = BlobInfo.of("bt", "nt"); + BlobInfo target = BLOB_INFO.toBuilder().bucket("bt").name("nt").build(); Capture capturedCopyRequest = Capture.newInstance(); expect(storage.copy(capture(capturedCopyRequest))).andReturn(target); replay(storage); - Blob targetBlob = blob.copyTo(target); + Blob targetBlob = blob.copyTo("bt", "nt"); assertEquals(target, targetBlob.info()); assertEquals(capturedCopyRequest.getValue().sourceBlob(), blob.info().name()); assertEquals(capturedCopyRequest.getValue().sourceBucket(), blob.info().bucket()); @@ -116,7 +129,7 @@ public void testCopyTo() throws Exception { @Test public void testReader() throws Exception { BlobReadChannel channel = createMock(BlobReadChannel.class); - expect(storage.reader(blobInfo.bucket(), blobInfo.name())).andReturn(channel); + expect(storage.reader(BLOB_INFO.bucket(), BLOB_INFO.name())).andReturn(channel); replay(storage); assertSame(channel, blob.reader()); } @@ -124,7 +137,7 @@ public void testReader() throws Exception { @Test public void testWriter() throws Exception { BlobWriteChannel channel = createMock(BlobWriteChannel.class); - expect(storage.writer(blobInfo)).andReturn(channel); + expect(storage.writer(BLOB_INFO)).andReturn(channel); replay(storage); assertSame(channel, blob.writer()); } @@ -132,7 +145,7 @@ public void testWriter() throws Exception { @Test public void testSignUrl() throws Exception { URL url = new URL("http://localhost:123/bla"); - expect(storage.signUrl(blobInfo, 100)).andReturn(url); + expect(storage.signUrl(BLOB_INFO, 100)).andReturn(url); replay(storage); assertEquals(url, blob.signUrl(100)); } From ccc023045b446ac8d5e4b6e921171e09e3eac37e Mon Sep 17 00:00:00 2001 From: Marco Ziccardi Date: Thu, 24 Sep 2015 11:00:36 +0200 Subject: [PATCH 08/12] Minor refactoring to Bucket and BucketTest --- .../java/com/google/gcloud/storage/Bucket.java | 2 +- .../com/google/gcloud/storage/BucketTest.java | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 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 77c16c106e57..ad2aa7246547 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 @@ -23,8 +23,8 @@ import com.google.gcloud.storage.Storage.BlobTargetOption; import com.google.gcloud.storage.Storage.BucketSourceOption; import com.google.gcloud.storage.Storage.BucketTargetOption; -import java.util.LinkedList; +import java.util.LinkedList; import java.util.List; import java.util.Objects; 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 4b23af708636..9dd4235c16b9 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 @@ -40,6 +40,7 @@ public class BucketTest { + private static final BucketInfo BUCKET_INFO = BucketInfo.of("b"); private static final Iterable BLOB_INFO_RESULTS = ImmutableList.of( BlobInfo.of("b", "n1"), BlobInfo.of("b", "n2"), @@ -47,12 +48,11 @@ public class BucketTest { private Storage storage; private Bucket bucket; - private final BucketInfo bucketInfo = BucketInfo.of("b"); @Before public void setUp() throws Exception { storage = createStrictMock(Storage.class); - bucket = new Bucket(storage, bucketInfo); + bucket = new Bucket(storage, BUCKET_INFO); } @After @@ -62,27 +62,27 @@ public void tearDown() throws Exception { @Test public void testInfo() throws Exception { - assertEquals(bucketInfo, bucket.info()); + assertEquals(BUCKET_INFO, bucket.info()); replay(storage); } @Test public void testExists_True() throws Exception { - expect(storage.get(bucketInfo.name())).andReturn(bucketInfo); + expect(storage.get(BUCKET_INFO.name())).andReturn(BUCKET_INFO); replay(storage); assertTrue(bucket.exists()); } @Test public void testExists_False() throws Exception { - expect(storage.get(bucketInfo.name())).andReturn(null); + expect(storage.get(BUCKET_INFO.name())).andReturn(null); replay(storage); assertFalse(bucket.exists()); } @Test public void testUpdate() throws Exception { - BucketInfo updatedInfo = bucketInfo.toBuilder().notFoundPage("p").build(); + BucketInfo updatedInfo = BUCKET_INFO.toBuilder().notFoundPage("p").build(); expect(storage.update(updatedInfo)).andReturn(updatedInfo); replay(storage); bucket.update(updatedInfo); @@ -92,7 +92,7 @@ public void testUpdate() throws Exception { @Test public void testDelete() throws Exception { - expect(storage.delete(bucketInfo.name())).andReturn(true); + expect(storage.delete(BUCKET_INFO.name())).andReturn(true); replay(storage); assertTrue(bucket.delete()); } @@ -100,7 +100,7 @@ public void testDelete() throws Exception { @Test public void testList() throws Exception { BaseListResult blobInfoResult = new BaseListResult<>(null, "c", BLOB_INFO_RESULTS); - expect(storage.list(bucketInfo.name())).andReturn(blobInfoResult); + expect(storage.list(BUCKET_INFO.name())).andReturn(blobInfoResult); replay(storage); ListResult blobResult = bucket.list(); Iterator blobInfoIterator = blobInfoResult.iterator(); @@ -116,7 +116,7 @@ public void testList() throws Exception { @Test public void testGet() throws Exception { BlobInfo info = BlobInfo.of("b", "n"); - expect(storage.get(bucketInfo.name(), "n")).andReturn(info); + expect(storage.get(BUCKET_INFO.name(), "n")).andReturn(info); replay(storage); Blob blob = bucket.get("n"); assertEquals(info, blob.info()); From 3cf6af39a09272ced21ba85b8c9388846581d1e2 Mon Sep 17 00:00:00 2001 From: Marco Ziccardi Date: Thu, 24 Sep 2015 13:53:33 +0200 Subject: [PATCH 09/12] Make Bucket and Blob immutable --- .../java/com/google/gcloud/storage/Blob.java | 20 +++++++++++++---- .../com/google/gcloud/storage/Bucket.java | 22 +++++++++++++++---- .../com/google/gcloud/storage/BlobTest.java | 14 ++++++++++-- .../com/google/gcloud/storage/BucketTest.java | 14 ++++++++++-- 4 files changed, 58 insertions(+), 12 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 afe2d1ab8375..f740a2e716ff 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 @@ -34,7 +34,7 @@ public final class Blob { private final Storage storage; - private BlobInfo info; + private final BlobInfo info; public static class BlobSourceOption extends Option { @@ -137,19 +137,31 @@ public byte[] content(Storage.BlobSourceOption... options) { return storage.readAllBytes(info.bucket(), info.name(), options); } + /** + * Fetches current blob's latest information. + * + * @param options blob read options + * @return a {@code Blob} object with latest information + * @throws StorageException upon failure + */ + public Blob reload(BlobSourceOption... options) { + return new Blob(storage, storage.get(info.bucket(), info.name(), convert(info, options))); + } + /** * Update the blob's information. Bucket or blob's name cannot be changed by this method. If you * want to rename the blob or move it to a different bucket use the {@link #copyTo} and - * {@link #delete} operations. + * {@link #delete} operations. A new {@code Blob} object is returned. * * @param blobInfo new blob's information. Bucket and blob names must match the current ones * @param options update options + * @return a {@code Blob} object with updated information * @throws StorageException upon failure */ - public void update(BlobInfo blobInfo, BlobTargetOption... options) { + public Blob update(BlobInfo blobInfo, BlobTargetOption... options) { checkArgument(Objects.equals(blobInfo.bucket(), info.bucket()), "Bucket name must match"); checkArgument(Objects.equals(blobInfo.name(), info.name()), "Blob name must match"); - info = storage.update(blobInfo, options); + return new Blob(storage, storage.update(blobInfo, options)); } /** 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 ad2aa7246547..47c16a3646ed 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 @@ -18,6 +18,7 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.gcloud.storage.Blob.BlobSourceOption.convert; import com.google.gcloud.storage.Storage.BlobSourceOption; import com.google.gcloud.storage.Storage.BlobTargetOption; @@ -34,7 +35,7 @@ public final class Bucket { private final Storage storage; - private BucketInfo info; + private final BucketInfo info; /** * Construct a {@code Bucket} object for the provided {@code BucketInfo}. The storage service is @@ -78,15 +79,28 @@ public boolean exists() { } /** - * Update the bucket's information. Bucket's name cannot be changed. + * Fetches current bucket's latest information. + * + * @param options bucket read options + * @return a {@code Bucket} object with latest information + * @throws StorageException upon failure + */ + public Bucket reload(BucketSourceOption... options) { + return new Bucket(storage, storage.get(info.name(), options)); + } + + /** + * Update the bucket's information. Bucket's name cannot be changed. A new {@code Bucket} object + * is returned. * * @param bucketInfo new bucket's information. Name must match the one of the current bucket * @param options update options + * @return a {@code Bucket} object with updated information * @throws StorageException upon failure */ - public void update(BucketInfo bucketInfo, BucketTargetOption... options) { + public Bucket update(BucketInfo bucketInfo, BucketTargetOption... options) { checkArgument(Objects.equals(bucketInfo.name(), info.name()), "Bucket name must match"); - info = storage.update(bucketInfo, options); + return new Bucket(storage, storage.update(bucketInfo, options)); } /** 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 a46d4d92d96c..4923b8b772e8 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 @@ -81,14 +81,24 @@ public void testContent() throws Exception { assertArrayEquals(content, blob.content()); } + @Test + public void testReload() throws Exception { + BlobInfo updatedInfo = BLOB_INFO.toBuilder().cacheControl("c").build(); + expect(storage.get(BLOB_INFO.bucket(), BLOB_INFO.name())).andReturn(updatedInfo); + replay(storage); + Blob updatedBlob = blob.reload(); + assertSame(storage, blob.storage()); + assertEquals(updatedInfo, updatedBlob.info()); + } + @Test public void testUpdate() throws Exception { BlobInfo updatedInfo = BLOB_INFO.toBuilder().cacheControl("c").build(); expect(storage.update(updatedInfo)).andReturn(updatedInfo); replay(storage); - blob.update(updatedInfo); + Blob updatedBlob = blob.update(updatedInfo); assertSame(storage, blob.storage()); - assertEquals(updatedInfo, blob.info()); + assertEquals(updatedInfo, updatedBlob.info()); } @Test 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 9dd4235c16b9..9b8546ecc27d 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 @@ -80,14 +80,24 @@ public void testExists_False() throws Exception { assertFalse(bucket.exists()); } + @Test + public void testReload() throws Exception { + BucketInfo updatedInfo = BUCKET_INFO.toBuilder().notFoundPage("p").build(); + expect(storage.get(updatedInfo.name())).andReturn(updatedInfo); + replay(storage); + Bucket updatedBucket = bucket.reload(); + assertSame(storage, bucket.storage()); + assertEquals(updatedInfo, updatedBucket.info()); + } + @Test public void testUpdate() throws Exception { BucketInfo updatedInfo = BUCKET_INFO.toBuilder().notFoundPage("p").build(); expect(storage.update(updatedInfo)).andReturn(updatedInfo); replay(storage); - bucket.update(updatedInfo); + Bucket updatedBucket = bucket.update(updatedInfo); assertSame(storage, bucket.storage()); - assertEquals(updatedInfo, bucket.info()); + assertEquals(updatedInfo, updatedBucket.info()); } @Test From 2e90926ac9042d6285704209eee1807ae273b2a8 Mon Sep 17 00:00:00 2001 From: Marco Ziccardi Date: Thu, 24 Sep 2015 14:13:05 +0200 Subject: [PATCH 10/12] Use transform in BlobListResult --- .../google/gcloud/storage/BlobListResult.java | 34 +++++-------------- 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobListResult.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobListResult.java index 9bc53fd94a9b..9e6ec9dc5655 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobListResult.java +++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobListResult.java @@ -18,6 +18,9 @@ import static com.google.common.base.Preconditions.checkNotNull; +import com.google.common.base.Function; +import com.google.common.collect.Iterators; + import java.util.Iterator; import java.util.Objects; @@ -29,30 +32,6 @@ public class BlobListResult implements ListResult { private final ListResult infoList; private final Storage storage; - private class BlobListIterator implements Iterator { - - private final Iterator blobInfoIterator; - - public BlobListIterator() { - this.blobInfoIterator = infoList.iterator(); - } - - @Override - public boolean hasNext() { - return blobInfoIterator.hasNext(); - } - - @Override - public Blob next() { - return new Blob(storage, blobInfoIterator.next()); - } - - @Override - public void remove() { - blobInfoIterator.remove(); - } - } - public BlobListResult(Storage storage, ListResult infoList) { this.storage = checkNotNull(storage); this.infoList = checkNotNull(infoList); @@ -74,7 +53,12 @@ public ListResult nextPage() { @Override public Iterator iterator() { - return new BlobListIterator(); + return Iterators.transform(infoList.iterator(), new Function() { + @Override + public Blob apply(BlobInfo info) { + return new Blob(storage, info); + } + }); } @Override From eb9ea81f9e2aa52558db21642cff7fc656aa4316 Mon Sep 17 00:00:00 2001 From: Marco Ziccardi Date: Fri, 25 Sep 2015 10:27:42 +0200 Subject: [PATCH 11/12] Update Blob and Bucket javadoc --- .../src/main/java/com/google/gcloud/storage/Blob.java | 11 ++++++++++- .../main/java/com/google/gcloud/storage/Bucket.java | 11 ++++++++++- 2 files changed, 20 insertions(+), 2 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 f740a2e716ff..d3fe181970c8 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 @@ -30,6 +30,12 @@ /** * A Google cloud storage object. + * + *

+ * Objects of this class are immutable. Operations that modify the blob like {@link #update} and + * {@link #copyTo} return a new object. To get a {@code Blob} object with the most recent + * information use {@link #reload}. + *

*/ public final class Blob { @@ -151,7 +157,10 @@ public Blob reload(BlobSourceOption... options) { /** * Update the blob's information. Bucket or blob's name cannot be changed by this method. If you * want to rename the blob or move it to a different bucket use the {@link #copyTo} and - * {@link #delete} operations. A new {@code Blob} object is returned. + * {@link #delete} operations. A new {@code Blob} object is returned. By default no checks are + * made on the metadata generation of the current blob. If you want to update the information only + * if the current blob metadata are at their latest version use the {@code metagenerationMatch} + * option: {@code blob.update(newInfo, BlobTargetOption.metagenerationMatch()}. * * @param blobInfo new blob's information. Bucket and blob names must match the current ones * @param options update options 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 47c16a3646ed..b56824f069f6 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 @@ -31,6 +31,12 @@ /** * A Google cloud storage bucket. + * + *

+ * Objects of this class are immutable. Operations that modify the bucket like {@link #update} + * return a new object. To get a {@code Bucket} object with the most recent information use + * {@link #reload}. + *

*/ public final class Bucket { @@ -91,7 +97,10 @@ public Bucket reload(BucketSourceOption... options) { /** * Update the bucket's information. Bucket's name cannot be changed. A new {@code Bucket} object - * is returned. + * is returned. By default no checks are made on the metadata generation of the current bucket. + * If you want to update the information only if the current bucket metadata are at their latest + * version use the {@code metagenerationMatch} option: + * {@code bucket.update(newInfo, BucketTargetOption.metagenerationMatch());} * * @param bucketInfo new bucket's information. Name must match the one of the current bucket * @param options update options From fa7d7fc0a6cba5a30c727949ab860fefcdc92d82 Mon Sep 17 00:00:00 2001 From: Marco Ziccardi Date: Fri, 25 Sep 2015 21:46:58 +0200 Subject: [PATCH 12/12] Minor updates javadoc Blob, Bucket, ListResult --- .../java/com/google/gcloud/storage/Blob.java | 30 ++++++++++--------- .../com/google/gcloud/storage/Bucket.java | 29 +++++++++--------- .../com/google/gcloud/storage/ListResult.java | 4 +-- 3 files changed, 32 insertions(+), 31 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 d3fe181970c8..e0863d23a70c 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 @@ -92,7 +92,7 @@ static Storage.BlobSourceOption[] convert(BlobInfo blobInfo, BlobSourceOption... } /** - * Construct a {@code Blob} object for the provided {@code BlobInfo}. The storage service is used + * Constructs a {@code Blob} object for the provided {@code BlobInfo}. The storage service is used * to issue requests. * * @param storage the storage service used for issuing requests @@ -104,7 +104,7 @@ public Blob(Storage storage, BlobInfo info) { } /** - * Construct a {@code Blob} object for the provided bucket and blob names. The storage service is + * Constructs a {@code Blob} object for the provided bucket and blob names. The storage service is * used to issue requests. * * @param storage the storage service used for issuing requests @@ -117,14 +117,14 @@ public Blob(Storage storage, String bucket, String blob) { } /** - * Return the blob's information. + * Returns the blob's information. */ public BlobInfo info() { return info; } /** - * Check if this blob exists. + * Checks if this blob exists. * * @return true if this blob exists, false otherwise * @throws StorageException upon failure @@ -134,7 +134,7 @@ public boolean exists() { } /** - * Return this blob's content. + * Returns this blob's content. * * @param options blob read options * @throws StorageException upon failure @@ -155,12 +155,12 @@ public Blob reload(BlobSourceOption... options) { } /** - * Update the blob's information. Bucket or blob's name cannot be changed by this method. If you + * Updates the blob's information. Bucket or blob's name cannot be changed by this method. If you * want to rename the blob or move it to a different bucket use the {@link #copyTo} and * {@link #delete} operations. A new {@code Blob} object is returned. By default no checks are * made on the metadata generation of the current blob. If you want to update the information only * if the current blob metadata are at their latest version use the {@code metagenerationMatch} - * option: {@code blob.update(newInfo, BlobTargetOption.metagenerationMatch()}. + * option: {@code blob.update(newInfo, BlobTargetOption.metagenerationMatch())}. * * @param blobInfo new blob's information. Bucket and blob names must match the current ones * @param options update options @@ -174,7 +174,7 @@ public Blob update(BlobInfo blobInfo, BlobTargetOption... options) { } /** - * Delete this blob. + * Deletes this blob. * * @param options blob delete options * @return true if blob was deleted @@ -185,7 +185,8 @@ public boolean delete(BlobSourceOption... options) { } /** - * Copy this blob to the target bucket, preserving its name. Possibly update metadata. + * Copies this blob to the target bucket, preserving its name. Possibly copying also some of the + * metadata (e.g. content-type). * * @param targetBucket target bucket's name * @param options source blob options @@ -197,7 +198,8 @@ public Blob copyTo(String targetBucket, BlobSourceOption... options) { } /** - * Copy this blob to the target bucket with a new name. Possibly update metadata. + * Copies this blob to the target bucket with a new name. Possibly copying also some of the + * metadata (e.g. content-type). * * @param targetBucket target bucket's name * @param targetBlob target blob's name @@ -214,7 +216,7 @@ public Blob copyTo(String targetBucket, String targetBlob, BlobSourceOption... o } /** - * Return a {@code BlobReadChannel} object for reading this blob's content. + * Returns a {@code BlobReadChannel} object for reading this blob's content. * * @param options blob read options * @throws StorageException upon failure @@ -224,7 +226,7 @@ public BlobReadChannel reader(BlobSourceOption... options) { } /** - * Return a {@code BlobWriteChannel} object for writing to this blob. + * Returns a {@code BlobWriteChannel} object for writing to this blob. * * @param options target blob options * @throws StorageException upon failure @@ -234,7 +236,7 @@ public BlobWriteChannel writer(BlobTargetOption... options) { } /** - * Generate a signed URL for this blob. If you want to allow access to for a fixed amount of time + * Generates a signed URL for this blob. If you want to allow access to for a fixed amount of time * for this blob, you can use this method to generate a URL that is only valid within a certain * time period. This is particularly useful if you don't want publicly accessible blobs, but don't * want to require users to explicitly log in. @@ -249,7 +251,7 @@ public URL signUrl(long expirationTimeInSeconds, SignUrlOption... options) { } /** - * Return the blob's {@code Storage} object used to issue requests. + * Returns the blob's {@code Storage} object used to issue requests. */ public Storage storage() { return storage; 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 b56824f069f6..d7cac447d051 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 @@ -18,14 +18,13 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.gcloud.storage.Blob.BlobSourceOption.convert; import com.google.gcloud.storage.Storage.BlobSourceOption; import com.google.gcloud.storage.Storage.BlobTargetOption; import com.google.gcloud.storage.Storage.BucketSourceOption; import com.google.gcloud.storage.Storage.BucketTargetOption; -import java.util.LinkedList; +import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -44,7 +43,7 @@ public final class Bucket { private final BucketInfo info; /** - * Construct a {@code Bucket} object for the provided {@code BucketInfo}. The storage service is + * Constructs a {@code Bucket} object for the provided {@code BucketInfo}. The storage service is * used to issue requests. * * @param storage the storage service used for issuing requests @@ -56,7 +55,7 @@ public Bucket(Storage storage, BucketInfo info) { } /** - * Construct a {@code Bucket} object for the provided name. The storage service is used to issue + * Constructs a {@code Bucket} object for the provided name. The storage service is used to issue * requests. * * @param storage the storage service used for issuing requests @@ -68,14 +67,14 @@ public Bucket(Storage storage, String bucket) { } /** - * Return the bucket's information. + * Returns the bucket's information. */ public BucketInfo info() { return info; } /** - * Check if this bucket exists. + * Checks if this bucket exists. * * @return true if this bucket exists, false otherwise * @throws StorageException upon failure @@ -96,11 +95,11 @@ public Bucket reload(BucketSourceOption... options) { } /** - * Update the bucket's information. Bucket's name cannot be changed. A new {@code Bucket} object + * Updates the bucket's information. Bucket's name cannot be changed. A new {@code Bucket} object * is returned. By default no checks are made on the metadata generation of the current bucket. * If you want to update the information only if the current bucket metadata are at their latest * version use the {@code metagenerationMatch} option: - * {@code bucket.update(newInfo, BucketTargetOption.metagenerationMatch());} + * {@code bucket.update(newInfo, BucketTargetOption.metagenerationMatch())} * * @param bucketInfo new bucket's information. Name must match the one of the current bucket * @param options update options @@ -113,7 +112,7 @@ public Bucket update(BucketInfo bucketInfo, BucketTargetOption... options) { } /** - * Delete this bucket. + * Deletes this bucket. * * @param options bucket delete options * @return true if bucket was deleted @@ -124,7 +123,7 @@ public boolean delete(BucketSourceOption... options) { } /** - * Return the paginated list of {@code Blob} in this bucket. + * Returns the paginated list of {@code Blob} in this bucket. * * @param options options for listing blobs * @throws StorageException upon failure @@ -134,7 +133,7 @@ public ListResult list(Storage.BlobListOption... options) { } /** - * Return the requested blob in this bucket or {@code null} if not found. + * Returns the requested blob in this bucket or {@code null} if not found. * * @param blob name of the requested blob * @param options blob search options @@ -145,7 +144,7 @@ public Blob get(String blob, BlobSourceOption... options) { } /** - * Return a list of requested blobs in this bucket. Blobs that do not exist are null. + * Returns a list of requested blobs in this bucket. Blobs that do not exist are null. * * @param blobNames names of the requested blobs * @throws StorageException upon failure @@ -155,7 +154,7 @@ public List getAll(String... blobNames) { for (String blobName : blobNames) { batch.get(info.name(), blobName); } - List blobs = new LinkedList<>(); + List blobs = new ArrayList<>(blobNames.length); BatchResponse response = storage.apply(batch.build()); for (BatchResponse.Result result : response.gets()) { BlobInfo blobInfo = result.get(); @@ -165,7 +164,7 @@ public List getAll(String... blobNames) { } /** - * Create a new blob in this bucket. + * Creates a new blob in this bucket. * * @param blob a blob name * @param content the blob content @@ -179,7 +178,7 @@ Blob create(String blob, byte[] content, BlobTargetOption... options) { } /** - * Return the bucket's {@code Storage} object used to issue requests. + * Returns the bucket's {@code Storage} object used to issue requests. */ public Storage storage() { return storage; diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/ListResult.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/ListResult.java index a07d6edff745..62b1f442310c 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/ListResult.java +++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/ListResult.java @@ -22,12 +22,12 @@ public interface ListResult extends Iterable { /** - * Return the cursor for the nextPage or {@code null} if no more results. + * Returns the cursor for the nextPage or {@code null} if no more results. */ String nextPageCursor(); /** - * Return the results of the nextPage or {@code null} if no more result. + * Returns the results of the nextPage or {@code null} if no more result. */ ListResult nextPage();