From 9ac507980dcef1a67d437b8be716cb59d8394213 Mon Sep 17 00:00:00 2001
From: Marco Ziccardi
Date: Wed, 17 Aug 2016 15:34:55 +0200
Subject: [PATCH] Add snippets to Blob's javadoc, BlobSnippets class and tests
---
.../storage/snippets/BlobSnippets.java | 231 ++++++++++++++++++
.../storage/snippets/ITBlobSnippets.java | 123 ++++++++++
.../java/com/google/cloud/storage/Blob.java | 112 +++++++--
3 files changed, 452 insertions(+), 14 deletions(-)
create mode 100644 gcloud-java-examples/src/main/java/com/google/cloud/examples/storage/snippets/BlobSnippets.java
create mode 100644 gcloud-java-examples/src/test/java/com/google/cloud/example/storage/snippets/ITBlobSnippets.java
diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/storage/snippets/BlobSnippets.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/storage/snippets/BlobSnippets.java
new file mode 100644
index 000000000000..2ffcd4dd5a38
--- /dev/null
+++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/storage/snippets/BlobSnippets.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright 2016 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.
+ */
+
+/*
+ * EDITING INSTRUCTIONS
+ * This file is referenced in Blob's javadoc. Any change to this file should be reflected in Blob's
+ * javadoc.
+ */
+
+package com.google.cloud.examples.storage.snippets;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import com.google.cloud.AuthCredentials;
+import com.google.cloud.ReadChannel;
+import com.google.cloud.ServiceAccountSigner;
+import com.google.cloud.WriteChannel;
+import com.google.cloud.storage.Blob;
+import com.google.cloud.storage.Blob.BlobSourceOption;
+import com.google.cloud.storage.BlobId;
+import com.google.cloud.storage.CopyWriter;
+import com.google.cloud.storage.Storage.SignUrlOption;
+import com.google.cloud.storage.StorageException;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.net.URL;
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * This class contains a number of snippets for the {@link Blob} class.
+ */
+public class BlobSnippets {
+
+ private final Blob blob;
+
+ public BlobSnippets(Blob blob) {
+ this.blob = blob;
+ }
+
+ /**
+ * Example of checking if the blob exists.
+ */
+ // [TARGET exists(BlobSourceOption...)]
+ public boolean exists() {
+ // [START exists]
+ boolean exists = blob.exists();
+ if (exists) {
+ // the blob exists
+ } else {
+ // the blob was not found
+ }
+ // [END exists]
+ return exists;
+ }
+
+ /**
+ * Example of reading all bytes of the blob, if its generation matches the
+ * {@link Blob#generation()} value, otherwise a {@link StorageException} is thrown.
+ */
+ // [TARGET content(BlobSourceOption...)]
+ public byte[] content() {
+ // [START content]
+ byte[] content = blob.content(BlobSourceOption.generationMatch());
+ // [END content]
+ return content;
+ }
+
+ /**
+ * Example of getting the blob's latest information, if its generation does not match the
+ * {@link Blob#generation()} value, otherwise a {@link StorageException} is thrown.
+ */
+ // [TARGET reload(BlobSourceOption...)]
+ public Blob reload() {
+ // [START reload]
+ Blob latestBlob = blob.reload(BlobSourceOption.generationNotMatch());
+ if (latestBlob == null) {
+ // the blob was not found
+ }
+ // [END reload]
+ return latestBlob;
+ }
+
+ /**
+ * Example of replacing blob's metadata.
+ */
+ // [TARGET update(BlobTargetOption...)]
+ public Blob update() {
+ // [START update]
+ Map newMetadata = new HashMap<>();
+ newMetadata.put("key", "value");
+ blob.toBuilder().metadata(null).build().update();
+ Blob updatedBlob = blob.toBuilder().metadata(newMetadata).build().update();
+ // [END update]
+ return updatedBlob;
+ }
+
+ /**
+ * Example of deleting the blob, if its generation matches the {@link Blob#generation()} value,
+ * otherwise a {@link StorageException} is thrown.
+ */
+ // [TARGET delete(BlobSourceOption...)]
+ public boolean delete() {
+ // [START delete]
+ boolean deleted = blob.delete(BlobSourceOption.generationMatch());
+ if (deleted) {
+ // the blob was deleted
+ } else {
+ // the blob was not found
+ }
+ // [END delete]
+ return deleted;
+ }
+
+ /**
+ * Example of copying the blob to a different bucket with a different name.
+ */
+ // [TARGET copyTo(BlobId, BlobSourceOption...)]
+ // [VARIABLE "my_unique_bucket"]
+ // [VARIABLE "copy_blob_name"]
+ public Blob copyToId(String bucketName, String blobName) {
+ // [START copyToId]
+ CopyWriter copyWriter = blob.copyTo(BlobId.of(bucketName, blobName));
+ Blob copiedBlob = copyWriter.result();
+ // [END copyToId]
+ return copiedBlob;
+ }
+
+ /**
+ * Example of copying the blob to a different bucket, keeping the original name.
+ */
+ // [TARGET copyTo(String, BlobSourceOption...)]
+ // [VARIABLE "my_unique_bucket"]
+ public Blob copyToBucket(String bucketName) {
+ // [START copyToBucket]
+ CopyWriter copyWriter = blob.copyTo(bucketName);
+ Blob copiedBlob = copyWriter.result();
+ // [END copyToBucket]
+ return copiedBlob;
+ }
+
+ /**
+ * Example of copying the blob to a different bucket with a different name.
+ */
+ // [TARGET copyTo(String, String, BlobSourceOption...)]
+ // [VARIABLE "my_unique_bucket"]
+ // [VARIABLE "copy_blob_name"]
+ public Blob copyToStrings(String bucketName, String blobName) {
+ // [START copyToStrings]
+ CopyWriter copyWriter = blob.copyTo(bucketName, blobName);
+ Blob copiedBlob = copyWriter.result();
+ // [END copyToStrings]
+ return copiedBlob;
+ }
+
+ /**
+ * Example of reading the blob's content through a reader.
+ */
+ // [TARGET reader(BlobSourceOption...)]
+ public void reader() throws IOException {
+ // [START reader]
+ try (ReadChannel reader = blob.reader()) {
+ ByteBuffer bytes = ByteBuffer.allocate(64 * 1024);
+ while (reader.read(bytes) > 0) {
+ bytes.flip();
+ // do something with bytes
+ bytes.clear();
+ }
+ }
+ // [END reader]
+ }
+
+ /**
+ * Example of writing the blob's content through a writer.
+ */
+ // [TARGET writer(BlobWriteOption...)]
+ public void writer() throws IOException {
+ // [START writer]
+ byte[] content = "Hello, World!".getBytes(UTF_8);
+ try (WriteChannel writer = blob.writer()) {
+ try {
+ writer.write(ByteBuffer.wrap(content, 0, content.length));
+ } catch (Exception ex) {
+ // handle exception
+ }
+ }
+ // [END writer]
+ }
+
+ /**
+ * Example of creating a signed URL for the blob that is valid for 2 weeks, using the default
+ * credentials for signing the URL.
+ */
+ // [TARGET signUrl(long, TimeUnit, SignUrlOption...)]
+ public URL signUrl() {
+ // [START signUrl]
+ URL signedUrl = blob.signUrl(14, TimeUnit.DAYS);
+ // [END signUrl]
+ return signedUrl;
+ }
+
+ /**
+ * Example of creating a signed URL for the blob passing the
+ * {@link SignUrlOption#signWith(ServiceAccountSigner)} option, that will be used to sign the URL.
+ */
+ // [TARGET signUrl(long, TimeUnit, SignUrlOption...)]
+ // [VARIABLE "/path/to/key.json"]
+ public URL signUrlWithSigner(String keyPath) throws IOException {
+ // [START signUrlWithSigner]
+ URL signedUrl = blob.signUrl(14, TimeUnit.DAYS, SignUrlOption.signWith(
+ AuthCredentials.createForJson(new FileInputStream(keyPath))));
+ // [END signUrlWithSigner]
+ return signedUrl;
+ }
+}
diff --git a/gcloud-java-examples/src/test/java/com/google/cloud/example/storage/snippets/ITBlobSnippets.java b/gcloud-java-examples/src/test/java/com/google/cloud/example/storage/snippets/ITBlobSnippets.java
new file mode 100644
index 000000000000..39c3e5d5b6db
--- /dev/null
+++ b/gcloud-java-examples/src/test/java/com/google/cloud/example/storage/snippets/ITBlobSnippets.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2016 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.cloud.example.storage.snippets;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.google.cloud.examples.storage.snippets.BlobSnippets;
+import com.google.cloud.storage.Blob;
+import com.google.cloud.storage.BlobId;
+import com.google.cloud.storage.BlobInfo;
+import com.google.cloud.storage.BucketInfo;
+import com.google.cloud.storage.Storage;
+import com.google.cloud.storage.StorageException;
+import com.google.cloud.storage.testing.RemoteStorageHelper;
+import com.google.common.collect.ImmutableMap;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class ITBlobSnippets {
+
+ private static final Logger log = Logger.getLogger(ITBlobSnippets.class.getName());
+ private static final String BUCKET = RemoteStorageHelper.generateBucketName();
+ private static final String BLOB = "blob";
+ private static final byte[] EMPTY_CONTENT = new byte[0];
+ private static final byte[] CONTENT = "Hello, World!".getBytes(UTF_8);
+
+ private static Storage storage;
+ private static Blob blob;
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ @BeforeClass
+ public static void beforeClass() {
+ RemoteStorageHelper helper = RemoteStorageHelper.create();
+ storage = helper.options().service();
+ storage.create(BucketInfo.of(BUCKET));
+ blob = storage.create(BlobInfo.builder(BUCKET, BLOB).build());
+ }
+
+ @AfterClass
+ public static void afterClass() throws ExecutionException, InterruptedException {
+ if (storage != null) {
+ boolean wasDeleted = RemoteStorageHelper.forceDelete(storage, BUCKET, 5, TimeUnit.SECONDS);
+ if (!wasDeleted && log.isLoggable(Level.WARNING)) {
+ log.log(Level.WARNING, "Deletion of bucket {0} timed out, bucket is not empty", BUCKET);
+ }
+ }
+ }
+
+ @Test
+ public void testBlob() throws IOException {
+ BlobSnippets blobSnippets = new BlobSnippets(blob);
+ assertTrue(blobSnippets.exists());
+ assertArrayEquals(EMPTY_CONTENT, blobSnippets.content());
+ try {
+ assertNotNull(blobSnippets.reload());
+ fail("Expected StorageException to be thrown");
+ } catch (StorageException ex) {
+ // expected
+ }
+ Blob updatedBlob = blobSnippets.update();
+ assertEquals(ImmutableMap.of("key", "value"), updatedBlob.metadata());
+ Blob copiedBlob = blobSnippets.copyToStrings(BUCKET, "copyBlob");
+ assertNotNull(copiedBlob);
+ copiedBlob.delete();
+ copiedBlob = blobSnippets.copyToId(BUCKET, "copyBlob");
+ assertNotNull(copiedBlob);
+ copiedBlob.delete();
+ copiedBlob = blobSnippets.copyToBucket(BUCKET);
+ assertNotNull(copiedBlob);
+ blobSnippets.reload();
+ blobSnippets.writer();
+ URL signedUrl = blobSnippets.signUrl();
+ URLConnection connection = signedUrl.openConnection();
+ byte[] readBytes = new byte[CONTENT.length];
+ try (InputStream responseStream = connection.getInputStream()) {
+ assertEquals(CONTENT.length, responseStream.read(readBytes));
+ assertArrayEquals(CONTENT, readBytes);
+ }
+ signedUrl = blobSnippets.signUrlWithSigner(System.getenv("GOOGLE_APPLICATION_CREDENTIALS"));
+ connection = signedUrl.openConnection();
+ try (InputStream responseStream = connection.getInputStream()) {
+ assertEquals(CONTENT.length, responseStream.read(readBytes));
+ assertArrayEquals(CONTENT, readBytes);
+ }
+ assertFalse(blobSnippets.delete());
+ storage.delete(BlobId.of(BUCKET, BLOB));
+ }
+}
diff --git a/gcloud-java-storage/src/main/java/com/google/cloud/storage/Blob.java b/gcloud-java-storage/src/main/java/com/google/cloud/storage/Blob.java
index 3cbb3826541f..139313ad835e 100644
--- a/gcloud-java-storage/src/main/java/com/google/cloud/storage/Blob.java
+++ b/gcloud-java-storage/src/main/java/com/google/cloud/storage/Blob.java
@@ -322,6 +322,16 @@ public Blob build() {
/**
* Checks if this blob exists.
*
+ * Example of checking if the blob exists.
+ *
{@code
+ * boolean exists = blob.exists();
+ * if (exists) {
+ * // the blob exists
+ * } else {
+ * // the blob was not found
+ * }
+ * }
+ *
* @param options blob read options
* @return true if this blob exists, false otherwise
* @throws StorageException upon failure
@@ -336,16 +346,31 @@ public boolean exists(BlobSourceOption... options) {
/**
* Returns this blob's content.
*
+ * Example of reading all bytes of the blob, if its generation matches the
+ * {@link Blob#generation()} value, otherwise a {@link StorageException} is thrown.
+ *
{@code
+ * byte[] content = blob.content(BlobSourceOption.generationMatch());
+ * }
+ *
* @param options blob read options
* @throws StorageException upon failure
*/
- public byte[] content(Storage.BlobSourceOption... options) {
- return storage.readAllBytes(blobId(), options);
+ public byte[] content(BlobSourceOption... options) {
+ return storage.readAllBytes(blobId(), toSourceOptions(this, options));
}
/**
* Fetches current blob's latest information. Returns {@code null} if the blob does not exist.
*
+ * Example of getting the blob's latest information, if its generation does not match the
+ * {@link Blob#generation()} value, otherwise a {@link StorageException} is thrown.
+ *
{@code
+ * Blob latestBlob = blob.reload(BlobSourceOption.generationNotMatch());
+ * if (latestBlob == null) {
+ * // the blob was not found
+ * }
+ * }
+ *
* @param options blob read options
* @return a {@code Blob} object with latest information or {@code null} if not found
* @throws StorageException upon failure
@@ -367,12 +392,13 @@ public Blob reload(BlobSourceOption... options) {
* {@code blob}'s metadata to {@code null}.
*
*
- * Example usage of replacing blob's metadata:
+ *
Example of replacing blob's metadata.
*
{@code
+ * Map newMetadata = new HashMap<>();
+ * newMetadata.put("key", "value");
* blob.toBuilder().metadata(null).build().update();
- * blob.toBuilder().metadata(newMetadata).build().update();
- * }
- *
+ * Blob updatedBlob = blob.toBuilder().metadata(newMetadata).build().update();
+ * }
*
* @param options update options
* @return a {@code Blob} object with updated information
@@ -385,6 +411,17 @@ public Blob update(BlobTargetOption... options) {
/**
* Deletes this blob.
*
+ * Example of deleting the blob, if its generation matches the {@link Blob#generation()} value,
+ * otherwise a {@link StorageException} is thrown.
+ *
{@code
+ * boolean deleted = blob.delete(BlobSourceOption.generationMatch());
+ * if (deleted) {
+ * // the blob was deleted
+ * } else {
+ * // the blob was not found
+ * }
+ * }
+ *
* @param options blob delete options
* @return {@code true} if blob was deleted, {@code false} if it was not found
* @throws StorageException upon failure
@@ -397,6 +434,14 @@ public boolean delete(BlobSourceOption... options) {
* Sends a copy request for the current blob to the target blob. Possibly also some of the
* metadata are copied (e.g. content-type).
*
+ * Example of copying the blob to a different bucket with a different name.
+ *
{@code
+ * String bucketName = "my_unique_bucket";
+ * String blobName = "copy_blob_name";
+ * CopyWriter copyWriter = blob.copyTo(BlobId.of(bucketName, blobName));
+ * Blob copiedBlob = copyWriter.result();
+ * }
+ *
* @param targetBlob target blob's id
* @param options source blob options
* @return a {@link CopyWriter} object that can be used to get information on the newly created
@@ -416,6 +461,13 @@ public CopyWriter copyTo(BlobId targetBlob, BlobSourceOption... options) {
* Sends a copy request for the current blob to the target bucket, preserving its name. Possibly
* copying also some of the metadata (e.g. content-type).
*
+ * Example of copying the blob to a different bucket, keeping the original name.
+ *
{@code
+ * String bucketName = "my_unique_bucket";
+ * CopyWriter copyWriter = blob.copyTo(bucketName);
+ * Blob copiedBlob = copyWriter.result();
+ * }
+ *
* @param targetBucket target bucket's name
* @param options source blob options
* @return a {@link CopyWriter} object that can be used to get information on the newly created
@@ -430,6 +482,14 @@ public CopyWriter copyTo(String targetBucket, BlobSourceOption... options) {
* Sends a copy request for the current blob to the target blob. Possibly also some of the
* metadata are copied (e.g. content-type).
*
+ * Example of copying the blob to a different bucket with a different name.
+ *
{@code
+ * String bucketName = "my_unique_bucket";
+ * String blobName = "copy_blob_name";
+ * CopyWriter copyWriter = blob.copyTo(bucketName, blobName);
+ * Blob copiedBlob = copyWriter.result();
+ * }
+ *
* @param targetBucket target bucket's name
* @param targetBlob target blob's name
* @param options source blob options
@@ -444,6 +504,18 @@ public CopyWriter copyTo(String targetBucket, String targetBlob, BlobSourceOptio
/**
* Returns a {@code ReadChannel} object for reading this blob's content.
*
+ * Example of reading the blob's content through a reader.
+ *
{@code
+ * try (ReadChannel reader = blob.reader()) {
+ * ByteBuffer bytes = ByteBuffer.allocate(64 * 1024);
+ * while (reader.read(bytes) > 0) {
+ * bytes.flip();
+ * // do something with bytes
+ * bytes.clear();
+ * }
+ * }
+ * }
+ *
* @param options blob read options
* @throws StorageException upon failure
*/
@@ -456,6 +528,18 @@ public ReadChannel reader(BlobSourceOption... options) {
* crc32c values in the current blob are ignored unless requested via the
* {@code BlobWriteOption.md5Match} and {@code BlobWriteOption.crc32cMatch} options.
*
+ * Example of writing the blob's content through a writer.
+ *
{@code
+ * byte[] content = "Hello, World!".getBytes(UTF_8);
+ * try (WriteChannel writer = blob.writer()) {
+ * try {
+ * writer.write(ByteBuffer.wrap(content, 0, content.length));
+ * } catch (Exception ex) {
+ * // handle exception
+ * }
+ * }
+ * }
+ *
* @param options target blob options
* @throws StorageException upon failure
*/
@@ -485,18 +569,18 @@ public WriteChannel writer(BlobWriteOption... options) {
* The default credentials, if no credentials were passed to {@link StorageOptions}
*
*
- * Example usage of creating a signed URL that is valid for 2 weeks, using the default
- * credentials for signing the URL:
+ *
Example of creating a signed URL for the blob that is valid for 2 weeks, using the default
+ * credentials for signing the URL.
*
{@code
- * blob.signUrl(14, TimeUnit.DAYS);
+ * URL signedUrl = blob.signUrl(14, TimeUnit.DAYS);
* }
*
- * Example usage of creating a signed URL passing the
- * {@link SignUrlOption#signWith(ServiceAccountSigner)} option, that will be used for signing the
- * URL:
+ *
Example of creating a signed URL for the blob passing the
+ * {@link SignUrlOption#signWith(ServiceAccountSigner)} option, that will be used to sign the URL.
*
{@code
- * blob.signUrl(14, TimeUnit.DAYS, SignUrlOption.signWith(
- * AuthCredentials.createForJson(new FileInputStream("/path/to/key.json"))));
+ * String keyPath = "/path/to/key.json";
+ * URL signedUrl = blob.signUrl(14, TimeUnit.DAYS, SignUrlOption.signWith(
+ * AuthCredentials.createForJson(new FileInputStream(keyPath))));
* }
*
* @param duration time until the signed URL expires, expressed in {@code unit}. The finer