diff --git a/README.md b/README.md index c0edb57fbf37..1b9103507b3c 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,7 @@ Here is a code snippet showing a simple usage example from within Compute/App En import static java.nio.charset.StandardCharsets.UTF_8; import com.google.gcloud.storage.Blob; +import com.google.gcloud.storage.BlobId; import com.google.gcloud.storage.Storage; import com.google.gcloud.storage.StorageFactory; import com.google.gcloud.storage.StorageOptions; @@ -111,12 +112,15 @@ import com.google.gcloud.storage.StorageOptions; import java.nio.ByteBuffer; import java.nio.channels.WritableByteChannel; -Storage storage = StorageFactory.instance().get(StorageOptions.getDefaultInstance()); -Blob blob = new Blob(storage, "bucket", "blob_name"); -if (!blob.exists()) { - storage2.create(blob.info(), "Hello, Cloud Storage!".getBytes(UTF_8)); +StorageOptions options = StorageOptions.builder().projectId("project").build(); +Storage storage = StorageFactory.instance().get(options); +BlobId blobId = BlobId.of("bucket", "blob_name"); +Blob blob = Blob.load(storage, blobId); +if (blob == null) { + BlobInfo blobInfo = BlobInfo.builder(blobId).contentType("text/plain").build(); + storage.create(blobInfo, "Hello, Cloud Storage!".getBytes(UTF_8)); } else { - System.out.println("Updating content for " + blob.info().name()); + System.out.println("Updating content for " + blobId.name()); byte[] prevContent = blob.content(); System.out.println(new String(prevContent, UTF_8)); WritableByteChannel channel = blob.writer(); diff --git a/gcloud-java-examples/src/main/java/com/google/gcloud/examples/StorageExample.java b/gcloud-java-examples/src/main/java/com/google/gcloud/examples/StorageExample.java index d1bf338ff301..d47494b953fc 100644 --- a/gcloud-java-examples/src/main/java/com/google/gcloud/examples/StorageExample.java +++ b/gcloud-java-examples/src/main/java/com/google/gcloud/examples/StorageExample.java @@ -20,9 +20,8 @@ import com.google.gcloud.AuthCredentials.ServiceAccountAuthCredentials; import com.google.gcloud.RetryParams; import com.google.gcloud.spi.StorageRpc.Tuple; -import com.google.gcloud.storage.BatchRequest; -import com.google.gcloud.storage.BatchResponse; import com.google.gcloud.storage.Blob; +import com.google.gcloud.storage.BlobId; import com.google.gcloud.storage.BlobInfo; import com.google.gcloud.storage.BlobReadChannel; import com.google.gcloud.storage.BlobWriteChannel; @@ -42,6 +41,7 @@ import java.nio.ByteBuffer; import java.nio.channels.Channels; import java.nio.channels.WritableByteChannel; +import static java.nio.charset.StandardCharsets.UTF_8; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -52,8 +52,8 @@ import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; import java.util.Arrays; -import java.util.Calendar; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -89,39 +89,23 @@ private static abstract class StorageAction { abstract void run(Storage storage, T request) throws Exception; - abstract T parse(Storage storage, String... args) throws Exception; + abstract T parse(String... args) throws Exception; protected String params() { return ""; } } - private static abstract class BlobAction extends StorageAction { + private static abstract class BlobsAction extends StorageAction { @Override - Blob parse(Storage storage, String... args) { - if (args.length != 2) { - throw new IllegalArgumentException(); - } - return new Blob(storage, args[0], args[1]); - } - - @Override - public String params() { - return " "; - } - } - - private static abstract class BlobsAction extends StorageAction { - - @Override - Blob[] parse(Storage storage, String... args) { + BlobId[] parse(String... args) { if (args.length < 2) { throw new IllegalArgumentException(); } - Blob[] blobs = new Blob[args.length - 1]; + BlobId[] blobs = new BlobId[args.length - 1]; for (int i = 1; i < args.length; i++) { - blobs[i - 1] = new Blob(storage, args[0], args[i]); + blobs[i - 1] = BlobId.of(args[0], args[i]); } return blobs; } @@ -141,35 +125,42 @@ public String params() { */ private static class InfoAction extends BlobsAction { @Override - public void run(Storage storage, Blob... blobs) { - if (blobs.length == 1) { - if (blobs[0].info().name().isEmpty()) { + public void run(Storage storage, BlobId... blobIds) { + if (blobIds.length == 1) { + if (blobIds[0].name().isEmpty()) { // get Bucket - Bucket bucket = new Bucket(storage, blobs[0].info().bucket()); - System.out.println("Bucket info: " + bucket.reload().info()); + Bucket bucket = Bucket.load(storage, blobIds[0].bucket()); + if (bucket == null) { + System.out.println("No such bucket"); + return; + } + System.out.println("Bucket info: " + bucket.info()); } else { // get Blob - System.out.println("Blob info: " + blobs[0].reload().info()); + Blob blob = Blob.load(storage, blobIds[0]); + if (blob == null) { + System.out.println("No such object"); + return; + } + System.out.println("Blob info: " + blob.info()); } } else { // use batch to get multiple blobs. - BatchRequest.Builder batch = BatchRequest.builder(); + List blobs = Blob.get(storage, blobIds); for (Blob blob : blobs) { - batch.get(blob.info().bucket(), blob.info().name()); - } - BatchResponse response = storage.apply(batch.build()); - for (BatchResponse.Result result : response.gets()) { - System.out.println(result.get()); + if (blob != null) { + System.out.println(blob.info()); + } } } } @Override - Blob[] parse(Storage storage, String... args) { + BlobId[] parse(String... args) { if (args.length < 2) { - return new Blob[] {new Blob(storage, args[0], "")}; + return new BlobId[] {BlobId.of(args[0], "")}; } - return super.parse(storage, args); + return super.parse(args); } @Override @@ -187,27 +178,16 @@ public String params() { */ private static class DeleteAction extends BlobsAction { @Override - public void run(Storage storage, Blob... blobs) { - if (blobs.length == 1) { - boolean wasDeleted = blobs[0].delete(); - if (wasDeleted) { - System.out.println("Blob " + blobs[0].info() + " was deleted"); - } - } else { - // use batch operation - BatchRequest.Builder batch = BatchRequest.builder(); - for (Blob blob : blobs) { - batch.delete(blob.info().bucket(), blob.info().name()); - } - int index = 0; - BatchResponse response = storage.apply(batch.build()); - for (BatchResponse.Result result : response.deletes()) { - if (result.get()) { - // request order is maintained - System.out.println("Blob " + blobs[index].info() + " was deleted"); - } - index++; + public void run(Storage storage, BlobId... blobIds) { + // use batch operation + List deleteResults = Blob.delete(storage, blobIds); + int index = 0; + for (Boolean deleted : deleteResults) { + if (deleted) { + // request order is maintained + System.out.println("Blob " + blobIds[index] + " was deleted"); } + index++; } } } @@ -220,7 +200,7 @@ public void run(Storage storage, Blob... blobs) { private static class ListAction extends StorageAction { @Override - String parse(Storage storage, String... args) { + String parse(String... args) { if (args.length == 0) { return null; } @@ -239,7 +219,11 @@ public void run(Storage storage, String bucketName) { } } else { // list a bucket's blobs - Bucket bucket = new Bucket(storage, bucketName); + Bucket bucket = Bucket.load(storage, bucketName); + if (bucket == null) { + System.out.println("No such bucket"); + return; + } for (Blob b : bucket.list()) { System.out.println(b.info()); } @@ -257,16 +241,17 @@ public String params() { * * @see Objects: insert */ - private static class UploadAction extends StorageAction> { + private static class UploadAction extends StorageAction> { @Override - public void run(Storage storage, Tuple tuple) throws Exception { + public void run(Storage storage, Tuple tuple) throws Exception { run(storage, tuple.x(), tuple.y()); } - private void run(Storage storage, Path uploadFrom, Blob blob) throws IOException { + private void run(Storage storage, Path uploadFrom, BlobInfo blobInfo) throws IOException { if (Files.size(uploadFrom) > 1_000_000) { // When content is not available or large (1MB or more) it is recommended // to write it in chunks via the blob's channel writer. + Blob blob = new Blob(storage, blobInfo); try (BlobWriteChannel writer = blob.writer()) { byte[] buffer = new byte[1024]; try (InputStream input = Files.newInputStream(uploadFrom)) { @@ -283,21 +268,20 @@ private void run(Storage storage, Path uploadFrom, Blob blob) throws IOException } else { byte[] bytes = Files.readAllBytes(uploadFrom); // create the blob in one request. - storage.create(blob.info(), bytes); + storage.create(blobInfo, bytes); } System.out.println("Blob was created"); } @Override - Tuple parse(Storage storage, String... args) throws IOException { + Tuple parse(String... args) throws IOException { if (args.length < 2 || args.length > 3) { throw new IllegalArgumentException(); } Path path = Paths.get(args[0]); String contentType = Files.probeContentType(path); String blob = args.length < 3 ? path.getFileName().toString() : args[2]; - BlobInfo info = BlobInfo.builder(args[1], blob).contentType(contentType).build(); - return Tuple.of(path, new Blob(storage, info)); + return Tuple.of(path, BlobInfo.builder(args[1], blob).contentType(contentType).build()); } @Override @@ -313,16 +297,16 @@ public String params() { * * @see Objects: get */ - private static class DownloadAction extends StorageAction> { + private static class DownloadAction extends StorageAction> { @Override - public void run(Storage storage, Tuple tuple) throws IOException { + public void run(Storage storage, Tuple tuple) throws IOException { run(storage, tuple.x(), tuple.y()); } - private void run(Storage storage, Blob blob, Path downloadTo) throws IOException { - blob = blob.reload(); - if (!blob.exists()) { + private void run(Storage storage, BlobId blobId, Path downloadTo) throws IOException { + Blob blob = Blob.load(storage, blobId); + if (blob == null) { System.out.println("No such object"); return; } @@ -354,7 +338,7 @@ private void run(Storage storage, Blob blob, Path downloadTo) throws IOException } @Override - Tuple parse(Storage storage, String... args) { + Tuple parse(String... args) { if (args.length < 2 || args.length > 3) { throw new IllegalArgumentException(); } @@ -367,7 +351,7 @@ Tuple parse(Storage storage, String... args) { } else { path = null; } - return Tuple.of(new Blob(storage, args[0], args[1]), path); + return Tuple.of(BlobId.of(args[0], args[1]), path); } @Override @@ -389,7 +373,7 @@ public void run(Storage storage, CopyRequest request) { } @Override - CopyRequest parse(Storage storage, String... args) { + CopyRequest parse(String... args) { if (args.length != 4) { throw new IllegalArgumentException(); } @@ -415,7 +399,7 @@ public void run(Storage storage, ComposeRequest request) { } @Override - ComposeRequest parse(Storage storage, String... args) { + ComposeRequest parse(String... args) { if (args.length < 3) { throw new IllegalArgumentException(); } @@ -439,17 +423,17 @@ public String params() { * @see Objects: update */ private static class UpdateMetadataAction extends - StorageAction>> { + StorageAction>> { @Override - public void run(Storage storage, Tuple> tuple) + public void run(Storage storage, Tuple> tuple) throws IOException { run(storage, tuple.x(), tuple.y()); } - private void run(Storage storage, Blob blob, Map metadata) { - blob = blob.reload(); - if (!blob.exists()) { + private void run(Storage storage, BlobId blobId, Map metadata) { + Blob blob = Blob.load(storage, blobId); + if (blob == null) { System.out.println("No such object"); return; } @@ -458,11 +442,11 @@ private void run(Storage storage, Blob blob, Map metadata) { } @Override - Tuple> parse(Storage storage, String... args) { + Tuple> parse(String... args) { if (args.length < 2) { throw new IllegalArgumentException(); } - Blob blob = new Blob(storage, args[0], args[1]); + BlobId blobId = BlobId.of(args[0], args[1]); Map metadata = new HashMap<>(); for (int i = 2; i < args.length; i++) { int idx = args[i].indexOf('='); @@ -472,7 +456,7 @@ Tuple> parse(Storage storage, String... args) { metadata.put(args[i].substring(0, idx), args[i].substring(idx + 1)); } } - return Tuple.of(blob, metadata); + return Tuple.of(blobId, metadata); } @Override @@ -488,25 +472,26 @@ public String params() { * @see Signed URLs */ private static class SignUrlAction extends - StorageAction> { + StorageAction> { private static final char[] PASSWORD = "notasecret".toCharArray(); @Override - public void run(Storage storage, Tuple tuple) + public void run(Storage storage, Tuple tuple) throws Exception { run(storage, tuple.x(), tuple.y()); } - private void run(Storage storage, ServiceAccountAuthCredentials cred, Blob blob) + private void run(Storage storage, ServiceAccountAuthCredentials cred, BlobInfo blobInfo) throws IOException { + Blob blob = new Blob(storage, blobInfo); System.out.println("Signed URL: " + blob.signUrl(1, TimeUnit.DAYS, SignUrlOption.serviceAccount(cred))); } @Override - Tuple parse(Storage storage, String... args) - throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException, + Tuple parse(String... args) throws IOException, + KeyStoreException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException { if (args.length != 4) { throw new IllegalArgumentException(); @@ -515,7 +500,7 @@ Tuple parse(Storage storage, String... args keystore.load(Files.newInputStream(Paths.get(args[0])), PASSWORD); PrivateKey privateKey = (PrivateKey) keystore.getKey("privatekey", PASSWORD); ServiceAccountAuthCredentials cred = AuthCredentials.createFor(args[1], privateKey); - return Tuple.of(cred, new Blob(storage, args[2], args[3])); + return Tuple.of(cred, BlobInfo.builder(BlobId.of(args[2], args[3])).build()); } @Override @@ -576,7 +561,7 @@ public static void main(String... args) throws Exception { Storage storage = StorageFactory.instance().get(optionsBuilder.build()); Object request; try { - request = action.parse(storage, args); + request = action.parse(args); } catch (IllegalArgumentException ex) { System.out.println("Invalid input for action '" + args[1] + "'"); System.out.println("Expected: " + action.params()); 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 4e5045d65052..e70e0f14370e 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,28 +109,31 @@ public Blob(Storage storage, BlobInfo info) { } /** - * Constructs a {@code Blob} object for the provided bucket and blob names. The storage service is - * used to issue requests. + * Creates a {@code Blob} object for the provided bucket and blob names. Performs an RPC call to + * get the latest blob information. * * @param storage the storage service used for issuing requests * @param bucket bucket's name * @param blob blob's name + * @return the {@code Blob} object or {@code null} if not found. + * @throws StorageException upon failure */ - public Blob(Storage storage, String bucket, String blob) { - this.storage = checkNotNull(storage); - this.info = BlobInfo.builder(BlobId.of(bucket, blob)).build(); + public static Blob load(Storage storage, String bucket, String blob) { + return load(storage, BlobId.of(bucket, blob)); } /** - * Constructs a {@code Blob} object for the provided {@code BlobId}. The storage service is used - * to issue requests. + * Creates a {@code Blob} object for the provided {@code blobId}. Performs an RPC call to get the + * latest blob information. * * @param storage the storage service used for issuing requests * @param blobId blob's identifier + * @return the {@code Blob} object or {@code null} if not found. + * @throws StorageException upon failure */ - public Blob(Storage storage, BlobId blobId) { - this.storage = checkNotNull(storage); - this.info = BlobInfo.builder(blobId).build(); + public static Blob load(Storage storage, BlobId blobId) { + BlobInfo info = storage.get(blobId); + return info != null ? new Blob(storage, info) : null; } /** @@ -209,9 +212,8 @@ public Blob update(BlobInfo blobInfo, BlobTargetOption... options) { */ public Blob copyTo(BlobId targetBlob, BlobSourceOption... options) { BlobInfo updatedInfo = info.toBuilder().blobId(targetBlob).build(); - CopyRequest copyRequest = - CopyRequest.builder().source(info.bucket(), info.name()) - .sourceOptions(convert(info, options)).target(updatedInfo).build(); + CopyRequest copyRequest = CopyRequest.builder().source(info.bucket(), info.name()) + .sourceOptions(convert(info, options)).target(updatedInfo).build(); return new Blob(storage, storage.copy(copyRequest)); } @@ -251,9 +253,8 @@ public Blob copyTo(String targetBucket, BlobSourceOption... options) { */ public Blob copyTo(String targetBucket, String targetBlob, BlobSourceOption... options) { BlobInfo updatedInfo = info.toBuilder().blobId(BlobId.of(targetBucket, targetBlob)).build(); - CopyRequest copyRequest = - CopyRequest.builder().source(info.bucket(), info.name()) - .sourceOptions(convert(info, options)).target(updatedInfo).build(); + CopyRequest copyRequest = CopyRequest.builder().source(info.bucket(), info.name()) + .sourceOptions(convert(info, options)).target(updatedInfo).build(); return new Blob(storage, storage.copy(copyRequest)); } 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 8da06ec12fea..3193259f68cb 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 @@ -56,15 +56,17 @@ public Bucket(Storage storage, BucketInfo info) { } /** - * Constructs a {@code Bucket} object for the provided name. The storage service is used to issue - * requests. + * Creates a {@code Bucket} object for the provided bucket name. Performs an RPC call to get the + * latest bucket information. * * @param storage the storage service used for issuing requests * @param bucket bucket's name + * @return the {@code Bucket} object or {@code null} if not found. + * @throws StorageException upon failure */ - public Bucket(Storage storage, String bucket) { - this.storage = checkNotNull(storage); - this.info = BucketInfo.of(checkNotNull(bucket)); + public static Bucket load(Storage storage, String bucket) { + BucketInfo info = storage.get(bucket); + return info != null ? new Bucket(storage, info) : null; } /** 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 229c4147d266..790961a7a3d1 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 @@ -695,7 +695,7 @@ public static Builder builder() { *

* Example usage of creating a signed URL that is valid for 2 weeks: *

   {@code
-   *     service.signUrl(BlobInfo.of("bucket", "name"), 14, TimeUnit.DAYS);
+   *     service.signUrl(BlobInfo.builder("bucket", "name").build(), 14, TimeUnit.DAYS);
    * }
* * @param blobInfo the blob associated with the signed URL diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/package-info.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/package-info.java index 0ee04b92e857..b4a701fde840 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/package-info.java +++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/package-info.java @@ -21,11 +21,13 @@ *
{@code
  * StorageOptions options = StorageOptions.builder().projectId("project").build();
  * Storage storage = StorageFactory.instance().get(options);
- * Blob blob = new Blob(storage, "bucket", "blob_name");
- * if (!blob.exists()) {
- *   storage.create(blob.info(), "Hello, Cloud Storage!".getBytes(UTF_8));
+ * BlobId blobId = BlobId.of("bucket", "blob_name");
+ * Blob blob = Blob.load(storage, blobId);
+ * if (blob == null) {
+ *   BlobInfo blobInfo = BlobInfo.builder(blobId).contentType("text/plain").build();
+ *   storage.create(blobInfo, "Hello, Cloud Storage!".getBytes(UTF_8));
  * } else {
- *   System.out.println("Updating content for " + blob.info().name());
+ *   System.out.println("Updating content for " + blobId.name());
  *   byte[] prevContent = blob.content();
  *   System.out.println(new String(prevContent, UTF_8));
  *   WritableByteChannel channel = blob.writer();
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 e60c1178c43d..dddbb763f04c 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
@@ -271,4 +271,20 @@ public void testDeleteSome() throws Exception {
       assertEquals(deleleResultList.get(i), result.get(i));
     }
   }
+
+  @Test
+  public void testLoadFromString() throws Exception {
+    expect(storage.get(BLOB_INFO.blobId())).andReturn(BLOB_INFO);
+    replay(storage);
+    Blob loadedBlob = Blob.load(storage, BLOB_INFO.bucket(), BLOB_INFO.name());
+    assertEquals(BLOB_INFO, loadedBlob.info());
+  }
+
+  @Test
+  public void testLoadFromId() throws Exception {
+    expect(storage.get(BLOB_INFO.blobId())).andReturn(BLOB_INFO);
+    replay(storage);
+    Blob loadedBlob = Blob.load(storage, BLOB_INFO.blobId());
+    assertEquals(BLOB_INFO, loadedBlob.info());
+  }
 }
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 d0b5d502a8da..af156cb932ee 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
@@ -168,4 +168,12 @@ public void testCreate() throws Exception {
     Blob blob = bucket.create("n", content);
     assertEquals(info, blob.info());
   }
+
+  @Test
+  public void testLoad() throws Exception {
+    expect(storage.get(BUCKET_INFO.name())).andReturn(BUCKET_INFO);
+    replay(storage);
+    Bucket loadedBucket = Bucket.load(storage, BUCKET_INFO.name());
+    assertEquals(BUCKET_INFO, loadedBucket.info());
+  }
 }