From c53560d92a50a4e05359cb2590ea33d9a7f3778c Mon Sep 17 00:00:00 2001 From: Marco Ziccardi Date: Thu, 19 May 2016 11:19:17 +0200 Subject: [PATCH 1/7] Add whenDone method and CompletionCallback to BigQuery Job --- README.md | 22 +++--- .../java/com/google/cloud/bigquery/Job.java | 58 +++++++++++++++- .../google/cloud/bigquery/package-info.java | 17 ++--- .../com/google/cloud/bigquery/JobTest.java | 67 ++++++++++++++++++- .../cloud/bigquery/it/ITBigQueryTest.java | 55 ++++++++------- .../examples/bigquery/BigQueryExample.java | 24 ++++--- .../snippets/CreateTableAndLoadData.java | 22 +++--- 7 files changed, 201 insertions(+), 64 deletions(-) diff --git a/README.md b/README.md index 76dff042d27d..4990bf42face 100644 --- a/README.md +++ b/README.md @@ -152,6 +152,7 @@ Complete source code can be found at ```java import com.google.cloud.bigquery.BigQuery; +import com.google.cloud.bigquery.BigQueryError; import com.google.cloud.bigquery.BigQueryOptions; import com.google.cloud.bigquery.Field; import com.google.cloud.bigquery.FormatOptions; @@ -162,6 +163,8 @@ import com.google.cloud.bigquery.Table; import com.google.cloud.bigquery.TableId; import com.google.cloud.bigquery.TableInfo; +import java.util.List; + BigQuery bigquery = BigQueryOptions.defaultInstance().service(); TableId tableId = TableId.of("dataset", "table"); Table table = bigquery.getTable(tableId); @@ -173,14 +176,17 @@ if (table == null) { } System.out.println("Loading data into table " + tableId); Job loadJob = table.load(FormatOptions.csv(), "gs://bucket/path"); -while (!loadJob.isDone()) { - Thread.sleep(1000L); -} -if (loadJob.status().error() != null) { - System.out.println("Job completed with errors"); -} else { - System.out.println("Job succeeded"); -} +loadJob.whenDone(new Job.CompletionCallback() { + @Override + public void success(Job job) { + System.out.println("Job succeeded"); + } + + @Override + public void error(BigQueryError error, List executionErrors) { + System.out.println("Job completed with errors"); + } +}); ``` Google Cloud Compute (Alpha) diff --git a/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/Job.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/Job.java index bfcca5b5388a..caea0ee38390 100644 --- a/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/Job.java +++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/Job.java @@ -20,6 +20,7 @@ import java.io.IOException; import java.io.ObjectInputStream; +import java.util.List; import java.util.Objects; /** @@ -37,6 +38,24 @@ public class Job extends JobInfo { private final BigQueryOptions options; private transient BigQuery bigquery; + /** + * A callback for job completion. + */ + public interface CompletionCallback { + /** + * The method called when the job completes successfully. + */ + void success(Job job); + + /** + * The method called when the job completes with errors. {@code error} is the final error that + * caused the job to fail (see {@link JobStatus#error()}). {@code executionErrors} are all the + * errors (possibly not fatal) encountered by the job during its execution (see + * {@link JobStatus#executionErrors()}). + */ + void error(BigQueryError error, List executionErrors); + } + /** * A builder for {@code Job} objects. */ @@ -143,6 +162,43 @@ public boolean isDone() { return job == null || job.status().state() == JobStatus.State.DONE; } + /** + * Waits until this job completes its execution, either failing or succeeding. If the job does not + * exist, this method returns without executing any method of the provided callback. If the job + * completed successfully the {@link CompletionCallback#success(Job)} method is called. If the job + * completed with errors the {@link CompletionCallback#error(BigQueryError, List)} method is + * called. + *
 {@code
+   * job.whenDone(new CompletionCallback() {
+   *   void success(Job job) {
+   *     // completed successfully
+   *   }
+   *
+   *   void error(BigQueryError error, List executionErrors) {
+   *     // handle error
+   *   }
+   * });}
+ * + * @throws BigQueryException upon failure + * @throws InterruptedException if the current thread gets interrupted while waiting for the job + * to complete + */ + public void whenDone(CompletionCallback callback) throws InterruptedException { + while (!isDone()) { + Thread.sleep(500L); + } + Job updatedJob = reload(); + if (updatedJob == null) { + return; + } + BigQueryError error = updatedJob.status().error(); + if (error != null) { + callback.error(error, updatedJob.status().executionErrors()); + } else { + callback.success(updatedJob); + } + } + /** * Fetches current job's latest information. Returns {@code null} if the job does not exist. * @@ -151,7 +207,7 @@ public boolean isDone() { * @throws BigQueryException upon failure */ public Job reload(BigQuery.JobOption... options) { - return bigquery.getJob(jobId().job(), options); + return bigquery.getJob(jobId(), options); } /** diff --git a/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/package-info.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/package-info.java index a701b82c1c2c..19e1d96fedef 100644 --- a/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/package-info.java +++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/package-info.java @@ -33,14 +33,15 @@ * } * System.out.println("Loading data into table " + tableId); * Job loadJob = table.load(FormatOptions.csv(), "gs://bucket/path"); - * while (!loadJob.isDone()) { - * Thread.sleep(1000L); - * } - * if (loadJob.status().error() != null) { - * System.out.println("Job completed with errors"); - * } else { - * System.out.println("Job succeeded"); - * }} + * loadJob.whenDone(new Job.CompletionCallback() { + * public void success(Job job) { + * System.out.println("Job succeeded"); + * } + * + * public void error(BigQueryError error, List executionErrors) { + * System.out.println("Job completed with errors"); + * } + * });} * * @see Google Cloud BigQuery */ diff --git a/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/JobTest.java b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/JobTest.java index 44e5e201e95c..11896baded43 100644 --- a/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/JobTest.java +++ b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/JobTest.java @@ -28,10 +28,14 @@ import static org.junit.Assert.assertTrue; import com.google.cloud.bigquery.JobStatistics.CopyStatistics; +import com.google.common.collect.ImmutableList; +import org.easymock.EasyMock; import org.junit.After; import org.junit.Test; +import java.util.List; + public class JobTest { private static final JobId JOB_ID = JobId.of("project", "job"); @@ -177,13 +181,70 @@ public void testIsDone_NotExists() throws Exception { assertTrue(job.isDone()); } + @Test + public void testWhenDone_Success() throws InterruptedException { + initializeExpectedJob(2); + Job.CompletionCallback callback = EasyMock.mock(Job.CompletionCallback.class); + BigQuery.JobOption[] expectedOptions = {BigQuery.JobOption.fields(BigQuery.JobField.STATUS)}; + JobStatus status = createStrictMock(JobStatus.class); + expect(status.state()).andReturn(JobStatus.State.DONE); + expect(status.error()).andReturn(null); + expect(bigquery.options()).andReturn(mockOptions); + Job completedJob = expectedJob.toBuilder().status(status).build(); + expect(bigquery.getJob(JOB_INFO.jobId(), expectedOptions)).andReturn(completedJob); + expect(bigquery.getJob(JOB_INFO.jobId())).andReturn(completedJob); + callback.success(completedJob); + EasyMock.expectLastCall(); + replay(status, bigquery, callback); + initializeJob(); + job.whenDone(callback); + verify(status, callback); + } + + @Test + public void testWhenDone_Error() throws InterruptedException { + initializeExpectedJob(2); + Job.CompletionCallback callback = EasyMock.mock(Job.CompletionCallback.class); + BigQuery.JobOption[] expectedOptions = {BigQuery.JobOption.fields(BigQuery.JobField.STATUS)}; + BigQueryError error = new BigQueryError("reason", "location", "message"); + List executionErrors = ImmutableList.of(error); + JobStatus status = createStrictMock(JobStatus.class); + expect(status.state()).andReturn(JobStatus.State.DONE); + expect(status.error()).andReturn(error); + expect(status.executionErrors()).andReturn(executionErrors); + expect(bigquery.options()).andReturn(mockOptions); + Job completedJob = expectedJob.toBuilder().status(status).build(); + expect(bigquery.getJob(JOB_INFO.jobId(), expectedOptions)).andReturn(completedJob); + expect(bigquery.getJob(JOB_INFO.jobId())).andReturn(completedJob); + callback.error(error, executionErrors); + EasyMock.expectLastCall(); + replay(status, bigquery, callback); + initializeJob(); + job.whenDone(callback); + verify(status, callback); + } + + @Test + public void testWhenDone_Null() throws InterruptedException { + initializeExpectedJob(1); + Job.CompletionCallback callback = EasyMock.mock(Job.CompletionCallback.class); + BigQuery.JobOption[] expectedOptions = {BigQuery.JobOption.fields(BigQuery.JobField.STATUS)}; + expect(bigquery.options()).andReturn(mockOptions); + expect(bigquery.getJob(JOB_INFO.jobId(), expectedOptions)).andReturn(null); + expect(bigquery.getJob(JOB_INFO.jobId())).andReturn(null); + replay(bigquery, callback); + initializeJob(); + job.whenDone(callback); + verify(callback); + } + @Test public void testReload() throws Exception { initializeExpectedJob(4); JobInfo updatedInfo = JOB_INFO.toBuilder().etag("etag").build(); Job expectedJob = new Job(serviceMockReturnsOptions, new JobInfo.BuilderImpl(updatedInfo)); expect(bigquery.options()).andReturn(mockOptions); - expect(bigquery.getJob(JOB_INFO.jobId().job())).andReturn(expectedJob); + expect(bigquery.getJob(JOB_INFO.jobId())).andReturn(expectedJob); replay(bigquery); initializeJob(); Job updatedJob = job.reload(); @@ -194,7 +255,7 @@ public void testReload() throws Exception { public void testReloadNull() throws Exception { initializeExpectedJob(1); expect(bigquery.options()).andReturn(mockOptions); - expect(bigquery.getJob(JOB_INFO.jobId().job())).andReturn(null); + expect(bigquery.getJob(JOB_INFO.jobId())).andReturn(null); replay(bigquery); initializeJob(); assertNull(job.reload()); @@ -206,7 +267,7 @@ public void testReloadWithOptions() throws Exception { JobInfo updatedInfo = JOB_INFO.toBuilder().etag("etag").build(); Job expectedJob = new Job(serviceMockReturnsOptions, new JobInfo.BuilderImpl(updatedInfo)); expect(bigquery.options()).andReturn(mockOptions); - expect(bigquery.getJob(JOB_INFO.jobId().job(), BigQuery.JobOption.fields())) + expect(bigquery.getJob(JOB_INFO.jobId(), BigQuery.JobOption.fields())) .andReturn(expectedJob); replay(bigquery); initializeJob(); diff --git a/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/it/ITBigQueryTest.java b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/it/ITBigQueryTest.java index 5007c73b69ce..444b25add682 100644 --- a/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/it/ITBigQueryTest.java +++ b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/it/ITBigQueryTest.java @@ -69,9 +69,9 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import org.easymock.EasyMock; import org.junit.AfterClass; import org.junit.BeforeClass; -import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.rules.Timeout; @@ -188,10 +188,11 @@ public static void beforeClass() throws InterruptedException { .schema(TABLE_SCHEMA) .build(); Job job = bigquery.create(JobInfo.of(configuration)); - while (!job.isDone()) { - Thread.sleep(1000); - } - assertNull(job.status().error()); + Job.CompletionCallback callback = EasyMock.createStrictMock(Job.CompletionCallback.class); + callback.success(EasyMock.anyObject()); + EasyMock.replay(callback); + job.whenDone(callback); + EasyMock.verify(callback); } @AfterClass @@ -799,10 +800,11 @@ public void testCopyJob() throws InterruptedException { TableId destinationTable = TableId.of(DATASET, destinationTableName); CopyJobConfiguration configuration = CopyJobConfiguration.of(destinationTable, sourceTable); Job remoteJob = bigquery.create(JobInfo.of(configuration)); - while (!remoteJob.isDone()) { - Thread.sleep(1000); - } - assertNull(remoteJob.status().error()); + Job.CompletionCallback callback = EasyMock.createStrictMock(Job.CompletionCallback.class); + callback.success(EasyMock.anyObject()); + EasyMock.replay(callback); + remoteJob.whenDone(callback); + EasyMock.verify(callback); Table remoteTable = bigquery.getTable(DATASET, destinationTableName); assertNotNull(remoteTable); assertEquals(destinationTable.dataset(), remoteTable.tableId().dataset()); @@ -825,10 +827,11 @@ public void testQueryJob() throws InterruptedException { .destinationTable(destinationTable) .build(); Job remoteJob = bigquery.create(JobInfo.of(configuration)); - while (!remoteJob.isDone()) { - Thread.sleep(1000); - } - assertNull(remoteJob.status().error()); + Job.CompletionCallback callback = EasyMock.createStrictMock(Job.CompletionCallback.class); + callback.success(EasyMock.anyObject()); + EasyMock.replay(callback); + remoteJob.whenDone(callback); + EasyMock.reset(callback); QueryResponse response = bigquery.getQueryResults(remoteJob.jobId()); while (!response.jobCompleted()) { @@ -866,20 +869,21 @@ public void testExtractJob() throws InterruptedException { .schema(SIMPLE_SCHEMA) .build(); Job remoteLoadJob = bigquery.create(JobInfo.of(configuration)); - while (!remoteLoadJob.isDone()) { - Thread.sleep(1000); - } - assertNull(remoteLoadJob.status().error()); + Job.CompletionCallback callback = EasyMock.createStrictMock(Job.CompletionCallback.class); + callback.success(EasyMock.anyObject()); + EasyMock.replay(callback); + remoteLoadJob.whenDone(callback); + EasyMock.reset(callback); ExtractJobConfiguration extractConfiguration = ExtractJobConfiguration.builder(destinationTable, "gs://" + BUCKET + "/" + EXTRACT_FILE) .printHeader(false) .build(); Job remoteExtractJob = bigquery.create(JobInfo.of(extractConfiguration)); - while (!remoteExtractJob.isDone()) { - Thread.sleep(1000); - } - assertNull(remoteExtractJob.status().error()); + callback.success(EasyMock.anyObject()); + EasyMock.replay(callback); + remoteExtractJob.whenDone(callback); + EasyMock.verify(callback); assertEquals(CSV_CONTENT, new String(storage.readAllBytes(BUCKET, EXTRACT_FILE), StandardCharsets.UTF_8)); assertTrue(bigquery.delete(DATASET, tableName)); @@ -896,10 +900,11 @@ public void testCancelJob() throws InterruptedException { .build(); Job remoteJob = bigquery.create(JobInfo.of(configuration)); assertTrue(remoteJob.cancel()); - while (!remoteJob.isDone()) { - Thread.sleep(1000); - } - assertNull(remoteJob.status().error()); + Job.CompletionCallback callback = EasyMock.createStrictMock(Job.CompletionCallback.class); + callback.success(EasyMock.anyObject()); + EasyMock.replay(callback); + remoteJob.whenDone(callback); + EasyMock.verify(callback); } @Test diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/bigquery/BigQueryExample.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/bigquery/BigQueryExample.java index 6acfb5bbef6d..41cacef7ed00 100644 --- a/gcloud-java-examples/src/main/java/com/google/cloud/examples/bigquery/BigQueryExample.java +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/bigquery/BigQueryExample.java @@ -523,17 +523,19 @@ private abstract static class JobRunAction extends BigQueryAction { @Override void run(BigQuery bigquery, JobInfo job) throws Exception { System.out.println("Creating job"); - Job startedJob = bigquery.create(job); - while (!startedJob.isDone()) { - System.out.println("Waiting for job " + startedJob.jobId().job() + " to complete"); - Thread.sleep(1000L); - } - if (startedJob.status().error() == null) { - System.out.println("Job " + startedJob.jobId().job() + " succeeded"); - } else { - System.out.println("Job " + startedJob.jobId().job() + " failed"); - System.out.println("Error: " + startedJob.status().error()); - } + final Job startedJob = bigquery.create(job); + startedJob.whenDone(new Job.CompletionCallback() { + @Override + public void success(Job updatedJob) { + System.out.println("Job " + updatedJob.jobId().job() + " succeeded"); + } + + @Override + public void error(BigQueryError error, List executionErrors) { + System.out.println("Job " + startedJob.jobId().job() + " failed"); + System.out.println("Error: " + startedJob.status().error()); + } + }); } } diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/bigquery/snippets/CreateTableAndLoadData.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/bigquery/snippets/CreateTableAndLoadData.java index 01290ec8b491..e74bbc3723cc 100644 --- a/gcloud-java-examples/src/main/java/com/google/cloud/examples/bigquery/snippets/CreateTableAndLoadData.java +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/bigquery/snippets/CreateTableAndLoadData.java @@ -23,6 +23,7 @@ package com.google.cloud.examples.bigquery.snippets; import com.google.cloud.bigquery.BigQuery; +import com.google.cloud.bigquery.BigQueryError; import com.google.cloud.bigquery.BigQueryOptions; import com.google.cloud.bigquery.Field; import com.google.cloud.bigquery.FormatOptions; @@ -33,6 +34,8 @@ import com.google.cloud.bigquery.TableId; import com.google.cloud.bigquery.TableInfo; +import java.util.List; + /** * A snippet for Google Cloud BigQuery showing how to get a BigQuery table or create it if it does * not exist. The snippet also starts a BigQuery job to load data into the table from a Cloud @@ -52,13 +55,16 @@ public static void main(String... args) throws InterruptedException { } System.out.println("Loading data into table " + tableId); Job loadJob = table.load(FormatOptions.csv(), "gs://bucket/path"); - while (!loadJob.isDone()) { - Thread.sleep(1000L); - } - if (loadJob.status().error() != null) { - System.out.println("Job completed with errors"); - } else { - System.out.println("Job succeeded"); - } + loadJob.whenDone(new Job.CompletionCallback() { + @Override + public void success(Job job) { + System.out.println("Job succeeded"); + } + + @Override + public void error(BigQueryError error, List executionErrors) { + System.out.println("Job completed with errors"); + } + }); } } From a185e4c508bcfa6c16253ecbbaf4ad1f8b10fc30 Mon Sep 17 00:00:00 2001 From: Marco Ziccardi Date: Thu, 19 May 2016 16:19:06 +0200 Subject: [PATCH 2/7] Add whenDone method and CompletionCallback to Compute Operation --- README.md | 35 ++- gcloud-java-compute/README.md | 77 +++-- .../com/google/cloud/compute/Operation.java | 65 +++- .../google/cloud/compute/package-info.java | 44 +-- .../google/cloud/compute/OperationTest.java | 74 ++++- .../cloud/compute/it/ITComputeTest.java | 281 +++++------------- .../CreateAddressDiskAndInstance.java | 79 ++--- .../compute/snippets/CreateInstance.java | 28 +- .../compute/snippets/CreateSnapshot.java | 28 +- 9 files changed, 378 insertions(+), 333 deletions(-) diff --git a/README.md b/README.md index 4990bf42face..5e86976601f6 100644 --- a/README.md +++ b/README.md @@ -209,22 +209,31 @@ import com.google.cloud.compute.Compute; import com.google.cloud.compute.ComputeOptions; import com.google.cloud.compute.Disk; import com.google.cloud.compute.DiskId; -import com.google.cloud.compute.Operation; +import com.google.cloud.compute.Operation.OperationError; +import com.google.cloud.compute.Operation.OperationWarning; import com.google.cloud.compute.Snapshot; -Compute compute = ComputeOptions.defaultInstance().service(); +import java.util.List; + +final Compute compute = ComputeOptions.defaultInstance().service(); DiskId diskId = DiskId.of("us-central1-a", "disk-name"); Disk disk = compute.getDisk(diskId, Compute.DiskOption.fields()); if (disk != null) { - String snapshotName = "disk-name-snapshot"; + final String snapshotName = "disk-name-snapshot"; Operation operation = disk.createSnapshot(snapshotName); - while (!operation.isDone()) { - Thread.sleep(1000L); - } - if (operation.errors() == null) { - // use snapshot - Snapshot snapshot = compute.getSnapshot("disk-name-snapshot"); - } + operation.whenDone(new Operation.CompletionCallback() { + @Override + public void success(Operation operation) { + // use snapshot + Snapshot snapshot = compute.getSnapshot(snapshotName); + } + + @Override + public void error(List errors, List warnings) { + // inspect errors + throw new RuntimeException("Snaphsot creation failed"); + } + }); } ``` The second snippet shows how to create a virtual machine instance. Complete source code can be found @@ -240,8 +249,10 @@ import com.google.cloud.compute.InstanceId; import com.google.cloud.compute.InstanceInfo; import com.google.cloud.compute.MachineTypeId; import com.google.cloud.compute.NetworkId; -import com.google.cloud.compute.NetworkInterface; -import com.google.cloud.compute.Operation; +import com.google.cloud.compute.Operation.OperationError; +import com.google.cloud.compute.Operation.OperationWarning; + +import java.util.List; Compute compute = ComputeOptions.defaultInstance().service(); ImageId imageId = ImageId.of("debian-cloud", "debian-8-jessie-v20160329"); diff --git a/gcloud-java-compute/README.md b/gcloud-java-compute/README.md index 81d46fd1270d..f137572acc91 100644 --- a/gcloud-java-compute/README.md +++ b/gcloud-java-compute/README.md @@ -104,7 +104,11 @@ Add the following imports at the top of your file: ```java import com.google.cloud.compute.AddressInfo; import com.google.cloud.compute.Operation; +import com.google.cloud.compute.Operation.OperationError; +import com.google.cloud.compute.Operation.OperationWarning; import com.google.cloud.compute.RegionAddressId; + +import java.util.List; ``` Then add the following code to create an address. Most Compute Engine calls return an `Operation` @@ -112,18 +116,21 @@ object that can be used to wait for operation completion and to check whether op succeeded: ```java -RegionAddressId addressId = RegionAddressId.of("us-central1", "test-address"); +final RegionAddressId addressId = RegionAddressId.of("us-central1", "test-address"); Operation operation = compute.create(AddressInfo.of(addressId)); -while (!operation.isDone()) { - Thread.sleep(1000L); -} -operation = operation.reload(); -if (operation.errors() == null) { - System.out.println("Address " + addressId + " was successfully created"); -} else { - // inspect operation.errors() - throw new RuntimeException("Address creation failed"); -} +// Wait for operation to complete +operation.whenDone(new Operation.CompletionCallback() { + @Override + public void success(Operation operation) { + System.out.println("Address " + addressId + " was successfully created"); + } + + @Override + public void error(List errors, List warnings) { + // inspect errors + throw new RuntimeException("Address creation failed"); + } +}); ``` #### Creating a persistent disk @@ -145,21 +152,23 @@ import com.google.cloud.compute.ImageId; Then add the following code to create a disk and wait for disk creation to terminate. ```java -ImageId imageId = ImageId.of("debian-cloud", "debian-8-jessie-v20160329"); +final ImageId imageId = ImageId.of("debian-cloud", "debian-8-jessie-v20160329"); DiskId diskId = DiskId.of("us-central1-a", "test-disk"); ImageDiskConfiguration diskConfiguration = ImageDiskConfiguration.of(imageId); DiskInfo disk = DiskInfo.of(diskId, diskConfiguration); Operation operation = compute.create(disk); -while (!operation.isDone()) { - Thread.sleep(1000L); -} -operation = operation.reload(); -if (operation.errors() == null) { - System.out.println("Disk " + diskId + " was successfully created"); -} else { - // inspect operation.errors() - throw new RuntimeException("Disk creation failed"); -} +operation.whenDone(new Operation.CompletionCallback() { + @Override + public void success(Operation operation) { + System.out.println("Disk " + diskId + " was successfully created"); + } + + @Override + public void error(List errors, List warnings) { + // inspect errors + throw new RuntimeException("Disk creation failed"); + } +}); ``` #### Creating a virtual machine instance @@ -186,7 +195,7 @@ Then add the following code to create an instance and wait for instance creation ```java Address externalIp = compute.getAddress(addressId); -InstanceId instanceId = InstanceId.of("us-central1-a", "test-instance"); +final InstanceId instanceId = InstanceId.of("us-central1-a", "test-instance"); NetworkId networkId = NetworkId.of("default"); PersistentDiskConfiguration attachConfiguration = PersistentDiskConfiguration.builder(diskId).boot(true).build(); @@ -198,16 +207,18 @@ MachineTypeId machineTypeId = MachineTypeId.of("us-central1-a", "n1-standard-1") InstanceInfo instance = InstanceInfo.of(instanceId, machineTypeId, attachedDisk, networkInterface); Operation operation = compute.create(instance); -while (!operation.isDone()) { - Thread.sleep(1000L); -} -operation = operation.reload(); -if (operation.errors() == null) { - System.out.println("Instance " + instanceId + " was successfully created"); -} else { - // inspect operation.errors() - throw new RuntimeException("Instance creation failed"); -} +operation.whenDone(new Operation.CompletionCallback() { + @Override + public void success(Operation operation) { + System.out.println("Instance " + instanceId + " was successfully created"); + } + + @Override + public void error(List errors, List warnings) { + // inspect errors + throw new RuntimeException("Instance creation failed"); + } +}); ``` #### Complete source code diff --git a/gcloud-java-compute/src/main/java/com/google/cloud/compute/Operation.java b/gcloud-java-compute/src/main/java/com/google/cloud/compute/Operation.java index 326b681098a6..9ec6ccc54278 100644 --- a/gcloud-java-compute/src/main/java/com/google/cloud/compute/Operation.java +++ b/gcloud-java-compute/src/main/java/com/google/cloud/compute/Operation.java @@ -42,7 +42,7 @@ * {@link #operationId()} returns {@link GlobalOperationId} for global operations, * {@link RegionOperationId} for region operations, and {@link ZoneOperationId} for zone operations. * To get an {@code Operation} object with the most recent information, use - * {@link #reload(OperationOption...)}. + * {@link #reload(Compute.OperationOption...)}. */ public class Operation implements Serializable { @@ -292,6 +292,24 @@ public int hashCode() { } } + /** + * A callback for operation completion. + */ + public interface CompletionCallback { + /** + * The method called when the operation completes successfully. + */ + void success(Operation operation); + + /** + * The method called when the operation completes with errors. {@code errors} contains all + * errors encountered while processing this operation (see {@link Operation#errors()}). + * {@code warnings} contains all warnings encountered while processing this operation (see + * {@link Operation#warnings()}). + */ + void error(List errors, List warnings); + } + static final class Builder { private Compute compute; @@ -635,7 +653,7 @@ public String description() { * @return {@code true} if this operation exists, {@code false} otherwise * @throws ComputeException upon failure */ - public boolean exists() throws ComputeException { + public boolean exists() { return reload(OperationOption.fields()) != null; } @@ -652,12 +670,49 @@ public boolean exists() throws ComputeException { * not exist, {@code false} if the state is not {@link Operation.Status#DONE} * @throws ComputeException upon failure */ - public boolean isDone() throws ComputeException { + public boolean isDone() { Operation operation = compute.getOperation(operationId, OperationOption.fields(Compute.OperationField.STATUS)); return operation == null || operation.status() == Status.DONE; } + /** + * Waits until this operation completes its execution, either failing or succeeding. If the + * operation does not exist, this method returns without executing any method of the provided + * callback. If the operation completed successfully the + * {@link CompletionCallback#success(Operation)} method is called. If the operation completed with + * errors the {@link CompletionCallback#error(List, List)} method is called. + *
 {@code
+   * operation.whenDone(new CompletionCallback() {
+   *   void success(Operation operation) {
+   *     // completed successfully
+   *   }
+   *
+   *   void error(List errors, List warnings) {
+   *     // handle error
+   *   }
+   * });}
+ * + * @throws ComputeException upon failure + * @throws InterruptedException if the current thread gets interrupted while waiting for the + * operation to complete + */ + public void whenDone(CompletionCallback callback) throws InterruptedException { + while (!isDone()) { + Thread.sleep(500L); + } + Operation updatedOperation = reload(); + if (updatedOperation == null) { + return; + } + List errors = updatedOperation.errors(); + if (errors != null) { + callback.error(errors, updatedOperation.warnings()); + } else { + callback.success(updatedOperation); + } + } + /** * Fetches current operation's latest information. Returns {@code null} if the operation does not * exist. @@ -666,7 +721,7 @@ public boolean isDone() throws ComputeException { * @return an {@code Operation} object with latest information or {@code null} if not found * @throws ComputeException upon failure */ - public Operation reload(OperationOption... options) throws ComputeException { + public Operation reload(OperationOption... options) { return compute.getOperation(operationId, options); } @@ -677,7 +732,7 @@ public Operation reload(OperationOption... options) throws ComputeException { * @return {@code true} if operation was deleted, {@code false} if it was not found * @throws ComputeException upon failure */ - public boolean delete() throws ComputeException { + public boolean delete() { return compute.deleteOperation(operationId); } diff --git a/gcloud-java-compute/src/main/java/com/google/cloud/compute/package-info.java b/gcloud-java-compute/src/main/java/com/google/cloud/compute/package-info.java index b7f589ea3b3f..3b1c703fa9b0 100644 --- a/gcloud-java-compute/src/main/java/com/google/cloud/compute/package-info.java +++ b/gcloud-java-compute/src/main/java/com/google/cloud/compute/package-info.java @@ -22,41 +22,49 @@ * * CreateSnapshot.java. *
 {@code
- * Compute compute = ComputeOptions.defaultInstance().service();
+ * final Compute compute = ComputeOptions.defaultInstance().service();
  * DiskId diskId = DiskId.of("us-central1-a", "disk-name");
  * Disk disk = compute.getDisk(diskId, Compute.DiskOption.fields());
  * if (disk != null) {
- *   String snapshotName = "disk-name-snapshot";
+ *   final String snapshotName = "disk-name-snapshot";
  *   Operation operation = disk.createSnapshot(snapshotName);
- *   while (!operation.isDone()) {
- *     Thread.sleep(1000L);
- *   }
- *   if (operation.errors() == null) {
- *     // use snapshot
- *     Snapshot snapshot = compute.getSnapshot("disk-name-snapshot");
- *   }
+ *   operation.whenDone(new Operation.CompletionCallback() {
+ *     public void success(Operation operation) {
+ *       // use snapshot
+ *       Snapshot snapshot = compute.getSnapshot(snapshotName);
+ *     }
+ *
+ *     public void error(List errors, List warnings) {
+ *       // inspect errors
+ *       throw new RuntimeException("Snaphsot creation failed");
+ *     }
+ *   });
  * }}
*

This second example shows how to create a virtual machine instance. Complete source code can * be found at * * CreateInstance.java. *

 {@code
- * Compute compute = ComputeOptions.defaultInstance().service();
+ * final Compute compute = ComputeOptions.defaultInstance().service();
  * ImageId imageId = ImageId.of("debian-cloud", "debian-8-jessie-v20160329");
  * NetworkId networkId = NetworkId.of("default");
  * AttachedDisk attachedDisk = AttachedDisk.of(AttachedDisk.CreateDiskConfiguration.of(imageId));
  * NetworkInterface networkInterface = NetworkInterface.of(networkId);
- * InstanceId instanceId = InstanceId.of("us-central1-a", "instance-name");
+ * final InstanceId instanceId = InstanceId.of("us-central1-a", "instance-name");
  * MachineTypeId machineTypeId = MachineTypeId.of("us-central1-a", "n1-standard-1");
  * Operation operation =
  * compute.create(InstanceInfo.of(instanceId, machineTypeId, attachedDisk, networkInterface));
- * while (!operation.isDone()) {
- *   Thread.sleep(1000L);
- * }
- * if (operation.errors() == null) {
- *   // use instance
- *   Instance instance = compute.getInstance(instanceId);
- * }}
+ * operation.whenDone(new Operation.CompletionCallback() { + * public void success(Operation operation) { + * // use instance + * Instance instance = compute.getInstance(instanceId); + * } + * + * public void error(List errors, List warnings) { + * // inspect errors + * throw new RuntimeException("Instance creation failed"); + * } + * });} * * @see Google Cloud Compute */ diff --git a/gcloud-java-compute/src/test/java/com/google/cloud/compute/OperationTest.java b/gcloud-java-compute/src/test/java/com/google/cloud/compute/OperationTest.java index d45fe48c1134..b07411e1607e 100644 --- a/gcloud-java-compute/src/test/java/com/google/cloud/compute/OperationTest.java +++ b/gcloud-java-compute/src/test/java/com/google/cloud/compute/OperationTest.java @@ -28,9 +28,12 @@ import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; +import com.google.cloud.compute.Operation.OperationError; +import com.google.cloud.compute.Operation.OperationWarning; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import org.easymock.EasyMock; import org.junit.After; import org.junit.Test; @@ -38,14 +41,14 @@ public class OperationTest { - private static final Operation.OperationError OPERATION_ERROR1 = - new Operation.OperationError("code1", "location1", "message1"); - private static final Operation.OperationError OPERATION_ERROR2 = - new Operation.OperationError("code2", "location2", "message2"); - private static final Operation.OperationWarning OPERATION_WARNING1 = - new Operation.OperationWarning("code1", "message1", ImmutableMap.of("k1", "v1")); - private static final Operation.OperationWarning OPERATION_WARNING2 = - new Operation.OperationWarning("code2", "location2", ImmutableMap.of("k2", "v2")); + private static final OperationError OPERATION_ERROR1 = + new OperationError("code1", "location1", "message1"); + private static final OperationError OPERATION_ERROR2 = + new OperationError("code2", "location2", "message2"); + private static final OperationWarning OPERATION_WARNING1 = + new OperationWarning("code1", "message1", ImmutableMap.of("k1", "v1")); + private static final OperationWarning OPERATION_WARNING2 = + new OperationWarning("code2", "location2", ImmutableMap.of("k2", "v2")); private static final String GENERATED_ID = "1"; private static final String CLIENT_OPERATION_ID = "clientOperationId"; private static final String OPERATION_TYPE = "delete"; @@ -58,9 +61,9 @@ public class OperationTest { private static final Long INSERT_TIME = 1453293540000L; private static final Long START_TIME = 1453293420000L; private static final Long END_TIME = 1453293480000L; - private static final List ERRORS = + private static final List ERRORS = ImmutableList.of(OPERATION_ERROR1, OPERATION_ERROR2); - private static final List WARNINGS = + private static final List WARNINGS = ImmutableList.of(OPERATION_WARNING1, OPERATION_WARNING2); private static final Integer HTTP_ERROR_STATUS_CODE = 404; private static final String HTTP_ERROR_MESSAGE = "NOT FOUND"; @@ -352,6 +355,57 @@ public void testIsDone_NotExists() throws Exception { verify(compute); } + @Test + public void testWhenDone_Success() throws InterruptedException { + initializeExpectedOperation(4); + Operation.CompletionCallback callback = EasyMock.mock(Operation.CompletionCallback.class); + Compute.OperationOption[] expectedOptions = + {Compute.OperationOption.fields(Compute.OperationField.STATUS)}; + Operation successOperation = + Operation.fromPb(serviceMockReturnsOptions, globalOperation.toPb().setError(null)); + expect(compute.options()).andReturn(mockOptions); + expect(compute.getOperation(GLOBAL_OPERATION_ID, expectedOptions)).andReturn(successOperation); + expect(compute.getOperation(GLOBAL_OPERATION_ID)).andReturn(successOperation); + callback.success(successOperation); + EasyMock.expectLastCall(); + replay(compute, callback); + initializeOperation(); + operation.whenDone(callback); + verify(callback); + } + + @Test + public void testWhenDone_Error() throws InterruptedException { + initializeExpectedOperation(3); + Operation.CompletionCallback callback = EasyMock.mock(Operation.CompletionCallback.class); + Compute.OperationOption[] expectedOptions = + {Compute.OperationOption.fields(Compute.OperationField.STATUS)}; + expect(compute.options()).andReturn(mockOptions); + expect(compute.getOperation(GLOBAL_OPERATION_ID, expectedOptions)).andReturn(globalOperation); + expect(compute.getOperation(GLOBAL_OPERATION_ID)).andReturn(globalOperation); + callback.error(ERRORS, WARNINGS); + EasyMock.expectLastCall(); + replay(compute, callback); + initializeOperation(); + operation.whenDone(callback); + verify(callback); + } + + @Test + public void testWhenDone_Null() throws InterruptedException { + initializeExpectedOperation(3); + Operation.CompletionCallback callback = EasyMock.mock(Operation.CompletionCallback.class); + Compute.OperationOption[] expectedOptions = + {Compute.OperationOption.fields(Compute.OperationField.STATUS)}; + expect(compute.options()).andReturn(mockOptions); + expect(compute.getOperation(GLOBAL_OPERATION_ID, expectedOptions)).andReturn(null); + expect(compute.getOperation(GLOBAL_OPERATION_ID)).andReturn(null); + replay(compute, callback); + initializeOperation(); + operation.whenDone(callback); + verify(callback); + } + @Test public void testReload() throws Exception { initializeExpectedOperation(5); diff --git a/gcloud-java-compute/src/test/java/com/google/cloud/compute/it/ITComputeTest.java b/gcloud-java-compute/src/test/java/com/google/cloud/compute/it/ITComputeTest.java index 71013a3c1120..e67efd7e5ae2 100644 --- a/gcloud-java-compute/src/test/java/com/google/cloud/compute/it/ITComputeTest.java +++ b/gcloud-java-compute/src/test/java/com/google/cloud/compute/it/ITComputeTest.java @@ -56,6 +56,9 @@ import com.google.cloud.compute.NetworkInfo; import com.google.cloud.compute.NetworkInterface; import com.google.cloud.compute.Operation; +import com.google.cloud.compute.Operation.CompletionCallback; +import com.google.cloud.compute.Operation.OperationError; +import com.google.cloud.compute.Operation.OperationWarning; import com.google.cloud.compute.Region; import com.google.cloud.compute.RegionAddressId; import com.google.cloud.compute.RegionOperationId; @@ -100,6 +103,17 @@ public class ITComputeTest { private static final ImageId IMAGE_ID = ImageId.of("debian-cloud", "debian-8-jessie-v20160219"); private static final String IMAGE_PROJECT = "debian-cloud"; + private static final CompletionCallback EMPTY_CALLBACK = new CompletionCallback() { + @Override + public void success(Operation operation) { + // do nothing + } + + @Override + public void error(List errors, List warnings) { + // do noting + } + }; private static Compute compute; @Rule @@ -686,9 +700,7 @@ public void testCreateGetAndDeleteRegionAddress() throws InterruptedException { AddressId addressId = RegionAddressId.of(REGION, name); AddressInfo addressInfo = AddressInfo.of(addressId); Operation operation = compute.create(addressInfo); - while (!operation.isDone()) { - Thread.sleep(1000L); - } + operation.whenDone(EMPTY_CALLBACK); // test get Address remoteAddress = compute.getAddress(addressId); assertNotNull(remoteAddress); @@ -709,9 +721,7 @@ public void testCreateGetAndDeleteRegionAddress() throws InterruptedException { assertNull(remoteAddress.creationTimestamp()); assertNull(remoteAddress.generatedId()); operation = remoteAddress.delete(); - while (!operation.isDone()) { - Thread.sleep(1000L); - } + operation.whenDone(EMPTY_CALLBACK); assertNull(compute.getAddress(addressId)); } @@ -723,12 +733,8 @@ public void testListRegionAddresses() throws InterruptedException { AddressId secondAddressId = RegionAddressId.of(REGION, addressNames[1]); Operation firstOperation = compute.create(AddressInfo.of(firstAddressId)); Operation secondOperation = compute.create(AddressInfo.of(secondAddressId)); - while (!firstOperation.isDone()) { - Thread.sleep(1000L); - } - while (!secondOperation.isDone()) { - Thread.sleep(1000L); - } + firstOperation.whenDone(EMPTY_CALLBACK); + secondOperation.whenDone(EMPTY_CALLBACK); Set addressSet = ImmutableSet.copyOf(addressNames); // test list Compute.AddressFilter filter = @@ -779,12 +785,8 @@ public void testAggregatedListAddresses() throws InterruptedException { AddressId secondAddressId = GlobalAddressId.of(REGION, addressNames[1]); Operation firstOperation = compute.create(AddressInfo.of(firstAddressId)); Operation secondOperation = compute.create(AddressInfo.of(secondAddressId)); - while (!firstOperation.isDone()) { - Thread.sleep(1000L); - } - while (!secondOperation.isDone()) { - Thread.sleep(1000L); - } + firstOperation.whenDone(EMPTY_CALLBACK); + secondOperation.whenDone(EMPTY_CALLBACK); Set addressSet = ImmutableSet.copyOf(addressNames); Compute.AddressFilter filter = Compute.AddressFilter.equals(Compute.AddressField.NAME, prefix + "\\d"); @@ -812,9 +814,7 @@ public void testCreateGetAndDeleteGlobalAddress() throws InterruptedException { AddressId addressId = GlobalAddressId.of(name); AddressInfo addressInfo = AddressInfo.of(addressId); Operation operation = compute.create(addressInfo); - while (!operation.isDone()) { - Thread.sleep(1000L); - } + operation.whenDone(EMPTY_CALLBACK); // test get Address remoteAddress = compute.getAddress(addressId); assertNotNull(remoteAddress); @@ -833,9 +833,7 @@ public void testCreateGetAndDeleteGlobalAddress() throws InterruptedException { assertNull(remoteAddress.creationTimestamp()); assertNull(remoteAddress.generatedId()); operation = remoteAddress.delete(); - while (!operation.isDone()) { - Thread.sleep(1000L); - } + operation.whenDone(EMPTY_CALLBACK); assertNull(compute.getAddress(addressId)); } @@ -847,12 +845,8 @@ public void testListGlobalAddresses() throws InterruptedException { AddressId secondAddressId = GlobalAddressId.of(addressNames[1]); Operation firstOperation = compute.create(AddressInfo.of(firstAddressId)); Operation secondOperation = compute.create(AddressInfo.of(secondAddressId)); - while (!firstOperation.isDone()) { - Thread.sleep(1000L); - } - while (!secondOperation.isDone()) { - Thread.sleep(1000L); - } + firstOperation.whenDone(EMPTY_CALLBACK); + secondOperation.whenDone(EMPTY_CALLBACK); Set addressSet = ImmutableSet.copyOf(addressNames); // test list Compute.AddressFilter filter = @@ -900,9 +894,7 @@ public void testCreateGetResizeAndDeleteStandardDisk() throws InterruptedExcepti DiskInfo diskInfo = DiskInfo.of(diskId, StandardDiskConfiguration.of(DiskTypeId.of(ZONE, "pd-ssd"), 100L)); Operation operation = compute.create(diskInfo); - while (!operation.isDone()) { - Thread.sleep(1000L); - } + operation.whenDone(EMPTY_CALLBACK); // test get Disk remoteDisk = compute.getDisk(diskId); assertNotNull(remoteDisk); @@ -918,9 +910,7 @@ public void testCreateGetResizeAndDeleteStandardDisk() throws InterruptedExcepti assertNull(remoteDisk.lastAttachTimestamp()); assertNull(remoteDisk.lastDetachTimestamp()); operation = remoteDisk.resize(200L); - while (!operation.isDone()) { - Thread.sleep(1000L); - } + operation.whenDone(EMPTY_CALLBACK); // test resize and get with selected fields remoteDisk = compute.getDisk(diskId, Compute.DiskOption.fields(Compute.DiskField.SIZE_GB)); assertNotNull(remoteDisk); @@ -936,9 +926,7 @@ public void testCreateGetResizeAndDeleteStandardDisk() throws InterruptedExcepti assertNull(remoteDisk.lastAttachTimestamp()); assertNull(remoteDisk.lastDetachTimestamp()); operation = remoteDisk.delete(); - while (!operation.isDone()) { - Thread.sleep(1000L); - } + operation.whenDone(EMPTY_CALLBACK); assertNull(compute.getDisk(diskId)); } @@ -948,9 +936,7 @@ public void testCreateGetAndDeleteImageDisk() throws InterruptedException { DiskId diskId = DiskId.of(ZONE, name); DiskInfo diskInfo = DiskInfo.of(diskId, ImageDiskConfiguration.of(IMAGE_ID)); Operation operation = compute.create(diskInfo); - while (!operation.isDone()) { - Thread.sleep(1000L); - } + operation.whenDone(EMPTY_CALLBACK); // test get Disk remoteDisk = compute.getDisk(diskId); assertNotNull(remoteDisk); @@ -985,9 +971,7 @@ public void testCreateGetAndDeleteImageDisk() throws InterruptedException { assertNull(remoteDisk.lastAttachTimestamp()); assertNull(remoteDisk.lastDetachTimestamp()); operation = remoteDisk.delete(); - while (!operation.isDone()) { - Thread.sleep(1000L); - } + operation.whenDone(EMPTY_CALLBACK); assertNull(compute.getDisk(diskId)); } @@ -1001,14 +985,10 @@ public void testCreateGetAndDeleteSnapshotAndSnapshotDisk() throws InterruptedEx DiskInfo diskInfo = DiskInfo.of(diskId, StandardDiskConfiguration.of(DiskTypeId.of(ZONE, "pd-ssd"), 100L)); Operation operation = compute.create(diskInfo); - while (!operation.isDone()) { - Thread.sleep(1000L); - } + operation.whenDone(EMPTY_CALLBACK); Disk remoteDisk = compute.getDisk(diskId); operation = remoteDisk.createSnapshot(snapshotName); - while (!operation.isDone()) { - Thread.sleep(1000L); - } + operation.whenDone(EMPTY_CALLBACK); // test get snapshot with selected fields Snapshot snapshot = compute.getSnapshot(snapshotName, Compute.SnapshotOption.fields(Compute.SnapshotField.CREATION_TIMESTAMP)); @@ -1038,9 +1018,7 @@ public void testCreateGetAndDeleteSnapshotAndSnapshotDisk() throws InterruptedEx diskInfo = DiskInfo.of(snapshotDiskId, SnapshotDiskConfiguration.of(SnapshotId.of(snapshotName))); operation = compute.create(diskInfo); - while (!operation.isDone()) { - Thread.sleep(1000L); - } +operation.whenDone(EMPTY_CALLBACK); // test get disk remoteDisk = compute.getDisk(snapshotDiskId); assertNotNull(remoteDisk); @@ -1076,14 +1054,10 @@ public void testCreateGetAndDeleteSnapshotAndSnapshotDisk() throws InterruptedEx assertNull(remoteDisk.lastAttachTimestamp()); assertNull(remoteDisk.lastDetachTimestamp()); operation = remoteDisk.delete(); - while (!operation.isDone()) { - Thread.sleep(1000L); - } +operation.whenDone(EMPTY_CALLBACK); assertNull(compute.getDisk(snapshotDiskId)); operation = snapshot.delete(); - while (!operation.isDone()) { - Thread.sleep(1000L); - } +operation.whenDone(EMPTY_CALLBACK); assertNull(compute.getSnapshot(snapshotName)); } @@ -1097,12 +1071,8 @@ public void testListDisksAndSnapshots() throws InterruptedException { StandardDiskConfiguration.of(DiskTypeId.of(ZONE, "pd-ssd"), 100L); Operation firstOperation = compute.create(DiskInfo.of(firstDiskId, configuration)); Operation secondOperation = compute.create(DiskInfo.of(secondDiskId, configuration)); - while (!firstOperation.isDone()) { - Thread.sleep(1000L); - } - while (!secondOperation.isDone()) { - Thread.sleep(1000L); - } + firstOperation.whenDone(EMPTY_CALLBACK); + secondOperation.whenDone(EMPTY_CALLBACK); Set diskSet = ImmutableSet.copyOf(diskNames); // test list disks Compute.DiskFilter diskFilter = @@ -1154,12 +1124,8 @@ public void testListDisksAndSnapshots() throws InterruptedException { SnapshotId secondSnapshotId = SnapshotId.of(diskNames[1]); firstOperation = compute.create(SnapshotInfo.of(firstSnapshotId, firstDiskId)); secondOperation = compute.create(SnapshotInfo.of(secondSnapshotId, secondDiskId)); - while (!firstOperation.isDone()) { - Thread.sleep(1000L); - } - while (!secondOperation.isDone()) { - Thread.sleep(1000L); - } + firstOperation.whenDone(EMPTY_CALLBACK); + secondOperation.whenDone(EMPTY_CALLBACK); // test list snapshots Compute.SnapshotFilter snapshotFilter = Compute.SnapshotFilter.equals(Compute.SnapshotField.NAME, prefix + "\\d"); @@ -1217,12 +1183,8 @@ public void testAggregatedListDisks() throws InterruptedException { StandardDiskConfiguration.of(DiskTypeId.of(ZONE, "pd-ssd"), 100L); Operation firstOperation = compute.create(DiskInfo.of(firstDiskId, configuration)); Operation secondOperation = compute.create(DiskInfo.of(secondDiskId, configuration)); - while (!firstOperation.isDone()) { - Thread.sleep(1000L); - } - while (!secondOperation.isDone()) { - Thread.sleep(1000L); - } + firstOperation.whenDone(EMPTY_CALLBACK); + secondOperation.whenDone(EMPTY_CALLBACK); Set zoneSet = ImmutableSet.copyOf(diskZones); Set diskSet = ImmutableSet.copyOf(diskNames); Compute.DiskFilter diskFilter = @@ -1258,15 +1220,11 @@ public void testCreateGetAndDeprecateImage() throws InterruptedException { DiskInfo diskInfo = DiskInfo.of(diskId, StandardDiskConfiguration.of(DiskTypeId.of(ZONE, "pd-ssd"), 100L)); Operation operation = compute.create(diskInfo); - while (!operation.isDone()) { - Thread.sleep(1000L); - } +operation.whenDone(EMPTY_CALLBACK); Disk remoteDisk = compute.getDisk(diskId); ImageInfo imageInfo = ImageInfo.of(imageId, DiskImageConfiguration.of(diskId)); operation = compute.create(imageInfo); - while (!operation.isDone()) { - Thread.sleep(1000L); - } +operation.whenDone(EMPTY_CALLBACK); // test get image with selected fields Image image = compute.getImage(imageId, Compute.ImageOption.fields(Compute.ImageField.CREATION_TIMESTAMP)); @@ -1302,16 +1260,12 @@ public void testCreateGetAndDeprecateImage() throws InterruptedException { .deprecated(System.currentTimeMillis()) .build(); operation = image.deprecate(deprecationStatus); - while (!operation.isDone()) { - Thread.sleep(1000L); - } +operation.whenDone(EMPTY_CALLBACK); image = compute.getImage(imageId); assertEquals(deprecationStatus, image.deprecationStatus()); remoteDisk.delete(); operation = image.delete(); - while (!operation.isDone()) { - Thread.sleep(1000L); - } +operation.whenDone(EMPTY_CALLBACK); assertNull(compute.getImage(imageId)); } @@ -1382,9 +1336,7 @@ public void testCreateAndGetNetwork() throws InterruptedException { NetworkInfo networkInfo = NetworkInfo.of(networkId, StandardNetworkConfiguration.of("192.168.0.0/16")); Operation operation = compute.create(networkInfo); - while (!operation.isDone()) { - Thread.sleep(1000L); - } +operation.whenDone(EMPTY_CALLBACK); // test get network with selected fields Network network = compute.getNetwork(networkId.network(), Compute.NetworkOption.fields(Compute.NetworkField.CREATION_TIMESTAMP)); @@ -1404,9 +1356,7 @@ public void testCreateAndGetNetwork() throws InterruptedException { remoteConfiguration = network.configuration(); assertEquals("192.168.0.0/16", remoteConfiguration.ipRange()); operation = network.delete(); - while (!operation.isDone()) { - Thread.sleep(1000L); - } +operation.whenDone(EMPTY_CALLBACK); assertNull(compute.getNetwork(name)); } @@ -1417,9 +1367,7 @@ public void testListNetworks() throws InterruptedException { NetworkInfo networkInfo = NetworkInfo.of(networkId, StandardNetworkConfiguration.of("192.168.0.0/16")); Operation operation = compute.create(networkInfo); - while (!operation.isDone()) { - Thread.sleep(1000L); - } +operation.whenDone(EMPTY_CALLBACK); // test list Compute.NetworkFilter filter = Compute.NetworkFilter.equals(Compute.NetworkField.NAME, name); Page networkPage = compute.listNetworks(Compute.NetworkListOption.filter(filter)); @@ -1454,9 +1402,7 @@ public void testListNetworks() throws InterruptedException { } assertEquals(1, count); operation = compute.deleteNetwork(networkId); - while (!operation.isDone()) { - Thread.sleep(1000L); - } +operation.whenDone(EMPTY_CALLBACK); assertNull(compute.getNetwork(name)); } @@ -1466,9 +1412,7 @@ public void testCreateNetworkAndSubnetwork() throws InterruptedException { NetworkId networkId = NetworkId.of(networkName); NetworkInfo networkInfo = NetworkInfo.of(networkId, SubnetNetworkConfiguration.of(false)); Operation operation = compute.create(networkInfo); - while (!operation.isDone()) { - Thread.sleep(1000L); - } +operation.whenDone(EMPTY_CALLBACK); // test get network Network network = compute.getNetwork(networkId.network()); assertEquals(networkId.network(), network.networkId().network()); @@ -1481,9 +1425,7 @@ public void testCreateNetworkAndSubnetwork() throws InterruptedException { SubnetworkId subnetworkId = SubnetworkId.of(REGION, subnetworkName); SubnetworkInfo subnetworkInfo = SubnetworkInfo.of(subnetworkId, networkId, "192.168.0.0/16"); operation = compute.create(subnetworkInfo); - while (!operation.isDone()) { - Thread.sleep(1000L); - } +operation.whenDone(EMPTY_CALLBACK); // test get subnetwork with selected fields Subnetwork subnetwork = compute.getSubnetwork(subnetworkId, Compute.SubnetworkOption.fields(Compute.SubnetworkField.CREATION_TIMESTAMP)); @@ -1538,13 +1480,9 @@ public void testCreateNetworkAndSubnetwork() throws InterruptedException { } assertEquals(1, count); operation = subnetwork.delete(); - while (!operation.isDone()) { - Thread.sleep(1000L); - } +operation.whenDone(EMPTY_CALLBACK); operation = compute.deleteNetwork(networkId); - while (!operation.isDone()) { - Thread.sleep(1000L); - } +operation.whenDone(EMPTY_CALLBACK); assertNull(compute.getSubnetwork(subnetworkId)); assertNull(compute.getNetwork(networkName)); } @@ -1555,9 +1493,7 @@ public void testAggregatedListSubnetworks() throws InterruptedException { NetworkId networkId = NetworkId.of(networkName); NetworkInfo networkInfo = NetworkInfo.of(networkId, SubnetNetworkConfiguration.of(false)); Operation operation = compute.create(networkInfo); - while (!operation.isDone()) { - Thread.sleep(1000L); - } +operation.whenDone(EMPTY_CALLBACK); String prefix = BASE_RESOURCE_NAME + "list-subnetwork"; String[] regionNames = {"us-central1", "us-east1"}; String[] subnetworkNames = {prefix + "1", prefix + "2"}; @@ -1570,12 +1506,8 @@ public void testAggregatedListSubnetworks() throws InterruptedException { SubnetworkInfo.of(secondSubnetworkId, networkId, ipRanges[1]); Operation firstOperation = compute.create(firstSubnetworkInfo); Operation secondOperation = compute.create(secondSubnetworkInfo); - while (!firstOperation.isDone()) { - Thread.sleep(1000L); - } - while (!secondOperation.isDone()) { - Thread.sleep(1000L); - } + firstOperation.whenDone(EMPTY_CALLBACK); + secondOperation.whenDone(EMPTY_CALLBACK); Set regionSet = ImmutableSet.copyOf(regionNames); Set subnetworkSet = ImmutableSet.copyOf(subnetworkNames); Set rangeSet = ImmutableSet.copyOf(ipRanges); @@ -1599,16 +1531,10 @@ public void testAggregatedListSubnetworks() throws InterruptedException { assertEquals(2, count); firstOperation = compute.deleteSubnetwork(firstSubnetworkId); secondOperation = compute.deleteSubnetwork(secondSubnetworkId); - while (!firstOperation.isDone()) { - Thread.sleep(1000L); - } - while (!secondOperation.isDone()) { - Thread.sleep(1000L); - } + firstOperation.whenDone(EMPTY_CALLBACK); + secondOperation.whenDone(EMPTY_CALLBACK); operation = compute.deleteNetwork(networkId); - while (!operation.isDone()) { - Thread.sleep(1000L); - } +operation.whenDone(EMPTY_CALLBACK); assertNull(compute.getNetwork(networkName)); } @@ -1620,9 +1546,7 @@ public void testCreateGetAndDeleteInstance() throws InterruptedException { AddressId addressId = RegionAddressId.of(REGION, addressName); AddressInfo addressInfo = AddressInfo.of(addressId); Operation operation = compute.create(addressInfo); - while (!operation.isDone()) { - Thread.sleep(1000L); - } +operation.whenDone(EMPTY_CALLBACK); Address address = compute.getAddress(addressId); // Create an instance InstanceId instanceId = InstanceId.of(ZONE, instanceName); @@ -1640,9 +1564,7 @@ public void testCreateGetAndDeleteInstance() throws InterruptedException { .networkInterfaces(networkInterface) .build(); operation = compute.create(instanceInfo); - while (!operation.isDone()) { - Thread.sleep(1000L); - } +operation.whenDone(EMPTY_CALLBACK); // test get Instance remoteInstance = compute.getInstance(instanceId); assertEquals(instanceName, remoteInstance.instanceId().instance()); @@ -1694,9 +1616,7 @@ public void testCreateGetAndDeleteInstance() throws InterruptedException { String newSerialPortOutput = remoteInstance.getSerialPortOutput(1); assertTrue(newSerialPortOutput.contains(serialPortOutput)); operation = remoteInstance.delete(); - while (!operation.isDone()) { - Thread.sleep(1000L); - } +operation.whenDone(EMPTY_CALLBACK); assertNull(compute.getInstance(instanceId)); address.delete(); } @@ -1715,30 +1635,22 @@ public void testStartStopAndResetInstance() throws InterruptedException { .networkInterfaces(networkInterface) .build(); Operation operation = compute.create(instanceInfo); - while (!operation.isDone()) { - Thread.sleep(1000L); - } +operation.whenDone(EMPTY_CALLBACK); Instance remoteInstance = compute.getInstance(instanceId, Compute.InstanceOption.fields(Compute.InstanceField.STATUS)); assertEquals(InstanceInfo.Status.RUNNING, remoteInstance.status()); operation = remoteInstance.stop(); - while (!operation.isDone()) { - Thread.sleep(1000L); - } +operation.whenDone(EMPTY_CALLBACK); remoteInstance = compute.getInstance(instanceId, Compute.InstanceOption.fields(Compute.InstanceField.STATUS)); assertEquals(InstanceInfo.Status.TERMINATED, remoteInstance.status()); operation = remoteInstance.start(); - while (!operation.isDone()) { - Thread.sleep(1000L); - } +operation.whenDone(EMPTY_CALLBACK); remoteInstance = compute.getInstance(instanceId, Compute.InstanceOption.fields(Compute.InstanceField.STATUS)); assertEquals(InstanceInfo.Status.RUNNING, remoteInstance.status()); operation = remoteInstance.reset(); - while (!operation.isDone()) { - Thread.sleep(1000L); - } +operation.whenDone(EMPTY_CALLBACK); remoteInstance = compute.getInstance(instanceId, Compute.InstanceOption.fields(Compute.InstanceField.STATUS)); assertEquals(InstanceInfo.Status.RUNNING, remoteInstance.status()); @@ -1759,44 +1671,32 @@ public void testSetInstanceProperties() throws InterruptedException { .networkInterfaces(networkInterface) .build(); Operation operation = compute.create(instanceInfo); - while (!operation.isDone()) { - Thread.sleep(1000L); - } +operation.whenDone(EMPTY_CALLBACK); Instance remoteInstance = compute.getInstance(instanceId); // test set tags List tags = ImmutableList.of("tag1", "tag2"); operation = remoteInstance.setTags(tags); - while (!operation.isDone()) { - Thread.sleep(1000L); - } +operation.whenDone(EMPTY_CALLBACK); remoteInstance = compute.getInstance(instanceId); assertEquals(tags, remoteInstance.tags().values()); // test set metadata Map metadata = ImmutableMap.of("key", "value"); operation = remoteInstance.setMetadata(metadata); - while (!operation.isDone()) { - Thread.sleep(1000L); - } +operation.whenDone(EMPTY_CALLBACK); remoteInstance = compute.getInstance(instanceId); assertEquals(metadata, remoteInstance.metadata().values()); // test set machine type operation = remoteInstance.stop(); - while (!operation.isDone()) { - Thread.sleep(1000L); - } +operation.whenDone(EMPTY_CALLBACK); operation = remoteInstance.setMachineType(MachineTypeId.of(ZONE, "n1-standard-1")); - while (!operation.isDone()) { - Thread.sleep(1000L); - } +operation.whenDone(EMPTY_CALLBACK); remoteInstance = compute.getInstance(instanceId); assertEquals("n1-standard-1", remoteInstance.machineType().type()); assertEquals(ZONE, remoteInstance.machineType().zone()); // test set scheduling options SchedulingOptions options = SchedulingOptions.standard(false, SchedulingOptions.Maintenance.TERMINATE); operation = remoteInstance.setSchedulingOptions(options); - while (!operation.isDone()) { - Thread.sleep(1000L); - } +operation.whenDone(EMPTY_CALLBACK); remoteInstance = compute.getInstance(instanceId); assertEquals(options, remoteInstance.schedulingOptions()); remoteInstance.delete(); @@ -1820,19 +1720,13 @@ public void testAttachAndDetachDisk() throws InterruptedException { DiskId diskId = DiskId.of(ZONE, diskName); Operation diskOperation = compute.create(DiskInfo.of(diskId, StandardDiskConfiguration.of(DiskTypeId.of(ZONE, "pd-ssd")))); - while (!instanceOperation.isDone()) { - Thread.sleep(1000L); - } - while (!diskOperation.isDone()) { - Thread.sleep(1000L); - } + instanceOperation.whenDone(EMPTY_CALLBACK); + diskOperation.whenDone(EMPTY_CALLBACK); Instance remoteInstance = compute.getInstance(instanceId); // test attach disk instanceOperation = remoteInstance.attachDisk("dev1", AttachedDisk.PersistentDiskConfiguration.builder(diskId).build()); - while (!instanceOperation.isDone()) { - Thread.sleep(1000L); - } + instanceOperation.whenDone(EMPTY_CALLBACK); remoteInstance = compute.getInstance(instanceId); Set deviceSet = ImmutableSet.of("dev0", "dev1"); assertEquals(2, remoteInstance.attachedDisks().size()); @@ -1841,9 +1735,7 @@ public void testAttachAndDetachDisk() throws InterruptedException { } // test set disk auto-delete instanceOperation = remoteInstance.setDiskAutoDelete("dev1", true); - while (!instanceOperation.isDone()) { - Thread.sleep(1000L); - } + instanceOperation.whenDone(EMPTY_CALLBACK); remoteInstance = compute.getInstance(instanceId); assertEquals(2, remoteInstance.attachedDisks().size()); for (AttachedDisk remoteAttachedDisk : remoteInstance.attachedDisks()) { @@ -1852,9 +1744,7 @@ public void testAttachAndDetachDisk() throws InterruptedException { } // test detach disk instanceOperation = remoteInstance.detachDisk("dev1"); - while (!instanceOperation.isDone()) { - Thread.sleep(1000L); - } + instanceOperation.whenDone(EMPTY_CALLBACK); remoteInstance = compute.getInstance(instanceId); assertEquals(1, remoteInstance.attachedDisks().size()); assertEquals("dev0", remoteInstance.attachedDisks().get(0).deviceName()); @@ -1880,15 +1770,8 @@ public void testAddAndRemoveAccessConfig() throws InterruptedException { AddressId addressId = RegionAddressId.of(REGION, addressName); AddressInfo addressInfo = AddressInfo.of(addressId); Operation addressOperation = compute.create(addressInfo); - while (!addressOperation.isDone()) { - Thread.sleep(1000L); - } - while (!instanceOperation.isDone()) { - Thread.sleep(1000L); - } - while (!addressOperation.isDone()) { - Thread.sleep(1000L); - } + addressOperation.whenDone(EMPTY_CALLBACK); + instanceOperation.whenDone(EMPTY_CALLBACK); Address remoteAddress = compute.getAddress(addressId); Instance remoteInstance = compute.getInstance(instanceId); String networkInterfaceName = remoteInstance.networkInterfaces().get(0).name(); @@ -1898,9 +1781,7 @@ public void testAddAndRemoveAccessConfig() throws InterruptedException { .name("NAT") .build(); instanceOperation = remoteInstance.addAccessConfig(networkInterfaceName, accessConfig); - while (!instanceOperation.isDone()) { - Thread.sleep(1000L); - } + instanceOperation.whenDone(EMPTY_CALLBACK); remoteInstance = compute.getInstance(instanceId); List accessConfigurations = remoteInstance.networkInterfaces().get(0).accessConfigurations(); @@ -1908,9 +1789,7 @@ public void testAddAndRemoveAccessConfig() throws InterruptedException { assertEquals("NAT", accessConfigurations.get(0).name()); // test delete access config instanceOperation = remoteInstance.deleteAccessConfig(networkInterfaceName, "NAT"); - while (!instanceOperation.isDone()) { - Thread.sleep(1000L); - } + instanceOperation.whenDone(EMPTY_CALLBACK); remoteInstance = compute.getInstance(instanceId); assertTrue(remoteInstance.networkInterfaces().get(0).accessConfigurations().isEmpty()); remoteInstance.delete(); diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/snippets/CreateAddressDiskAndInstance.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/snippets/CreateAddressDiskAndInstance.java index 5334f746c95b..4948f68b6db8 100644 --- a/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/snippets/CreateAddressDiskAndInstance.java +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/snippets/CreateAddressDiskAndInstance.java @@ -33,8 +33,12 @@ import com.google.cloud.compute.NetworkInterface; import com.google.cloud.compute.NetworkInterface.AccessConfig; import com.google.cloud.compute.Operation; +import com.google.cloud.compute.Operation.OperationError; +import com.google.cloud.compute.Operation.OperationWarning; import com.google.cloud.compute.RegionAddressId; +import java.util.List; + /** * A snippet for Google Cloud Compute Engine showing how to create a disk and an address. The * snippet also shows how to create a virtual machine instance using the created disk and address. @@ -47,43 +51,45 @@ public static void main(String... args) throws InterruptedException { Compute compute = ComputeOptions.defaultInstance().service(); // Create an external region address - RegionAddressId addressId = RegionAddressId.of("us-central1", "test-address"); + final RegionAddressId addressId = RegionAddressId.of("us-central1", "test-address"); Operation operation = compute.create(AddressInfo.of(addressId)); // Wait for operation to complete - while (!operation.isDone()) { - Thread.sleep(1000L); - } - // Check operation errors - operation = operation.reload(); - if (operation.errors() == null) { - System.out.println("Address " + addressId + " was successfully created"); - } else { - // inspect operation.errors() - throw new RuntimeException("Address creation failed"); - } + operation.whenDone(new Operation.CompletionCallback() { + @Override + public void success(Operation operation) { + System.out.println("Address " + addressId + " was successfully created"); + } + + @Override + public void error(List errors, List warnings) { + // inspect errors + throw new RuntimeException("Address creation failed"); + } + }); // Create a persistent disk ImageId imageId = ImageId.of("debian-cloud", "debian-8-jessie-v20160329"); - DiskId diskId = DiskId.of("us-central1-a", "test-disk"); + final DiskId diskId = DiskId.of("us-central1-a", "test-disk"); ImageDiskConfiguration diskConfiguration = ImageDiskConfiguration.of(imageId); DiskInfo disk = DiskInfo.of(diskId, diskConfiguration); operation = compute.create(disk); // Wait for operation to complete - while (!operation.isDone()) { - Thread.sleep(1000L); - } - // Check operation errors - operation = operation.reload(); - if (operation.errors() == null) { - System.out.println("Disk " + diskId + " was successfully created"); - } else { - // inspect operation.errors() - throw new RuntimeException("Disk creation failed"); - } + operation.whenDone(new Operation.CompletionCallback() { + @Override + public void success(Operation operation) { + System.out.println("Disk " + diskId + " was successfully created"); + } + + @Override + public void error(List errors, List warnings) { + // inspect errors + throw new RuntimeException("Disk creation failed"); + } + }); // Create a virtual machine instance Address externalIp = compute.getAddress(addressId); - InstanceId instanceId = InstanceId.of("us-central1-a", "test-instance"); + final InstanceId instanceId = InstanceId.of("us-central1-a", "test-instance"); NetworkId networkId = NetworkId.of("default"); PersistentDiskConfiguration attachConfiguration = PersistentDiskConfiguration.builder(diskId).boot(true).build(); @@ -96,16 +102,17 @@ public static void main(String... args) throws InterruptedException { InstanceInfo.of(instanceId, machineTypeId, attachedDisk, networkInterface); operation = compute.create(instance); // Wait for operation to complete - while (!operation.isDone()) { - Thread.sleep(1000L); - } - // Check operation errors - operation = operation.reload(); - if (operation.errors() == null) { - System.out.println("Instance " + instanceId + " was successfully created"); - } else { - // inspect operation.errors() - throw new RuntimeException("Instance creation failed"); - } + operation.whenDone(new Operation.CompletionCallback() { + @Override + public void success(Operation operation) { + System.out.println("Instance " + instanceId + " was successfully created"); + } + + @Override + public void error(List errors, List warnings) { + // inspect errors + throw new RuntimeException("Instance creation failed"); + } + }); } } diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/snippets/CreateInstance.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/snippets/CreateInstance.java index d8162908d133..0e70aa2ea83f 100644 --- a/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/snippets/CreateInstance.java +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/snippets/CreateInstance.java @@ -27,6 +27,10 @@ import com.google.cloud.compute.NetworkId; import com.google.cloud.compute.NetworkInterface; import com.google.cloud.compute.Operation; +import com.google.cloud.compute.Operation.OperationError; +import com.google.cloud.compute.Operation.OperationWarning; + +import java.util.List; /** * A snippet for Google Cloud Compute Engine showing how to create a virtual machine instance. @@ -34,21 +38,27 @@ public class CreateInstance { public static void main(String... args) throws InterruptedException { - Compute compute = ComputeOptions.defaultInstance().service(); + final Compute compute = ComputeOptions.defaultInstance().service(); ImageId imageId = ImageId.of("debian-cloud", "debian-8-jessie-v20160329"); NetworkId networkId = NetworkId.of("default"); AttachedDisk attachedDisk = AttachedDisk.of(AttachedDisk.CreateDiskConfiguration.of(imageId)); NetworkInterface networkInterface = NetworkInterface.of(networkId); - InstanceId instanceId = InstanceId.of("us-central1-a", "instance-name"); + final InstanceId instanceId = InstanceId.of("us-central1-a", "instance-name"); MachineTypeId machineTypeId = MachineTypeId.of("us-central1-a", "n1-standard-1"); Operation operation = compute.create(InstanceInfo.of(instanceId, machineTypeId, attachedDisk, networkInterface)); - while (!operation.isDone()) { - Thread.sleep(1000L); - } - if (operation.errors() == null) { - // use instance - Instance instance = compute.getInstance(instanceId); - } + operation.whenDone(new Operation.CompletionCallback() { + @Override + public void success(Operation operation) { + // use instance + Instance instance = compute.getInstance(instanceId); + } + + @Override + public void error(List errors, List warnings) { + // inspect errors + throw new RuntimeException("Instance creation failed"); + } + }); } } diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/snippets/CreateSnapshot.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/snippets/CreateSnapshot.java index cc8029936186..1c364bf0c353 100644 --- a/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/snippets/CreateSnapshot.java +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/snippets/CreateSnapshot.java @@ -21,8 +21,12 @@ import com.google.cloud.compute.Disk; import com.google.cloud.compute.DiskId; import com.google.cloud.compute.Operation; +import com.google.cloud.compute.Operation.OperationError; +import com.google.cloud.compute.Operation.OperationWarning; import com.google.cloud.compute.Snapshot; +import java.util.List; + /** * A snippet for Google Cloud Compute Engine showing how to create a snapshot of a disk if the disk * exists. @@ -30,19 +34,25 @@ public class CreateSnapshot { public static void main(String... args) throws InterruptedException { - Compute compute = ComputeOptions.defaultInstance().service(); + final Compute compute = ComputeOptions.defaultInstance().service(); DiskId diskId = DiskId.of("us-central1-a", "disk-name"); Disk disk = compute.getDisk(diskId, Compute.DiskOption.fields()); if (disk != null) { - String snapshotName = "disk-name-snapshot"; + final String snapshotName = "disk-name-snapshot"; Operation operation = disk.createSnapshot(snapshotName); - while (!operation.isDone()) { - Thread.sleep(1000L); - } - if (operation.errors() == null) { - // use snapshot - Snapshot snapshot = compute.getSnapshot("disk-name-snapshot"); - } + operation.whenDone(new Operation.CompletionCallback() { + @Override + public void success(Operation operation) { + // use snapshot + Snapshot snapshot = compute.getSnapshot(snapshotName); + } + + @Override + public void error(List errors, List warnings) { + // inspect errors + throw new RuntimeException("Snaphsot creation failed"); + } + }); } } } From fe296fac3e533e2891a4d58457ce31bc4d28d8b8 Mon Sep 17 00:00:00 2001 From: Marco Ziccardi Date: Mon, 23 May 2016 11:52:15 +0200 Subject: [PATCH 3/7] Add blocking waitFor method, remove whenDone and callback --- README.md | 50 ++---- .../java/com/google/cloud/bigquery/Job.java | 81 +++++----- .../google/cloud/bigquery/package-info.java | 15 +- .../com/google/cloud/bigquery/JobTest.java | 66 ++++---- .../cloud/bigquery/it/ITBigQueryTest.java | 42 ++--- gcloud-java-compute/README.md | 63 +++----- .../com/google/cloud/compute/Operation.java | 80 +++++----- .../google/cloud/compute/package-info.java | 34 ++-- .../google/cloud/compute/OperationTest.java | 63 +++++--- .../cloud/compute/it/ITComputeTest.java | 146 ++++++++---------- .../examples/bigquery/BigQueryExample.java | 25 ++- .../snippets/CreateTableAndLoadData.java | 20 +-- .../CreateAddressDiskAndInstance.java | 61 +++----- .../compute/snippets/CreateInstance.java | 22 +-- .../compute/snippets/CreateSnapshot.java | 22 +-- 15 files changed, 333 insertions(+), 457 deletions(-) diff --git a/README.md b/README.md index 5e86976601f6..c0ae99ed71c2 100644 --- a/README.md +++ b/README.md @@ -152,7 +152,6 @@ Complete source code can be found at ```java import com.google.cloud.bigquery.BigQuery; -import com.google.cloud.bigquery.BigQueryError; import com.google.cloud.bigquery.BigQueryOptions; import com.google.cloud.bigquery.Field; import com.google.cloud.bigquery.FormatOptions; @@ -163,8 +162,6 @@ import com.google.cloud.bigquery.Table; import com.google.cloud.bigquery.TableId; import com.google.cloud.bigquery.TableInfo; -import java.util.List; - BigQuery bigquery = BigQueryOptions.defaultInstance().service(); TableId tableId = TableId.of("dataset", "table"); Table table = bigquery.getTable(tableId); @@ -176,17 +173,12 @@ if (table == null) { } System.out.println("Loading data into table " + tableId); Job loadJob = table.load(FormatOptions.csv(), "gs://bucket/path"); -loadJob.whenDone(new Job.CompletionCallback() { - @Override - public void success(Job job) { - System.out.println("Job succeeded"); - } - - @Override - public void error(BigQueryError error, List executionErrors) { - System.out.println("Job completed with errors"); - } -}); +loadJob = loadJob.waitFor(); +if (loadJob.status().error() != null) { + System.out.println("Job completed with errors"); +} else { + System.out.println("Job succeeded"); +} ``` Google Cloud Compute (Alpha) @@ -209,31 +201,19 @@ import com.google.cloud.compute.Compute; import com.google.cloud.compute.ComputeOptions; import com.google.cloud.compute.Disk; import com.google.cloud.compute.DiskId; -import com.google.cloud.compute.Operation.OperationError; -import com.google.cloud.compute.Operation.OperationWarning; import com.google.cloud.compute.Snapshot; -import java.util.List; - final Compute compute = ComputeOptions.defaultInstance().service(); DiskId diskId = DiskId.of("us-central1-a", "disk-name"); Disk disk = compute.getDisk(diskId, Compute.DiskOption.fields()); if (disk != null) { final String snapshotName = "disk-name-snapshot"; Operation operation = disk.createSnapshot(snapshotName); - operation.whenDone(new Operation.CompletionCallback() { - @Override - public void success(Operation operation) { - // use snapshot - Snapshot snapshot = compute.getSnapshot(snapshotName); - } - - @Override - public void error(List errors, List warnings) { - // inspect errors - throw new RuntimeException("Snaphsot creation failed"); - } - }); + operation = operation.waitFor(); + if (operation.errors() == null) { + // use snapshot + Snapshot snapshot = compute.getSnapshot(snapshotName); + } } ``` The second snippet shows how to create a virtual machine instance. Complete source code can be found @@ -249,10 +229,6 @@ import com.google.cloud.compute.InstanceId; import com.google.cloud.compute.InstanceInfo; import com.google.cloud.compute.MachineTypeId; import com.google.cloud.compute.NetworkId; -import com.google.cloud.compute.Operation.OperationError; -import com.google.cloud.compute.Operation.OperationWarning; - -import java.util.List; Compute compute = ComputeOptions.defaultInstance().service(); ImageId imageId = ImageId.of("debian-cloud", "debian-8-jessie-v20160329"); @@ -263,9 +239,7 @@ InstanceId instanceId = InstanceId.of("us-central1-a", "instance-name"); MachineTypeId machineTypeId = MachineTypeId.of("us-central1-a", "n1-standard-1"); Operation operation = compute.create(InstanceInfo.of(instanceId, machineTypeId, attachedDisk, networkInterface)); -while (!operation.isDone()) { - Thread.sleep(1000L); -} +operation = operation.waitFor(); if (operation.errors() == null) { // use instance Instance instance = compute.getInstance(instanceId); diff --git a/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/Job.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/Job.java index caea0ee38390..0089bff21dd7 100644 --- a/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/Job.java +++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/Job.java @@ -20,8 +20,8 @@ import java.io.IOException; import java.io.ObjectInputStream; -import java.util.List; import java.util.Objects; +import java.util.concurrent.TimeUnit; /** * A Google BigQuery Job. @@ -38,24 +38,6 @@ public class Job extends JobInfo { private final BigQueryOptions options; private transient BigQuery bigquery; - /** - * A callback for job completion. - */ - public interface CompletionCallback { - /** - * The method called when the job completes successfully. - */ - void success(Job job); - - /** - * The method called when the job completes with errors. {@code error} is the final error that - * caused the job to fail (see {@link JobStatus#error()}). {@code executionErrors} are all the - * errors (possibly not fatal) encountered by the job during its execution (see - * {@link JobStatus#executionErrors()}). - */ - void error(BigQueryError error, List executionErrors); - } - /** * A builder for {@code Job} objects. */ @@ -163,40 +145,51 @@ public boolean isDone() { } /** - * Waits until this job completes its execution, either failing or succeeding. If the job does not - * exist, this method returns without executing any method of the provided callback. If the job - * completed successfully the {@link CompletionCallback#success(Job)} method is called. If the job - * completed with errors the {@link CompletionCallback#error(BigQueryError, List)} method is - * called. + * Blocks until this job completes its execution, either failing or succeeding. The job status is + * checked every 500 milliseconds. This method returns current job's latest information. If the + * job no longer exists, this method returns {@code null}. *
 {@code
-   * job.whenDone(new CompletionCallback() {
-   *   void success(Job job) {
-   *     // completed successfully
-   *   }
+   * Job completedJob = job.waitFor();
+   * if (completedJob == null) {
+   *   // job no longer exists
+   * } else if (completedJob.status().error() != null) {
+   *   // job failed, handle error
+   * } else {
+   *   // job completed successfully
+   * }}
* - * void error(BigQueryError error, List executionErrors) { - * // handle error - * } - * });} + * @throws BigQueryException upon failure + * @throws InterruptedException if the current thread gets interrupted while waiting for the job + * to complete + */ + public Job waitFor() throws InterruptedException { + return waitFor(500, TimeUnit.MILLISECONDS); + } + + /** + * Blocks until this job completes its execution, either failing or succeeding. The + * {@code checkEvery} and {@code unit} parameters determine how often the job's status is checked. + * This method returns current job's latest information. If the job no longer exists, this method + * returns {@code null}. + *
 {@code
+   * Job completedJob = job.waitFor(1, TimeUnit.SECONDS);
+   * if (completedJob == null) {
+   *   // job no longer exists
+   * } else if (completedJob.status().error() != null) {
+   *   // job failed, handle error
+   * } else {
+   *   // job completed successfully
+   * }}
* * @throws BigQueryException upon failure * @throws InterruptedException if the current thread gets interrupted while waiting for the job * to complete */ - public void whenDone(CompletionCallback callback) throws InterruptedException { + public Job waitFor(int checkEvery, TimeUnit unit) throws InterruptedException { while (!isDone()) { - Thread.sleep(500L); - } - Job updatedJob = reload(); - if (updatedJob == null) { - return; - } - BigQueryError error = updatedJob.status().error(); - if (error != null) { - callback.error(error, updatedJob.status().executionErrors()); - } else { - callback.success(updatedJob); + unit.sleep(checkEvery); } + return reload(); } /** diff --git a/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/package-info.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/package-info.java index 19e1d96fedef..3b4d392d26dc 100644 --- a/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/package-info.java +++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/package-info.java @@ -33,15 +33,12 @@ * } * System.out.println("Loading data into table " + tableId); * Job loadJob = table.load(FormatOptions.csv(), "gs://bucket/path"); - * loadJob.whenDone(new Job.CompletionCallback() { - * public void success(Job job) { - * System.out.println("Job succeeded"); - * } - * - * public void error(BigQueryError error, List executionErrors) { - * System.out.println("Job completed with errors"); - * } - * });} + * loadJob = loadJob.waitFor(); + * if (loadJob.status().error() != null) { + * System.out.println("Job completed with errors"); + * } else { + * System.out.println("Job succeeded"); + * }} * * @see Google Cloud BigQuery */ diff --git a/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/JobTest.java b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/JobTest.java index 11896baded43..26178054d304 100644 --- a/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/JobTest.java +++ b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/JobTest.java @@ -28,13 +28,12 @@ import static org.junit.Assert.assertTrue; import com.google.cloud.bigquery.JobStatistics.CopyStatistics; -import com.google.common.collect.ImmutableList; import org.easymock.EasyMock; import org.junit.After; import org.junit.Test; -import java.util.List; +import java.util.concurrent.TimeUnit; public class JobTest { @@ -182,60 +181,71 @@ public void testIsDone_NotExists() throws Exception { } @Test - public void testWhenDone_Success() throws InterruptedException { + public void testWaitFor() throws InterruptedException { initializeExpectedJob(2); - Job.CompletionCallback callback = EasyMock.mock(Job.CompletionCallback.class); BigQuery.JobOption[] expectedOptions = {BigQuery.JobOption.fields(BigQuery.JobField.STATUS)}; JobStatus status = createStrictMock(JobStatus.class); expect(status.state()).andReturn(JobStatus.State.DONE); - expect(status.error()).andReturn(null); expect(bigquery.options()).andReturn(mockOptions); Job completedJob = expectedJob.toBuilder().status(status).build(); expect(bigquery.getJob(JOB_INFO.jobId(), expectedOptions)).andReturn(completedJob); expect(bigquery.getJob(JOB_INFO.jobId())).andReturn(completedJob); - callback.success(completedJob); - EasyMock.expectLastCall(); - replay(status, bigquery, callback); + replay(status, bigquery); initializeJob(); - job.whenDone(callback); - verify(status, callback); + assertSame(completedJob, job.waitFor()); + verify(status); } @Test - public void testWhenDone_Error() throws InterruptedException { - initializeExpectedJob(2); - Job.CompletionCallback callback = EasyMock.mock(Job.CompletionCallback.class); + public void testWaitFor_Null() throws InterruptedException { + initializeExpectedJob(1); + BigQuery.JobOption[] expectedOptions = {BigQuery.JobOption.fields(BigQuery.JobField.STATUS)}; + expect(bigquery.options()).andReturn(mockOptions); + expect(bigquery.getJob(JOB_INFO.jobId(), expectedOptions)).andReturn(null); + expect(bigquery.getJob(JOB_INFO.jobId())).andReturn(null); + replay(bigquery); + initializeJob(); + assertNull(job.waitFor()); + } + + @Test + public void testWaitForWithTimeUnit() throws InterruptedException { + initializeExpectedJob(3); BigQuery.JobOption[] expectedOptions = {BigQuery.JobOption.fields(BigQuery.JobField.STATUS)}; - BigQueryError error = new BigQueryError("reason", "location", "message"); - List executionErrors = ImmutableList.of(error); + TimeUnit timeUnit = createStrictMock(TimeUnit.class); + timeUnit.sleep(42); + EasyMock.expectLastCall(); JobStatus status = createStrictMock(JobStatus.class); + expect(status.state()).andReturn(JobStatus.State.RUNNING); expect(status.state()).andReturn(JobStatus.State.DONE); - expect(status.error()).andReturn(error); - expect(status.executionErrors()).andReturn(executionErrors); expect(bigquery.options()).andReturn(mockOptions); + Job runningJob = expectedJob.toBuilder().status(status).build(); Job completedJob = expectedJob.toBuilder().status(status).build(); + expect(bigquery.getJob(JOB_INFO.jobId(), expectedOptions)).andReturn(runningJob); expect(bigquery.getJob(JOB_INFO.jobId(), expectedOptions)).andReturn(completedJob); expect(bigquery.getJob(JOB_INFO.jobId())).andReturn(completedJob); - callback.error(error, executionErrors); - EasyMock.expectLastCall(); - replay(status, bigquery, callback); + replay(status, bigquery, timeUnit); initializeJob(); - job.whenDone(callback); - verify(status, callback); + assertSame(completedJob, job.waitFor(42, timeUnit)); + verify(status, timeUnit); } @Test - public void testWhenDone_Null() throws InterruptedException { - initializeExpectedJob(1); - Job.CompletionCallback callback = EasyMock.mock(Job.CompletionCallback.class); + public void testWaitForWithTimeUnit_Null() throws InterruptedException { + initializeExpectedJob(2); BigQuery.JobOption[] expectedOptions = {BigQuery.JobOption.fields(BigQuery.JobField.STATUS)}; + TimeUnit timeUnit = createStrictMock(TimeUnit.class); + timeUnit.sleep(42); + EasyMock.expectLastCall(); expect(bigquery.options()).andReturn(mockOptions); + Job runningJob = expectedJob.toBuilder().status(new JobStatus(JobStatus.State.RUNNING)).build(); + expect(bigquery.getJob(JOB_INFO.jobId(), expectedOptions)).andReturn(runningJob); expect(bigquery.getJob(JOB_INFO.jobId(), expectedOptions)).andReturn(null); expect(bigquery.getJob(JOB_INFO.jobId())).andReturn(null); - replay(bigquery, callback); + replay(bigquery, timeUnit); initializeJob(); - job.whenDone(callback); - verify(callback); + assertNull(job.waitFor(42, timeUnit)); + verify(bigquery, timeUnit); } @Test diff --git a/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/it/ITBigQueryTest.java b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/it/ITBigQueryTest.java index 444b25add682..92151ea8bc4c 100644 --- a/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/it/ITBigQueryTest.java +++ b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/it/ITBigQueryTest.java @@ -69,7 +69,6 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import org.easymock.EasyMock; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Rule; @@ -188,11 +187,8 @@ public static void beforeClass() throws InterruptedException { .schema(TABLE_SCHEMA) .build(); Job job = bigquery.create(JobInfo.of(configuration)); - Job.CompletionCallback callback = EasyMock.createStrictMock(Job.CompletionCallback.class); - callback.success(EasyMock.anyObject()); - EasyMock.replay(callback); - job.whenDone(callback); - EasyMock.verify(callback); + job = job.waitFor(); + assertNull(job.status().error()); } @AfterClass @@ -800,11 +796,8 @@ public void testCopyJob() throws InterruptedException { TableId destinationTable = TableId.of(DATASET, destinationTableName); CopyJobConfiguration configuration = CopyJobConfiguration.of(destinationTable, sourceTable); Job remoteJob = bigquery.create(JobInfo.of(configuration)); - Job.CompletionCallback callback = EasyMock.createStrictMock(Job.CompletionCallback.class); - callback.success(EasyMock.anyObject()); - EasyMock.replay(callback); - remoteJob.whenDone(callback); - EasyMock.verify(callback); + remoteJob = remoteJob.waitFor(); + assertNull(remoteJob.status().error()); Table remoteTable = bigquery.getTable(DATASET, destinationTableName); assertNotNull(remoteTable); assertEquals(destinationTable.dataset(), remoteTable.tableId().dataset()); @@ -827,11 +820,8 @@ public void testQueryJob() throws InterruptedException { .destinationTable(destinationTable) .build(); Job remoteJob = bigquery.create(JobInfo.of(configuration)); - Job.CompletionCallback callback = EasyMock.createStrictMock(Job.CompletionCallback.class); - callback.success(EasyMock.anyObject()); - EasyMock.replay(callback); - remoteJob.whenDone(callback); - EasyMock.reset(callback); + remoteJob = remoteJob.waitFor(); + assertNull(remoteJob.status().error()); QueryResponse response = bigquery.getQueryResults(remoteJob.jobId()); while (!response.jobCompleted()) { @@ -869,21 +859,16 @@ public void testExtractJob() throws InterruptedException { .schema(SIMPLE_SCHEMA) .build(); Job remoteLoadJob = bigquery.create(JobInfo.of(configuration)); - Job.CompletionCallback callback = EasyMock.createStrictMock(Job.CompletionCallback.class); - callback.success(EasyMock.anyObject()); - EasyMock.replay(callback); - remoteLoadJob.whenDone(callback); - EasyMock.reset(callback); + remoteLoadJob = remoteLoadJob.waitFor(); + assertNull(remoteLoadJob.status().error()); ExtractJobConfiguration extractConfiguration = ExtractJobConfiguration.builder(destinationTable, "gs://" + BUCKET + "/" + EXTRACT_FILE) .printHeader(false) .build(); Job remoteExtractJob = bigquery.create(JobInfo.of(extractConfiguration)); - callback.success(EasyMock.anyObject()); - EasyMock.replay(callback); - remoteExtractJob.whenDone(callback); - EasyMock.verify(callback); + remoteExtractJob = remoteExtractJob.waitFor(); + assertNull(remoteExtractJob.status().error()); assertEquals(CSV_CONTENT, new String(storage.readAllBytes(BUCKET, EXTRACT_FILE), StandardCharsets.UTF_8)); assertTrue(bigquery.delete(DATASET, tableName)); @@ -900,11 +885,8 @@ public void testCancelJob() throws InterruptedException { .build(); Job remoteJob = bigquery.create(JobInfo.of(configuration)); assertTrue(remoteJob.cancel()); - Job.CompletionCallback callback = EasyMock.createStrictMock(Job.CompletionCallback.class); - callback.success(EasyMock.anyObject()); - EasyMock.replay(callback); - remoteJob.whenDone(callback); - EasyMock.verify(callback); + remoteJob = remoteJob.waitFor(); + assertNull(remoteJob.status().error()); } @Test diff --git a/gcloud-java-compute/README.md b/gcloud-java-compute/README.md index f137572acc91..e6661ffb909b 100644 --- a/gcloud-java-compute/README.md +++ b/gcloud-java-compute/README.md @@ -104,11 +104,7 @@ Add the following imports at the top of your file: ```java import com.google.cloud.compute.AddressInfo; import com.google.cloud.compute.Operation; -import com.google.cloud.compute.Operation.OperationError; -import com.google.cloud.compute.Operation.OperationWarning; import com.google.cloud.compute.RegionAddressId; - -import java.util.List; ``` Then add the following code to create an address. Most Compute Engine calls return an `Operation` @@ -119,18 +115,13 @@ succeeded: final RegionAddressId addressId = RegionAddressId.of("us-central1", "test-address"); Operation operation = compute.create(AddressInfo.of(addressId)); // Wait for operation to complete -operation.whenDone(new Operation.CompletionCallback() { - @Override - public void success(Operation operation) { - System.out.println("Address " + addressId + " was successfully created"); - } - - @Override - public void error(List errors, List warnings) { - // inspect errors - throw new RuntimeException("Address creation failed"); - } -}); +operation = operation.waitFor(); +if (operation.errors() == null) { + System.out.println("Address " + addressId + " was successfully created"); +} else { + // inspect operation.errors() + throw new RuntimeException("Address creation failed"); +} ``` #### Creating a persistent disk @@ -157,18 +148,14 @@ DiskId diskId = DiskId.of("us-central1-a", "test-disk"); ImageDiskConfiguration diskConfiguration = ImageDiskConfiguration.of(imageId); DiskInfo disk = DiskInfo.of(diskId, diskConfiguration); Operation operation = compute.create(disk); -operation.whenDone(new Operation.CompletionCallback() { - @Override - public void success(Operation operation) { - System.out.println("Disk " + diskId + " was successfully created"); - } - - @Override - public void error(List errors, List warnings) { - // inspect errors - throw new RuntimeException("Disk creation failed"); - } -}); +// Wait for operation to complete +operation = operation.waitFor(); +if (operation.errors() == null) { + System.out.println("Disk " + diskId + " was successfully created"); +} else { + // inspect operation.errors() + throw new RuntimeException("Disk creation failed"); +} ``` #### Creating a virtual machine instance @@ -207,18 +194,14 @@ MachineTypeId machineTypeId = MachineTypeId.of("us-central1-a", "n1-standard-1") InstanceInfo instance = InstanceInfo.of(instanceId, machineTypeId, attachedDisk, networkInterface); Operation operation = compute.create(instance); -operation.whenDone(new Operation.CompletionCallback() { - @Override - public void success(Operation operation) { - System.out.println("Instance " + instanceId + " was successfully created"); - } - - @Override - public void error(List errors, List warnings) { - // inspect errors - throw new RuntimeException("Instance creation failed"); - } -}); +// Wait for operation to complete +operation = operation.waitFor(); +if (operation.errors() == null) { + System.out.println("Instance " + instanceId + " was successfully created"); +} else { + // inspect operation.errors() + throw new RuntimeException("Instance creation failed"); +} ``` #### Complete source code diff --git a/gcloud-java-compute/src/main/java/com/google/cloud/compute/Operation.java b/gcloud-java-compute/src/main/java/com/google/cloud/compute/Operation.java index 9ec6ccc54278..cf8afb04cfbb 100644 --- a/gcloud-java-compute/src/main/java/com/google/cloud/compute/Operation.java +++ b/gcloud-java-compute/src/main/java/com/google/cloud/compute/Operation.java @@ -36,6 +36,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.concurrent.TimeUnit; /** * Google Compute Engine operations. Operation identity can be obtained via {@link #operationId()}. @@ -292,24 +293,6 @@ public int hashCode() { } } - /** - * A callback for operation completion. - */ - public interface CompletionCallback { - /** - * The method called when the operation completes successfully. - */ - void success(Operation operation); - - /** - * The method called when the operation completes with errors. {@code errors} contains all - * errors encountered while processing this operation (see {@link Operation#errors()}). - * {@code warnings} contains all warnings encountered while processing this operation (see - * {@link Operation#warnings()}). - */ - void error(List errors, List warnings); - } - static final class Builder { private Compute compute; @@ -677,40 +660,51 @@ public boolean isDone() { } /** - * Waits until this operation completes its execution, either failing or succeeding. If the - * operation does not exist, this method returns without executing any method of the provided - * callback. If the operation completed successfully the - * {@link CompletionCallback#success(Operation)} method is called. If the operation completed with - * errors the {@link CompletionCallback#error(List, List)} method is called. + * Blocks until this operation completes its execution, either failing or succeeding. The + * operation status is checked every 500 milliseconds. This method returns current operation's + * latest information. If the operation no longer exists, this method returns {@code null}. *
 {@code
-   * operation.whenDone(new CompletionCallback() {
-   *   void success(Operation operation) {
-   *     // completed successfully
-   *   }
+   * Operation completedOperation = operation.waitFor();
+   * if (completedOperation == null) {
+   *   // operation no longer exists
+   * } else if (completedOperation.errors() != null) {
+   *   // operation failed, handle error
+   * } else {
+   *   // operation completed successfully
+   * }}
* - * void error(List errors, List warnings) { - * // handle error - * } - * });} + * @throws ComputeException upon failure + * @throws InterruptedException if the current thread gets interrupted while waiting for the + * operation to complete + */ + public Operation waitFor() throws InterruptedException { + return waitFor(500, TimeUnit.MILLISECONDS); + } + + /** + * Blocks until this operation completes its execution, either failing or succeeding. The + * {@code checkEvery} and {@code unit} parameters determine how often the operation status is + * checked. This method returns current operation's latest information. If the operation no longer + * exists, this method returns {@code null}. + *
 {@code
+   * Operation completedOperation = operation.waitFor(1, TimeUnit.SECONDS);
+   * if (completedOperation == null) {
+   *   // operation no longer exists
+   * } else if (completedOperation.errors() != null) {
+   *   // operation failed, handle error
+   * } else {
+   *   // operation completed successfully
+   * }}
* * @throws ComputeException upon failure * @throws InterruptedException if the current thread gets interrupted while waiting for the * operation to complete */ - public void whenDone(CompletionCallback callback) throws InterruptedException { + public Operation waitFor(int checkEvery, TimeUnit unit) throws InterruptedException { while (!isDone()) { - Thread.sleep(500L); - } - Operation updatedOperation = reload(); - if (updatedOperation == null) { - return; - } - List errors = updatedOperation.errors(); - if (errors != null) { - callback.error(errors, updatedOperation.warnings()); - } else { - callback.success(updatedOperation); + unit.sleep(checkEvery); } + return reload(); } /** diff --git a/gcloud-java-compute/src/main/java/com/google/cloud/compute/package-info.java b/gcloud-java-compute/src/main/java/com/google/cloud/compute/package-info.java index 3b1c703fa9b0..da0a43ac43cf 100644 --- a/gcloud-java-compute/src/main/java/com/google/cloud/compute/package-info.java +++ b/gcloud-java-compute/src/main/java/com/google/cloud/compute/package-info.java @@ -28,17 +28,11 @@ * if (disk != null) { * final String snapshotName = "disk-name-snapshot"; * Operation operation = disk.createSnapshot(snapshotName); - * operation.whenDone(new Operation.CompletionCallback() { - * public void success(Operation operation) { - * // use snapshot - * Snapshot snapshot = compute.getSnapshot(snapshotName); - * } - * - * public void error(List errors, List warnings) { - * // inspect errors - * throw new RuntimeException("Snaphsot creation failed"); - * } - * }); + * operation = operation.waitFor(); + * if (operation.errors() == null) { + * // use snapshot + * Snapshot snapshot = compute.getSnapshot(snapshotName); + * } * }} *

This second example shows how to create a virtual machine instance. Complete source code can * be found at @@ -53,18 +47,12 @@ * final InstanceId instanceId = InstanceId.of("us-central1-a", "instance-name"); * MachineTypeId machineTypeId = MachineTypeId.of("us-central1-a", "n1-standard-1"); * Operation operation = - * compute.create(InstanceInfo.of(instanceId, machineTypeId, attachedDisk, networkInterface)); - * operation.whenDone(new Operation.CompletionCallback() { - * public void success(Operation operation) { - * // use instance - * Instance instance = compute.getInstance(instanceId); - * } - * - * public void error(List errors, List warnings) { - * // inspect errors - * throw new RuntimeException("Instance creation failed"); - * } - * });} + * compute.create(InstanceInfo.of(instanceId, machineTypeId, attachedDisk, networkInterface)); + * operation = operation.waitFor(); + * if (operation.errors() == null) { + * // use instance + * Instance instance = compute.getInstance(instanceId); + * }} * * @see Google Cloud Compute */ diff --git a/gcloud-java-compute/src/test/java/com/google/cloud/compute/OperationTest.java b/gcloud-java-compute/src/test/java/com/google/cloud/compute/OperationTest.java index b07411e1607e..63a0fc511d26 100644 --- a/gcloud-java-compute/src/test/java/com/google/cloud/compute/OperationTest.java +++ b/gcloud-java-compute/src/test/java/com/google/cloud/compute/OperationTest.java @@ -38,6 +38,7 @@ import org.junit.Test; import java.util.List; +import java.util.concurrent.TimeUnit; public class OperationTest { @@ -356,9 +357,8 @@ public void testIsDone_NotExists() throws Exception { } @Test - public void testWhenDone_Success() throws InterruptedException { + public void testWaitFor() throws InterruptedException { initializeExpectedOperation(4); - Operation.CompletionCallback callback = EasyMock.mock(Operation.CompletionCallback.class); Compute.OperationOption[] expectedOptions = {Compute.OperationOption.fields(Compute.OperationField.STATUS)}; Operation successOperation = @@ -366,44 +366,65 @@ public void testWhenDone_Success() throws InterruptedException { expect(compute.options()).andReturn(mockOptions); expect(compute.getOperation(GLOBAL_OPERATION_ID, expectedOptions)).andReturn(successOperation); expect(compute.getOperation(GLOBAL_OPERATION_ID)).andReturn(successOperation); - callback.success(successOperation); - EasyMock.expectLastCall(); - replay(compute, callback); + replay(compute); initializeOperation(); - operation.whenDone(callback); - verify(callback); + assertSame(successOperation, operation.waitFor()); } @Test - public void testWhenDone_Error() throws InterruptedException { + public void testWaitFor_Null() throws InterruptedException { initializeExpectedOperation(3); - Operation.CompletionCallback callback = EasyMock.mock(Operation.CompletionCallback.class); Compute.OperationOption[] expectedOptions = {Compute.OperationOption.fields(Compute.OperationField.STATUS)}; expect(compute.options()).andReturn(mockOptions); - expect(compute.getOperation(GLOBAL_OPERATION_ID, expectedOptions)).andReturn(globalOperation); - expect(compute.getOperation(GLOBAL_OPERATION_ID)).andReturn(globalOperation); - callback.error(ERRORS, WARNINGS); + expect(compute.getOperation(GLOBAL_OPERATION_ID, expectedOptions)).andReturn(null); + expect(compute.getOperation(GLOBAL_OPERATION_ID)).andReturn(null); + replay(compute); + initializeOperation(); + assertNull(operation.waitFor()); + } + + @Test + public void testWaitForWithTimeUnit() throws InterruptedException { + initializeExpectedOperation(5); + Compute.OperationOption[] expectedOptions = + {Compute.OperationOption.fields(Compute.OperationField.STATUS)}; + TimeUnit timeUnit = createStrictMock(TimeUnit.class); + timeUnit.sleep(42); EasyMock.expectLastCall(); - replay(compute, callback); + Operation runningOperation = Operation.fromPb(serviceMockReturnsOptions, + globalOperation.toPb().setError(null).setStatus("RUNNING")); + Operation completedOperation = + Operation.fromPb(serviceMockReturnsOptions, globalOperation.toPb().setError(null)); + expect(compute.options()).andReturn(mockOptions); + expect(compute.getOperation(GLOBAL_OPERATION_ID, expectedOptions)).andReturn(runningOperation); + expect(compute.getOperation(GLOBAL_OPERATION_ID, expectedOptions)) + .andReturn(completedOperation); + expect(compute.getOperation(GLOBAL_OPERATION_ID)).andReturn(completedOperation); + replay(compute, timeUnit); initializeOperation(); - operation.whenDone(callback); - verify(callback); + assertSame(completedOperation, operation.waitFor(42, timeUnit)); + verify(timeUnit); } @Test - public void testWhenDone_Null() throws InterruptedException { - initializeExpectedOperation(3); - Operation.CompletionCallback callback = EasyMock.mock(Operation.CompletionCallback.class); + public void testWaitForWithTimeUnit_Null() throws InterruptedException { + initializeExpectedOperation(4); Compute.OperationOption[] expectedOptions = {Compute.OperationOption.fields(Compute.OperationField.STATUS)}; + TimeUnit timeUnit = createStrictMock(TimeUnit.class); + timeUnit.sleep(42); + EasyMock.expectLastCall(); + Operation runningOperation = Operation.fromPb(serviceMockReturnsOptions, + globalOperation.toPb().setError(null).setStatus("RUNNING")); expect(compute.options()).andReturn(mockOptions); + expect(compute.getOperation(GLOBAL_OPERATION_ID, expectedOptions)).andReturn(runningOperation); expect(compute.getOperation(GLOBAL_OPERATION_ID, expectedOptions)).andReturn(null); expect(compute.getOperation(GLOBAL_OPERATION_ID)).andReturn(null); - replay(compute, callback); + replay(compute, timeUnit); initializeOperation(); - operation.whenDone(callback); - verify(callback); + assertNull(operation.waitFor(42, timeUnit)); + verify(compute, timeUnit); } @Test diff --git a/gcloud-java-compute/src/test/java/com/google/cloud/compute/it/ITComputeTest.java b/gcloud-java-compute/src/test/java/com/google/cloud/compute/it/ITComputeTest.java index e67efd7e5ae2..ba9b58d775e1 100644 --- a/gcloud-java-compute/src/test/java/com/google/cloud/compute/it/ITComputeTest.java +++ b/gcloud-java-compute/src/test/java/com/google/cloud/compute/it/ITComputeTest.java @@ -56,9 +56,6 @@ import com.google.cloud.compute.NetworkInfo; import com.google.cloud.compute.NetworkInterface; import com.google.cloud.compute.Operation; -import com.google.cloud.compute.Operation.CompletionCallback; -import com.google.cloud.compute.Operation.OperationError; -import com.google.cloud.compute.Operation.OperationWarning; import com.google.cloud.compute.Region; import com.google.cloud.compute.RegionAddressId; import com.google.cloud.compute.RegionOperationId; @@ -103,17 +100,6 @@ public class ITComputeTest { private static final ImageId IMAGE_ID = ImageId.of("debian-cloud", "debian-8-jessie-v20160219"); private static final String IMAGE_PROJECT = "debian-cloud"; - private static final CompletionCallback EMPTY_CALLBACK = new CompletionCallback() { - @Override - public void success(Operation operation) { - // do nothing - } - - @Override - public void error(List errors, List warnings) { - // do noting - } - }; private static Compute compute; @Rule @@ -700,7 +686,7 @@ public void testCreateGetAndDeleteRegionAddress() throws InterruptedException { AddressId addressId = RegionAddressId.of(REGION, name); AddressInfo addressInfo = AddressInfo.of(addressId); Operation operation = compute.create(addressInfo); - operation.whenDone(EMPTY_CALLBACK); + operation.waitFor(); // test get Address remoteAddress = compute.getAddress(addressId); assertNotNull(remoteAddress); @@ -721,7 +707,7 @@ public void testCreateGetAndDeleteRegionAddress() throws InterruptedException { assertNull(remoteAddress.creationTimestamp()); assertNull(remoteAddress.generatedId()); operation = remoteAddress.delete(); - operation.whenDone(EMPTY_CALLBACK); + operation.waitFor(); assertNull(compute.getAddress(addressId)); } @@ -733,8 +719,8 @@ public void testListRegionAddresses() throws InterruptedException { AddressId secondAddressId = RegionAddressId.of(REGION, addressNames[1]); Operation firstOperation = compute.create(AddressInfo.of(firstAddressId)); Operation secondOperation = compute.create(AddressInfo.of(secondAddressId)); - firstOperation.whenDone(EMPTY_CALLBACK); - secondOperation.whenDone(EMPTY_CALLBACK); + firstOperation.waitFor(); + secondOperation.waitFor(); Set addressSet = ImmutableSet.copyOf(addressNames); // test list Compute.AddressFilter filter = @@ -785,8 +771,8 @@ public void testAggregatedListAddresses() throws InterruptedException { AddressId secondAddressId = GlobalAddressId.of(REGION, addressNames[1]); Operation firstOperation = compute.create(AddressInfo.of(firstAddressId)); Operation secondOperation = compute.create(AddressInfo.of(secondAddressId)); - firstOperation.whenDone(EMPTY_CALLBACK); - secondOperation.whenDone(EMPTY_CALLBACK); + firstOperation.waitFor(); + secondOperation.waitFor(); Set addressSet = ImmutableSet.copyOf(addressNames); Compute.AddressFilter filter = Compute.AddressFilter.equals(Compute.AddressField.NAME, prefix + "\\d"); @@ -814,7 +800,7 @@ public void testCreateGetAndDeleteGlobalAddress() throws InterruptedException { AddressId addressId = GlobalAddressId.of(name); AddressInfo addressInfo = AddressInfo.of(addressId); Operation operation = compute.create(addressInfo); - operation.whenDone(EMPTY_CALLBACK); + operation.waitFor(); // test get Address remoteAddress = compute.getAddress(addressId); assertNotNull(remoteAddress); @@ -833,7 +819,7 @@ public void testCreateGetAndDeleteGlobalAddress() throws InterruptedException { assertNull(remoteAddress.creationTimestamp()); assertNull(remoteAddress.generatedId()); operation = remoteAddress.delete(); - operation.whenDone(EMPTY_CALLBACK); + operation.waitFor(); assertNull(compute.getAddress(addressId)); } @@ -845,8 +831,8 @@ public void testListGlobalAddresses() throws InterruptedException { AddressId secondAddressId = GlobalAddressId.of(addressNames[1]); Operation firstOperation = compute.create(AddressInfo.of(firstAddressId)); Operation secondOperation = compute.create(AddressInfo.of(secondAddressId)); - firstOperation.whenDone(EMPTY_CALLBACK); - secondOperation.whenDone(EMPTY_CALLBACK); + firstOperation.waitFor(); + secondOperation.waitFor(); Set addressSet = ImmutableSet.copyOf(addressNames); // test list Compute.AddressFilter filter = @@ -894,7 +880,7 @@ public void testCreateGetResizeAndDeleteStandardDisk() throws InterruptedExcepti DiskInfo diskInfo = DiskInfo.of(diskId, StandardDiskConfiguration.of(DiskTypeId.of(ZONE, "pd-ssd"), 100L)); Operation operation = compute.create(diskInfo); - operation.whenDone(EMPTY_CALLBACK); + operation.waitFor(); // test get Disk remoteDisk = compute.getDisk(diskId); assertNotNull(remoteDisk); @@ -910,7 +896,7 @@ public void testCreateGetResizeAndDeleteStandardDisk() throws InterruptedExcepti assertNull(remoteDisk.lastAttachTimestamp()); assertNull(remoteDisk.lastDetachTimestamp()); operation = remoteDisk.resize(200L); - operation.whenDone(EMPTY_CALLBACK); + operation.waitFor(); // test resize and get with selected fields remoteDisk = compute.getDisk(diskId, Compute.DiskOption.fields(Compute.DiskField.SIZE_GB)); assertNotNull(remoteDisk); @@ -926,7 +912,7 @@ public void testCreateGetResizeAndDeleteStandardDisk() throws InterruptedExcepti assertNull(remoteDisk.lastAttachTimestamp()); assertNull(remoteDisk.lastDetachTimestamp()); operation = remoteDisk.delete(); - operation.whenDone(EMPTY_CALLBACK); + operation.waitFor(); assertNull(compute.getDisk(diskId)); } @@ -936,7 +922,7 @@ public void testCreateGetAndDeleteImageDisk() throws InterruptedException { DiskId diskId = DiskId.of(ZONE, name); DiskInfo diskInfo = DiskInfo.of(diskId, ImageDiskConfiguration.of(IMAGE_ID)); Operation operation = compute.create(diskInfo); - operation.whenDone(EMPTY_CALLBACK); + operation.waitFor(); // test get Disk remoteDisk = compute.getDisk(diskId); assertNotNull(remoteDisk); @@ -971,7 +957,7 @@ public void testCreateGetAndDeleteImageDisk() throws InterruptedException { assertNull(remoteDisk.lastAttachTimestamp()); assertNull(remoteDisk.lastDetachTimestamp()); operation = remoteDisk.delete(); - operation.whenDone(EMPTY_CALLBACK); + operation.waitFor(); assertNull(compute.getDisk(diskId)); } @@ -985,10 +971,10 @@ public void testCreateGetAndDeleteSnapshotAndSnapshotDisk() throws InterruptedEx DiskInfo diskInfo = DiskInfo.of(diskId, StandardDiskConfiguration.of(DiskTypeId.of(ZONE, "pd-ssd"), 100L)); Operation operation = compute.create(diskInfo); - operation.whenDone(EMPTY_CALLBACK); + operation.waitFor(); Disk remoteDisk = compute.getDisk(diskId); operation = remoteDisk.createSnapshot(snapshotName); - operation.whenDone(EMPTY_CALLBACK); + operation.waitFor(); // test get snapshot with selected fields Snapshot snapshot = compute.getSnapshot(snapshotName, Compute.SnapshotOption.fields(Compute.SnapshotField.CREATION_TIMESTAMP)); @@ -1018,7 +1004,7 @@ public void testCreateGetAndDeleteSnapshotAndSnapshotDisk() throws InterruptedEx diskInfo = DiskInfo.of(snapshotDiskId, SnapshotDiskConfiguration.of(SnapshotId.of(snapshotName))); operation = compute.create(diskInfo); -operation.whenDone(EMPTY_CALLBACK); +operation.waitFor(); // test get disk remoteDisk = compute.getDisk(snapshotDiskId); assertNotNull(remoteDisk); @@ -1054,10 +1040,10 @@ public void testCreateGetAndDeleteSnapshotAndSnapshotDisk() throws InterruptedEx assertNull(remoteDisk.lastAttachTimestamp()); assertNull(remoteDisk.lastDetachTimestamp()); operation = remoteDisk.delete(); -operation.whenDone(EMPTY_CALLBACK); +operation.waitFor(); assertNull(compute.getDisk(snapshotDiskId)); operation = snapshot.delete(); -operation.whenDone(EMPTY_CALLBACK); +operation.waitFor(); assertNull(compute.getSnapshot(snapshotName)); } @@ -1071,8 +1057,8 @@ public void testListDisksAndSnapshots() throws InterruptedException { StandardDiskConfiguration.of(DiskTypeId.of(ZONE, "pd-ssd"), 100L); Operation firstOperation = compute.create(DiskInfo.of(firstDiskId, configuration)); Operation secondOperation = compute.create(DiskInfo.of(secondDiskId, configuration)); - firstOperation.whenDone(EMPTY_CALLBACK); - secondOperation.whenDone(EMPTY_CALLBACK); + firstOperation.waitFor(); + secondOperation.waitFor(); Set diskSet = ImmutableSet.copyOf(diskNames); // test list disks Compute.DiskFilter diskFilter = @@ -1124,8 +1110,8 @@ public void testListDisksAndSnapshots() throws InterruptedException { SnapshotId secondSnapshotId = SnapshotId.of(diskNames[1]); firstOperation = compute.create(SnapshotInfo.of(firstSnapshotId, firstDiskId)); secondOperation = compute.create(SnapshotInfo.of(secondSnapshotId, secondDiskId)); - firstOperation.whenDone(EMPTY_CALLBACK); - secondOperation.whenDone(EMPTY_CALLBACK); + firstOperation.waitFor(); + secondOperation.waitFor(); // test list snapshots Compute.SnapshotFilter snapshotFilter = Compute.SnapshotFilter.equals(Compute.SnapshotField.NAME, prefix + "\\d"); @@ -1183,8 +1169,8 @@ public void testAggregatedListDisks() throws InterruptedException { StandardDiskConfiguration.of(DiskTypeId.of(ZONE, "pd-ssd"), 100L); Operation firstOperation = compute.create(DiskInfo.of(firstDiskId, configuration)); Operation secondOperation = compute.create(DiskInfo.of(secondDiskId, configuration)); - firstOperation.whenDone(EMPTY_CALLBACK); - secondOperation.whenDone(EMPTY_CALLBACK); + firstOperation.waitFor(); + secondOperation.waitFor(); Set zoneSet = ImmutableSet.copyOf(diskZones); Set diskSet = ImmutableSet.copyOf(diskNames); Compute.DiskFilter diskFilter = @@ -1220,11 +1206,11 @@ public void testCreateGetAndDeprecateImage() throws InterruptedException { DiskInfo diskInfo = DiskInfo.of(diskId, StandardDiskConfiguration.of(DiskTypeId.of(ZONE, "pd-ssd"), 100L)); Operation operation = compute.create(diskInfo); -operation.whenDone(EMPTY_CALLBACK); +operation.waitFor(); Disk remoteDisk = compute.getDisk(diskId); ImageInfo imageInfo = ImageInfo.of(imageId, DiskImageConfiguration.of(diskId)); operation = compute.create(imageInfo); -operation.whenDone(EMPTY_CALLBACK); +operation.waitFor(); // test get image with selected fields Image image = compute.getImage(imageId, Compute.ImageOption.fields(Compute.ImageField.CREATION_TIMESTAMP)); @@ -1260,12 +1246,12 @@ public void testCreateGetAndDeprecateImage() throws InterruptedException { .deprecated(System.currentTimeMillis()) .build(); operation = image.deprecate(deprecationStatus); -operation.whenDone(EMPTY_CALLBACK); +operation.waitFor(); image = compute.getImage(imageId); assertEquals(deprecationStatus, image.deprecationStatus()); remoteDisk.delete(); operation = image.delete(); -operation.whenDone(EMPTY_CALLBACK); +operation.waitFor(); assertNull(compute.getImage(imageId)); } @@ -1336,7 +1322,7 @@ public void testCreateAndGetNetwork() throws InterruptedException { NetworkInfo networkInfo = NetworkInfo.of(networkId, StandardNetworkConfiguration.of("192.168.0.0/16")); Operation operation = compute.create(networkInfo); -operation.whenDone(EMPTY_CALLBACK); +operation.waitFor(); // test get network with selected fields Network network = compute.getNetwork(networkId.network(), Compute.NetworkOption.fields(Compute.NetworkField.CREATION_TIMESTAMP)); @@ -1356,7 +1342,7 @@ public void testCreateAndGetNetwork() throws InterruptedException { remoteConfiguration = network.configuration(); assertEquals("192.168.0.0/16", remoteConfiguration.ipRange()); operation = network.delete(); -operation.whenDone(EMPTY_CALLBACK); +operation.waitFor(); assertNull(compute.getNetwork(name)); } @@ -1367,7 +1353,7 @@ public void testListNetworks() throws InterruptedException { NetworkInfo networkInfo = NetworkInfo.of(networkId, StandardNetworkConfiguration.of("192.168.0.0/16")); Operation operation = compute.create(networkInfo); -operation.whenDone(EMPTY_CALLBACK); +operation.waitFor(); // test list Compute.NetworkFilter filter = Compute.NetworkFilter.equals(Compute.NetworkField.NAME, name); Page networkPage = compute.listNetworks(Compute.NetworkListOption.filter(filter)); @@ -1402,7 +1388,7 @@ public void testListNetworks() throws InterruptedException { } assertEquals(1, count); operation = compute.deleteNetwork(networkId); -operation.whenDone(EMPTY_CALLBACK); +operation.waitFor(); assertNull(compute.getNetwork(name)); } @@ -1412,7 +1398,7 @@ public void testCreateNetworkAndSubnetwork() throws InterruptedException { NetworkId networkId = NetworkId.of(networkName); NetworkInfo networkInfo = NetworkInfo.of(networkId, SubnetNetworkConfiguration.of(false)); Operation operation = compute.create(networkInfo); -operation.whenDone(EMPTY_CALLBACK); +operation.waitFor(); // test get network Network network = compute.getNetwork(networkId.network()); assertEquals(networkId.network(), network.networkId().network()); @@ -1425,7 +1411,7 @@ public void testCreateNetworkAndSubnetwork() throws InterruptedException { SubnetworkId subnetworkId = SubnetworkId.of(REGION, subnetworkName); SubnetworkInfo subnetworkInfo = SubnetworkInfo.of(subnetworkId, networkId, "192.168.0.0/16"); operation = compute.create(subnetworkInfo); -operation.whenDone(EMPTY_CALLBACK); +operation.waitFor(); // test get subnetwork with selected fields Subnetwork subnetwork = compute.getSubnetwork(subnetworkId, Compute.SubnetworkOption.fields(Compute.SubnetworkField.CREATION_TIMESTAMP)); @@ -1480,9 +1466,9 @@ public void testCreateNetworkAndSubnetwork() throws InterruptedException { } assertEquals(1, count); operation = subnetwork.delete(); -operation.whenDone(EMPTY_CALLBACK); +operation.waitFor(); operation = compute.deleteNetwork(networkId); -operation.whenDone(EMPTY_CALLBACK); +operation.waitFor(); assertNull(compute.getSubnetwork(subnetworkId)); assertNull(compute.getNetwork(networkName)); } @@ -1493,7 +1479,7 @@ public void testAggregatedListSubnetworks() throws InterruptedException { NetworkId networkId = NetworkId.of(networkName); NetworkInfo networkInfo = NetworkInfo.of(networkId, SubnetNetworkConfiguration.of(false)); Operation operation = compute.create(networkInfo); -operation.whenDone(EMPTY_CALLBACK); +operation.waitFor(); String prefix = BASE_RESOURCE_NAME + "list-subnetwork"; String[] regionNames = {"us-central1", "us-east1"}; String[] subnetworkNames = {prefix + "1", prefix + "2"}; @@ -1506,8 +1492,8 @@ public void testAggregatedListSubnetworks() throws InterruptedException { SubnetworkInfo.of(secondSubnetworkId, networkId, ipRanges[1]); Operation firstOperation = compute.create(firstSubnetworkInfo); Operation secondOperation = compute.create(secondSubnetworkInfo); - firstOperation.whenDone(EMPTY_CALLBACK); - secondOperation.whenDone(EMPTY_CALLBACK); + firstOperation.waitFor(); + secondOperation.waitFor(); Set regionSet = ImmutableSet.copyOf(regionNames); Set subnetworkSet = ImmutableSet.copyOf(subnetworkNames); Set rangeSet = ImmutableSet.copyOf(ipRanges); @@ -1531,10 +1517,10 @@ public void testAggregatedListSubnetworks() throws InterruptedException { assertEquals(2, count); firstOperation = compute.deleteSubnetwork(firstSubnetworkId); secondOperation = compute.deleteSubnetwork(secondSubnetworkId); - firstOperation.whenDone(EMPTY_CALLBACK); - secondOperation.whenDone(EMPTY_CALLBACK); + firstOperation.waitFor(); + secondOperation.waitFor(); operation = compute.deleteNetwork(networkId); -operation.whenDone(EMPTY_CALLBACK); +operation.waitFor(); assertNull(compute.getNetwork(networkName)); } @@ -1546,7 +1532,7 @@ public void testCreateGetAndDeleteInstance() throws InterruptedException { AddressId addressId = RegionAddressId.of(REGION, addressName); AddressInfo addressInfo = AddressInfo.of(addressId); Operation operation = compute.create(addressInfo); -operation.whenDone(EMPTY_CALLBACK); +operation.waitFor(); Address address = compute.getAddress(addressId); // Create an instance InstanceId instanceId = InstanceId.of(ZONE, instanceName); @@ -1564,7 +1550,7 @@ public void testCreateGetAndDeleteInstance() throws InterruptedException { .networkInterfaces(networkInterface) .build(); operation = compute.create(instanceInfo); -operation.whenDone(EMPTY_CALLBACK); +operation.waitFor(); // test get Instance remoteInstance = compute.getInstance(instanceId); assertEquals(instanceName, remoteInstance.instanceId().instance()); @@ -1616,7 +1602,7 @@ public void testCreateGetAndDeleteInstance() throws InterruptedException { String newSerialPortOutput = remoteInstance.getSerialPortOutput(1); assertTrue(newSerialPortOutput.contains(serialPortOutput)); operation = remoteInstance.delete(); -operation.whenDone(EMPTY_CALLBACK); +operation.waitFor(); assertNull(compute.getInstance(instanceId)); address.delete(); } @@ -1635,22 +1621,22 @@ public void testStartStopAndResetInstance() throws InterruptedException { .networkInterfaces(networkInterface) .build(); Operation operation = compute.create(instanceInfo); -operation.whenDone(EMPTY_CALLBACK); +operation.waitFor(); Instance remoteInstance = compute.getInstance(instanceId, Compute.InstanceOption.fields(Compute.InstanceField.STATUS)); assertEquals(InstanceInfo.Status.RUNNING, remoteInstance.status()); operation = remoteInstance.stop(); -operation.whenDone(EMPTY_CALLBACK); +operation.waitFor(); remoteInstance = compute.getInstance(instanceId, Compute.InstanceOption.fields(Compute.InstanceField.STATUS)); assertEquals(InstanceInfo.Status.TERMINATED, remoteInstance.status()); operation = remoteInstance.start(); -operation.whenDone(EMPTY_CALLBACK); +operation.waitFor(); remoteInstance = compute.getInstance(instanceId, Compute.InstanceOption.fields(Compute.InstanceField.STATUS)); assertEquals(InstanceInfo.Status.RUNNING, remoteInstance.status()); operation = remoteInstance.reset(); -operation.whenDone(EMPTY_CALLBACK); +operation.waitFor(); remoteInstance = compute.getInstance(instanceId, Compute.InstanceOption.fields(Compute.InstanceField.STATUS)); assertEquals(InstanceInfo.Status.RUNNING, remoteInstance.status()); @@ -1671,32 +1657,32 @@ public void testSetInstanceProperties() throws InterruptedException { .networkInterfaces(networkInterface) .build(); Operation operation = compute.create(instanceInfo); -operation.whenDone(EMPTY_CALLBACK); +operation.waitFor(); Instance remoteInstance = compute.getInstance(instanceId); // test set tags List tags = ImmutableList.of("tag1", "tag2"); operation = remoteInstance.setTags(tags); -operation.whenDone(EMPTY_CALLBACK); +operation.waitFor(); remoteInstance = compute.getInstance(instanceId); assertEquals(tags, remoteInstance.tags().values()); // test set metadata Map metadata = ImmutableMap.of("key", "value"); operation = remoteInstance.setMetadata(metadata); -operation.whenDone(EMPTY_CALLBACK); +operation.waitFor(); remoteInstance = compute.getInstance(instanceId); assertEquals(metadata, remoteInstance.metadata().values()); // test set machine type operation = remoteInstance.stop(); -operation.whenDone(EMPTY_CALLBACK); +operation.waitFor(); operation = remoteInstance.setMachineType(MachineTypeId.of(ZONE, "n1-standard-1")); -operation.whenDone(EMPTY_CALLBACK); +operation.waitFor(); remoteInstance = compute.getInstance(instanceId); assertEquals("n1-standard-1", remoteInstance.machineType().type()); assertEquals(ZONE, remoteInstance.machineType().zone()); // test set scheduling options SchedulingOptions options = SchedulingOptions.standard(false, SchedulingOptions.Maintenance.TERMINATE); operation = remoteInstance.setSchedulingOptions(options); -operation.whenDone(EMPTY_CALLBACK); +operation.waitFor(); remoteInstance = compute.getInstance(instanceId); assertEquals(options, remoteInstance.schedulingOptions()); remoteInstance.delete(); @@ -1720,13 +1706,13 @@ public void testAttachAndDetachDisk() throws InterruptedException { DiskId diskId = DiskId.of(ZONE, diskName); Operation diskOperation = compute.create(DiskInfo.of(diskId, StandardDiskConfiguration.of(DiskTypeId.of(ZONE, "pd-ssd")))); - instanceOperation.whenDone(EMPTY_CALLBACK); - diskOperation.whenDone(EMPTY_CALLBACK); + instanceOperation.waitFor(); + diskOperation.waitFor(); Instance remoteInstance = compute.getInstance(instanceId); // test attach disk instanceOperation = remoteInstance.attachDisk("dev1", AttachedDisk.PersistentDiskConfiguration.builder(diskId).build()); - instanceOperation.whenDone(EMPTY_CALLBACK); + instanceOperation.waitFor(); remoteInstance = compute.getInstance(instanceId); Set deviceSet = ImmutableSet.of("dev0", "dev1"); assertEquals(2, remoteInstance.attachedDisks().size()); @@ -1735,7 +1721,7 @@ public void testAttachAndDetachDisk() throws InterruptedException { } // test set disk auto-delete instanceOperation = remoteInstance.setDiskAutoDelete("dev1", true); - instanceOperation.whenDone(EMPTY_CALLBACK); + instanceOperation.waitFor(); remoteInstance = compute.getInstance(instanceId); assertEquals(2, remoteInstance.attachedDisks().size()); for (AttachedDisk remoteAttachedDisk : remoteInstance.attachedDisks()) { @@ -1744,7 +1730,7 @@ public void testAttachAndDetachDisk() throws InterruptedException { } // test detach disk instanceOperation = remoteInstance.detachDisk("dev1"); - instanceOperation.whenDone(EMPTY_CALLBACK); + instanceOperation.waitFor(); remoteInstance = compute.getInstance(instanceId); assertEquals(1, remoteInstance.attachedDisks().size()); assertEquals("dev0", remoteInstance.attachedDisks().get(0).deviceName()); @@ -1770,8 +1756,8 @@ public void testAddAndRemoveAccessConfig() throws InterruptedException { AddressId addressId = RegionAddressId.of(REGION, addressName); AddressInfo addressInfo = AddressInfo.of(addressId); Operation addressOperation = compute.create(addressInfo); - addressOperation.whenDone(EMPTY_CALLBACK); - instanceOperation.whenDone(EMPTY_CALLBACK); + addressOperation.waitFor(); + instanceOperation.waitFor(); Address remoteAddress = compute.getAddress(addressId); Instance remoteInstance = compute.getInstance(instanceId); String networkInterfaceName = remoteInstance.networkInterfaces().get(0).name(); @@ -1781,7 +1767,7 @@ public void testAddAndRemoveAccessConfig() throws InterruptedException { .name("NAT") .build(); instanceOperation = remoteInstance.addAccessConfig(networkInterfaceName, accessConfig); - instanceOperation.whenDone(EMPTY_CALLBACK); + instanceOperation.waitFor(); remoteInstance = compute.getInstance(instanceId); List accessConfigurations = remoteInstance.networkInterfaces().get(0).accessConfigurations(); @@ -1789,7 +1775,7 @@ public void testAddAndRemoveAccessConfig() throws InterruptedException { assertEquals("NAT", accessConfigurations.get(0).name()); // test delete access config instanceOperation = remoteInstance.deleteAccessConfig(networkInterfaceName, "NAT"); - instanceOperation.whenDone(EMPTY_CALLBACK); + instanceOperation.waitFor(); remoteInstance = compute.getInstance(instanceId); assertTrue(remoteInstance.networkInterfaces().get(0).accessConfigurations().isEmpty()); remoteInstance.delete(); diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/bigquery/BigQueryExample.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/bigquery/BigQueryExample.java index 41cacef7ed00..156e475a5ac9 100644 --- a/gcloud-java-examples/src/main/java/com/google/cloud/examples/bigquery/BigQueryExample.java +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/bigquery/BigQueryExample.java @@ -523,19 +523,18 @@ private abstract static class JobRunAction extends BigQueryAction { @Override void run(BigQuery bigquery, JobInfo job) throws Exception { System.out.println("Creating job"); - final Job startedJob = bigquery.create(job); - startedJob.whenDone(new Job.CompletionCallback() { - @Override - public void success(Job updatedJob) { - System.out.println("Job " + updatedJob.jobId().job() + " succeeded"); - } - - @Override - public void error(BigQueryError error, List executionErrors) { - System.out.println("Job " + startedJob.jobId().job() + " failed"); - System.out.println("Error: " + startedJob.status().error()); - } - }); + Job startedJob = bigquery.create(job); + while (!startedJob.isDone()) { + System.out.println("Waiting for job " + startedJob.jobId().job() + " to complete"); + Thread.sleep(1000L); + } + startedJob = startedJob.reload(); + if (startedJob.status().error() == null) { + System.out.println("Job " + startedJob.jobId().job() + " succeeded"); + } else { + System.out.println("Job " + startedJob.jobId().job() + " failed"); + System.out.println("Error: " + startedJob.status().error()); + } } } diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/bigquery/snippets/CreateTableAndLoadData.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/bigquery/snippets/CreateTableAndLoadData.java index e74bbc3723cc..8dd76b0265f8 100644 --- a/gcloud-java-examples/src/main/java/com/google/cloud/examples/bigquery/snippets/CreateTableAndLoadData.java +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/bigquery/snippets/CreateTableAndLoadData.java @@ -23,7 +23,6 @@ package com.google.cloud.examples.bigquery.snippets; import com.google.cloud.bigquery.BigQuery; -import com.google.cloud.bigquery.BigQueryError; import com.google.cloud.bigquery.BigQueryOptions; import com.google.cloud.bigquery.Field; import com.google.cloud.bigquery.FormatOptions; @@ -34,8 +33,6 @@ import com.google.cloud.bigquery.TableId; import com.google.cloud.bigquery.TableInfo; -import java.util.List; - /** * A snippet for Google Cloud BigQuery showing how to get a BigQuery table or create it if it does * not exist. The snippet also starts a BigQuery job to load data into the table from a Cloud @@ -55,16 +52,11 @@ public static void main(String... args) throws InterruptedException { } System.out.println("Loading data into table " + tableId); Job loadJob = table.load(FormatOptions.csv(), "gs://bucket/path"); - loadJob.whenDone(new Job.CompletionCallback() { - @Override - public void success(Job job) { - System.out.println("Job succeeded"); - } - - @Override - public void error(BigQueryError error, List executionErrors) { - System.out.println("Job completed with errors"); - } - }); + loadJob = loadJob.waitFor(); + if (loadJob.status().error() != null) { + System.out.println("Job completed with errors"); + } else { + System.out.println("Job succeeded"); + } } } diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/snippets/CreateAddressDiskAndInstance.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/snippets/CreateAddressDiskAndInstance.java index 4948f68b6db8..04d2116e0a75 100644 --- a/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/snippets/CreateAddressDiskAndInstance.java +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/snippets/CreateAddressDiskAndInstance.java @@ -33,12 +33,8 @@ import com.google.cloud.compute.NetworkInterface; import com.google.cloud.compute.NetworkInterface.AccessConfig; import com.google.cloud.compute.Operation; -import com.google.cloud.compute.Operation.OperationError; -import com.google.cloud.compute.Operation.OperationWarning; import com.google.cloud.compute.RegionAddressId; -import java.util.List; - /** * A snippet for Google Cloud Compute Engine showing how to create a disk and an address. The * snippet also shows how to create a virtual machine instance using the created disk and address. @@ -54,18 +50,13 @@ public static void main(String... args) throws InterruptedException { final RegionAddressId addressId = RegionAddressId.of("us-central1", "test-address"); Operation operation = compute.create(AddressInfo.of(addressId)); // Wait for operation to complete - operation.whenDone(new Operation.CompletionCallback() { - @Override - public void success(Operation operation) { - System.out.println("Address " + addressId + " was successfully created"); - } - - @Override - public void error(List errors, List warnings) { - // inspect errors - throw new RuntimeException("Address creation failed"); - } - }); + operation = operation.waitFor(); + if (operation.errors() == null) { + System.out.println("Address " + addressId + " was successfully created"); + } else { + // inspect operation.errors() + throw new RuntimeException("Address creation failed"); + } // Create a persistent disk ImageId imageId = ImageId.of("debian-cloud", "debian-8-jessie-v20160329"); @@ -74,18 +65,13 @@ public void error(List errors, List warnings) DiskInfo disk = DiskInfo.of(diskId, diskConfiguration); operation = compute.create(disk); // Wait for operation to complete - operation.whenDone(new Operation.CompletionCallback() { - @Override - public void success(Operation operation) { - System.out.println("Disk " + diskId + " was successfully created"); - } - - @Override - public void error(List errors, List warnings) { - // inspect errors - throw new RuntimeException("Disk creation failed"); - } - }); + operation = operation.waitFor(); + if (operation.errors() == null) { + System.out.println("Disk " + diskId + " was successfully created"); + } else { + // inspect operation.errors() + throw new RuntimeException("Disk creation failed"); + } // Create a virtual machine instance Address externalIp = compute.getAddress(addressId); @@ -102,17 +88,12 @@ public void error(List errors, List warnings) InstanceInfo.of(instanceId, machineTypeId, attachedDisk, networkInterface); operation = compute.create(instance); // Wait for operation to complete - operation.whenDone(new Operation.CompletionCallback() { - @Override - public void success(Operation operation) { - System.out.println("Instance " + instanceId + " was successfully created"); - } - - @Override - public void error(List errors, List warnings) { - // inspect errors - throw new RuntimeException("Instance creation failed"); - } - }); + operation = operation.waitFor(); + if (operation.errors() == null) { + System.out.println("Instance " + instanceId + " was successfully created"); + } else { + // inspect operation.errors() + throw new RuntimeException("Instance creation failed"); + } } } diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/snippets/CreateInstance.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/snippets/CreateInstance.java index 0e70aa2ea83f..c144edcd3042 100644 --- a/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/snippets/CreateInstance.java +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/snippets/CreateInstance.java @@ -27,10 +27,6 @@ import com.google.cloud.compute.NetworkId; import com.google.cloud.compute.NetworkInterface; import com.google.cloud.compute.Operation; -import com.google.cloud.compute.Operation.OperationError; -import com.google.cloud.compute.Operation.OperationWarning; - -import java.util.List; /** * A snippet for Google Cloud Compute Engine showing how to create a virtual machine instance. @@ -47,18 +43,10 @@ public static void main(String... args) throws InterruptedException { MachineTypeId machineTypeId = MachineTypeId.of("us-central1-a", "n1-standard-1"); Operation operation = compute.create(InstanceInfo.of(instanceId, machineTypeId, attachedDisk, networkInterface)); - operation.whenDone(new Operation.CompletionCallback() { - @Override - public void success(Operation operation) { - // use instance - Instance instance = compute.getInstance(instanceId); - } - - @Override - public void error(List errors, List warnings) { - // inspect errors - throw new RuntimeException("Instance creation failed"); - } - }); + operation = operation.waitFor(); + if (operation.errors() == null) { + // use instance + Instance instance = compute.getInstance(instanceId); + } } } diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/snippets/CreateSnapshot.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/snippets/CreateSnapshot.java index 1c364bf0c353..5f74f1a37fb7 100644 --- a/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/snippets/CreateSnapshot.java +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/snippets/CreateSnapshot.java @@ -21,12 +21,8 @@ import com.google.cloud.compute.Disk; import com.google.cloud.compute.DiskId; import com.google.cloud.compute.Operation; -import com.google.cloud.compute.Operation.OperationError; -import com.google.cloud.compute.Operation.OperationWarning; import com.google.cloud.compute.Snapshot; -import java.util.List; - /** * A snippet for Google Cloud Compute Engine showing how to create a snapshot of a disk if the disk * exists. @@ -40,19 +36,11 @@ public static void main(String... args) throws InterruptedException { if (disk != null) { final String snapshotName = "disk-name-snapshot"; Operation operation = disk.createSnapshot(snapshotName); - operation.whenDone(new Operation.CompletionCallback() { - @Override - public void success(Operation operation) { - // use snapshot - Snapshot snapshot = compute.getSnapshot(snapshotName); - } - - @Override - public void error(List errors, List warnings) { - // inspect errors - throw new RuntimeException("Snaphsot creation failed"); - } - }); + operation = operation.waitFor(); + if (operation.errors() == null) { + // use snapshot + Snapshot snapshot = compute.getSnapshot(snapshotName); + } } } } From 197ddeb4138123cf9a34cdd4196708c4a521f711 Mon Sep 17 00:00:00 2001 From: Marco Ziccardi Date: Mon, 23 May 2016 19:49:41 +0200 Subject: [PATCH 4/7] Remove no-longer-necessary final --- README.md | 4 ++-- gcloud-java-compute/README.md | 6 +++--- .../main/java/com/google/cloud/compute/package-info.java | 8 ++++---- .../compute/snippets/CreateAddressDiskAndInstance.java | 6 +++--- .../cloud/examples/compute/snippets/CreateInstance.java | 4 ++-- .../cloud/examples/compute/snippets/CreateSnapshot.java | 4 ++-- 6 files changed, 16 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index c0ae99ed71c2..f851112f415a 100644 --- a/README.md +++ b/README.md @@ -203,11 +203,11 @@ import com.google.cloud.compute.Disk; import com.google.cloud.compute.DiskId; import com.google.cloud.compute.Snapshot; -final Compute compute = ComputeOptions.defaultInstance().service(); +Compute compute = ComputeOptions.defaultInstance().service(); DiskId diskId = DiskId.of("us-central1-a", "disk-name"); Disk disk = compute.getDisk(diskId, Compute.DiskOption.fields()); if (disk != null) { - final String snapshotName = "disk-name-snapshot"; + String snapshotName = "disk-name-snapshot"; Operation operation = disk.createSnapshot(snapshotName); operation = operation.waitFor(); if (operation.errors() == null) { diff --git a/gcloud-java-compute/README.md b/gcloud-java-compute/README.md index e6661ffb909b..19c0d56b5b41 100644 --- a/gcloud-java-compute/README.md +++ b/gcloud-java-compute/README.md @@ -112,7 +112,7 @@ object that can be used to wait for operation completion and to check whether op succeeded: ```java -final RegionAddressId addressId = RegionAddressId.of("us-central1", "test-address"); +RegionAddressId addressId = RegionAddressId.of("us-central1", "test-address"); Operation operation = compute.create(AddressInfo.of(addressId)); // Wait for operation to complete operation = operation.waitFor(); @@ -143,7 +143,7 @@ import com.google.cloud.compute.ImageId; Then add the following code to create a disk and wait for disk creation to terminate. ```java -final ImageId imageId = ImageId.of("debian-cloud", "debian-8-jessie-v20160329"); +ImageId imageId = ImageId.of("debian-cloud", "debian-8-jessie-v20160329"); DiskId diskId = DiskId.of("us-central1-a", "test-disk"); ImageDiskConfiguration diskConfiguration = ImageDiskConfiguration.of(imageId); DiskInfo disk = DiskInfo.of(diskId, diskConfiguration); @@ -182,7 +182,7 @@ Then add the following code to create an instance and wait for instance creation ```java Address externalIp = compute.getAddress(addressId); -final InstanceId instanceId = InstanceId.of("us-central1-a", "test-instance"); +InstanceId instanceId = InstanceId.of("us-central1-a", "test-instance"); NetworkId networkId = NetworkId.of("default"); PersistentDiskConfiguration attachConfiguration = PersistentDiskConfiguration.builder(diskId).boot(true).build(); diff --git a/gcloud-java-compute/src/main/java/com/google/cloud/compute/package-info.java b/gcloud-java-compute/src/main/java/com/google/cloud/compute/package-info.java index da0a43ac43cf..aff2e4254b57 100644 --- a/gcloud-java-compute/src/main/java/com/google/cloud/compute/package-info.java +++ b/gcloud-java-compute/src/main/java/com/google/cloud/compute/package-info.java @@ -22,11 +22,11 @@ * * CreateSnapshot.java. *

 {@code
- * final Compute compute = ComputeOptions.defaultInstance().service();
+ * Compute compute = ComputeOptions.defaultInstance().service();
  * DiskId diskId = DiskId.of("us-central1-a", "disk-name");
  * Disk disk = compute.getDisk(diskId, Compute.DiskOption.fields());
  * if (disk != null) {
- *   final String snapshotName = "disk-name-snapshot";
+ *   String snapshotName = "disk-name-snapshot";
  *   Operation operation = disk.createSnapshot(snapshotName);
  *   operation = operation.waitFor();
  *   if (operation.errors() == null) {
@@ -39,12 +39,12 @@
  * 
  * CreateInstance.java.
  * 
 {@code
- * final Compute compute = ComputeOptions.defaultInstance().service();
+ * Compute compute = ComputeOptions.defaultInstance().service();
  * ImageId imageId = ImageId.of("debian-cloud", "debian-8-jessie-v20160329");
  * NetworkId networkId = NetworkId.of("default");
  * AttachedDisk attachedDisk = AttachedDisk.of(AttachedDisk.CreateDiskConfiguration.of(imageId));
  * NetworkInterface networkInterface = NetworkInterface.of(networkId);
- * final InstanceId instanceId = InstanceId.of("us-central1-a", "instance-name");
+ * InstanceId instanceId = InstanceId.of("us-central1-a", "instance-name");
  * MachineTypeId machineTypeId = MachineTypeId.of("us-central1-a", "n1-standard-1");
  * Operation operation =
  *     compute.create(InstanceInfo.of(instanceId, machineTypeId, attachedDisk, networkInterface));
diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/snippets/CreateAddressDiskAndInstance.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/snippets/CreateAddressDiskAndInstance.java
index 04d2116e0a75..8b87302398e9 100644
--- a/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/snippets/CreateAddressDiskAndInstance.java
+++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/snippets/CreateAddressDiskAndInstance.java
@@ -47,7 +47,7 @@ public static void main(String... args) throws InterruptedException {
     Compute compute = ComputeOptions.defaultInstance().service();
 
     // Create an external region address
-    final RegionAddressId addressId = RegionAddressId.of("us-central1", "test-address");
+    RegionAddressId addressId = RegionAddressId.of("us-central1", "test-address");
     Operation operation = compute.create(AddressInfo.of(addressId));
     // Wait for operation to complete
     operation = operation.waitFor();
@@ -60,7 +60,7 @@ public static void main(String... args) throws InterruptedException {
 
     // Create a persistent disk
     ImageId imageId = ImageId.of("debian-cloud", "debian-8-jessie-v20160329");
-    final DiskId diskId = DiskId.of("us-central1-a", "test-disk");
+    DiskId diskId = DiskId.of("us-central1-a", "test-disk");
     ImageDiskConfiguration diskConfiguration = ImageDiskConfiguration.of(imageId);
     DiskInfo disk = DiskInfo.of(diskId, diskConfiguration);
     operation = compute.create(disk);
@@ -75,7 +75,7 @@ public static void main(String... args) throws InterruptedException {
 
     // Create a virtual machine instance
     Address externalIp = compute.getAddress(addressId);
-    final InstanceId instanceId = InstanceId.of("us-central1-a", "test-instance");
+    InstanceId instanceId = InstanceId.of("us-central1-a", "test-instance");
     NetworkId networkId = NetworkId.of("default");
     PersistentDiskConfiguration attachConfiguration =
         PersistentDiskConfiguration.builder(diskId).boot(true).build();
diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/snippets/CreateInstance.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/snippets/CreateInstance.java
index c144edcd3042..3608c29e8ee9 100644
--- a/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/snippets/CreateInstance.java
+++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/snippets/CreateInstance.java
@@ -34,12 +34,12 @@
 public class CreateInstance {
 
   public static void main(String... args) throws InterruptedException {
-    final Compute compute = ComputeOptions.defaultInstance().service();
+    Compute compute = ComputeOptions.defaultInstance().service();
     ImageId imageId = ImageId.of("debian-cloud", "debian-8-jessie-v20160329");
     NetworkId networkId = NetworkId.of("default");
     AttachedDisk attachedDisk = AttachedDisk.of(AttachedDisk.CreateDiskConfiguration.of(imageId));
     NetworkInterface networkInterface = NetworkInterface.of(networkId);
-    final InstanceId instanceId = InstanceId.of("us-central1-a", "instance-name");
+    InstanceId instanceId = InstanceId.of("us-central1-a", "instance-name");
     MachineTypeId machineTypeId = MachineTypeId.of("us-central1-a", "n1-standard-1");
     Operation operation =
         compute.create(InstanceInfo.of(instanceId, machineTypeId, attachedDisk, networkInterface));
diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/snippets/CreateSnapshot.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/snippets/CreateSnapshot.java
index 5f74f1a37fb7..9e14904e86cc 100644
--- a/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/snippets/CreateSnapshot.java
+++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/snippets/CreateSnapshot.java
@@ -30,11 +30,11 @@
 public class CreateSnapshot {
 
   public static void main(String... args) throws InterruptedException {
-    final Compute compute = ComputeOptions.defaultInstance().service();
+    Compute compute = ComputeOptions.defaultInstance().service();
     DiskId diskId = DiskId.of("us-central1-a", "disk-name");
     Disk disk = compute.getDisk(diskId, Compute.DiskOption.fields());
     if (disk != null) {
-      final String snapshotName = "disk-name-snapshot";
+      String snapshotName = "disk-name-snapshot";
       Operation operation = disk.createSnapshot(snapshotName);
       operation = operation.waitFor();
       if (operation.errors() == null) {

From e5af98853207c4dae542c653533d1213e698afe4 Mon Sep 17 00:00:00 2001
From: Marco Ziccardi 
Date: Mon, 30 May 2016 08:23:53 +0200
Subject: [PATCH 5/7] Add vararg options (WaitForOption) to waitFor method

---
 .../java/com/google/cloud/bigquery/Job.java   |  53 +++--
 .../com/google/cloud/bigquery/JobTest.java    |  66 ++++--
 .../cloud/bigquery/it/ITBigQueryTest.java     |  11 +-
 .../com/google/cloud/compute/Operation.java   |  53 +++--
 .../google/cloud/compute/OperationTest.java   |  64 ++++--
 .../cloud/compute/it/ITComputeTest.java       |  41 ++--
 .../java/com/google/cloud/WaitForOption.java  | 204 ++++++++++++++++++
 .../com/google/cloud/SerializationTest.java   |   5 +-
 .../com/google/cloud/WaitForOptionTest.java   | 115 ++++++++++
 .../snippets/CreateTableAndLoadData.java      |   4 +-
 .../CreateAddressDiskAndInstance.java         |   4 +-
 .../compute/snippets/CreateInstance.java      |   4 +-
 .../compute/snippets/CreateSnapshot.java      |   4 +-
 13 files changed, 535 insertions(+), 93 deletions(-)
 create mode 100644 gcloud-java-core/src/main/java/com/google/cloud/WaitForOption.java
 create mode 100644 gcloud-java-core/src/test/java/com/google/cloud/WaitForOptionTest.java

diff --git a/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/Job.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/Job.java
index 0089bff21dd7..158ed25b518a 100644
--- a/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/Job.java
+++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/Job.java
@@ -16,12 +16,21 @@
 
 package com.google.cloud.bigquery;
 
+import static com.google.cloud.WaitForOption.Option.CHECKING_PERIOD;
+import static com.google.cloud.WaitForOption.Option.TIMEOUT;
+import static com.google.common.base.MoreObjects.firstNonNull;
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import com.google.cloud.Clock;
+import com.google.cloud.WaitForOption;
+import com.google.cloud.WaitForOption.CheckingPeriod;
+
 import java.io.IOException;
 import java.io.ObjectInputStream;
+import java.util.Map;
 import java.util.Objects;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 
 /**
  * A Google BigQuery Job.
@@ -145,9 +154,13 @@ public boolean isDone() {
   }
 
   /**
-   * Blocks until this job completes its execution, either failing or succeeding. The job status is
-   * checked every 500 milliseconds. This method returns current job's latest information. If the
-   * job no longer exists, this method returns {@code null}.
+   * Blocks until this job completes its execution, either failing or succeeding. This method
+   * returns current job's latest information. If the job no longer exists, this method returns
+   * {@code null}. By default, the job status is checked every 500 milliseconds, to configure this
+   * value use {@link WaitForOption#checkEvery(long, TimeUnit)}. Use
+   * {@link WaitForOption#timeout(long, TimeUnit)} to set the maximum time to wait.
+   *
+   * 

Example usage of {@code waitFor()}: *

 {@code
    * Job completedJob = job.waitFor();
    * if (completedJob == null) {
@@ -158,21 +171,10 @@ public boolean isDone() {
    *   // job completed successfully
    * }}
* - * @throws BigQueryException upon failure - * @throws InterruptedException if the current thread gets interrupted while waiting for the job - * to complete - */ - public Job waitFor() throws InterruptedException { - return waitFor(500, TimeUnit.MILLISECONDS); - } - - /** - * Blocks until this job completes its execution, either failing or succeeding. The - * {@code checkEvery} and {@code unit} parameters determine how often the job's status is checked. - * This method returns current job's latest information. If the job no longer exists, this method - * returns {@code null}. + *

Example usage of {@code waitFor()} with checking period and timeout: *

 {@code
-   * Job completedJob = job.waitFor(1, TimeUnit.SECONDS);
+   * Job completedJob = job.waitFor(WaitForOption.checkEvery(1, TimeUnit.SECONDS),
+   *     WaitForOption.timeout(60, TimeUnit.SECONDS));
    * if (completedJob == null) {
    *   // job no longer exists
    * } else if (completedJob.status().error() != null) {
@@ -181,13 +183,26 @@ public Job waitFor() throws InterruptedException {
    *   // job completed successfully
    * }}
* + * @param waitOptions options to configure checking period and timeout * @throws BigQueryException upon failure * @throws InterruptedException if the current thread gets interrupted while waiting for the job * to complete + * @throws TimeoutException if the timeout provided with + * {@link WaitForOption#timeout(long, TimeUnit)} is exceeded. If no such option is provided + * this exception is never thrown. */ - public Job waitFor(int checkEvery, TimeUnit unit) throws InterruptedException { + public Job waitFor(WaitForOption... waitOptions) throws InterruptedException, TimeoutException { + Map optionMap = WaitForOption.asMap(waitOptions); + CheckingPeriod checkingPeriod = firstNonNull(CHECKING_PERIOD.getCheckingPeriod(optionMap), + CheckingPeriod.defaultInstance()); + long timeout = firstNonNull(TIMEOUT.getLong(optionMap), -1L); + Clock clock = options.clock(); + long startTime = clock.millis(); while (!isDone()) { - unit.sleep(checkEvery); + if (timeout != -1 && (clock.millis() - startTime) >= timeout) { + throw new TimeoutException(); + } + checkingPeriod.sleep(); } return reload(); } diff --git a/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/JobTest.java b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/JobTest.java index 26178054d304..fb47b54428c0 100644 --- a/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/JobTest.java +++ b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/JobTest.java @@ -27,13 +27,18 @@ import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; +import com.google.cloud.Clock; +import com.google.cloud.WaitForOption; import com.google.cloud.bigquery.JobStatistics.CopyStatistics; import org.easymock.EasyMock; import org.junit.After; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; public class JobTest { @@ -69,6 +74,9 @@ public class JobTest { private Job expectedJob; private Job job; + @Rule + public final ExpectedException thrown = ExpectedException.none(); + private void initializeExpectedJob(int optionsCalls) { expect(serviceMockReturnsOptions.options()).andReturn(mockOptions).times(optionsCalls); replay(serviceMockReturnsOptions); @@ -181,35 +189,38 @@ public void testIsDone_NotExists() throws Exception { } @Test - public void testWaitFor() throws InterruptedException { + public void testWaitFor() throws InterruptedException, TimeoutException { initializeExpectedJob(2); BigQuery.JobOption[] expectedOptions = {BigQuery.JobOption.fields(BigQuery.JobField.STATUS)}; JobStatus status = createStrictMock(JobStatus.class); expect(status.state()).andReturn(JobStatus.State.DONE); expect(bigquery.options()).andReturn(mockOptions); + expect(mockOptions.clock()).andReturn(Clock.defaultClock()); Job completedJob = expectedJob.toBuilder().status(status).build(); expect(bigquery.getJob(JOB_INFO.jobId(), expectedOptions)).andReturn(completedJob); expect(bigquery.getJob(JOB_INFO.jobId())).andReturn(completedJob); - replay(status, bigquery); + replay(status, bigquery, mockOptions); initializeJob(); assertSame(completedJob, job.waitFor()); - verify(status); + verify(status, mockOptions); } @Test - public void testWaitFor_Null() throws InterruptedException { + public void testWaitFor_Null() throws InterruptedException, TimeoutException { initializeExpectedJob(1); BigQuery.JobOption[] expectedOptions = {BigQuery.JobOption.fields(BigQuery.JobField.STATUS)}; expect(bigquery.options()).andReturn(mockOptions); + expect(mockOptions.clock()).andReturn(Clock.defaultClock()); expect(bigquery.getJob(JOB_INFO.jobId(), expectedOptions)).andReturn(null); expect(bigquery.getJob(JOB_INFO.jobId())).andReturn(null); - replay(bigquery); + replay(bigquery, mockOptions); initializeJob(); assertNull(job.waitFor()); + verify(mockOptions); } @Test - public void testWaitForWithTimeUnit() throws InterruptedException { + public void testWaitForWithCheckingPeriod() throws InterruptedException, TimeoutException { initializeExpectedJob(3); BigQuery.JobOption[] expectedOptions = {BigQuery.JobOption.fields(BigQuery.JobField.STATUS)}; TimeUnit timeUnit = createStrictMock(TimeUnit.class); @@ -219,33 +230,62 @@ public void testWaitForWithTimeUnit() throws InterruptedException { expect(status.state()).andReturn(JobStatus.State.RUNNING); expect(status.state()).andReturn(JobStatus.State.DONE); expect(bigquery.options()).andReturn(mockOptions); + expect(mockOptions.clock()).andReturn(Clock.defaultClock()); Job runningJob = expectedJob.toBuilder().status(status).build(); Job completedJob = expectedJob.toBuilder().status(status).build(); expect(bigquery.getJob(JOB_INFO.jobId(), expectedOptions)).andReturn(runningJob); expect(bigquery.getJob(JOB_INFO.jobId(), expectedOptions)).andReturn(completedJob); expect(bigquery.getJob(JOB_INFO.jobId())).andReturn(completedJob); - replay(status, bigquery, timeUnit); + replay(status, bigquery, timeUnit, mockOptions); initializeJob(); - assertSame(completedJob, job.waitFor(42, timeUnit)); - verify(status, timeUnit); + assertSame(completedJob, job.waitFor(WaitForOption.checkEvery(42, timeUnit))); + verify(status, timeUnit, mockOptions); } @Test - public void testWaitForWithTimeUnit_Null() throws InterruptedException { + public void testWaitForWithCheckingPeriod_Null() throws InterruptedException, TimeoutException { initializeExpectedJob(2); BigQuery.JobOption[] expectedOptions = {BigQuery.JobOption.fields(BigQuery.JobField.STATUS)}; TimeUnit timeUnit = createStrictMock(TimeUnit.class); timeUnit.sleep(42); EasyMock.expectLastCall(); expect(bigquery.options()).andReturn(mockOptions); + expect(mockOptions.clock()).andReturn(Clock.defaultClock()); Job runningJob = expectedJob.toBuilder().status(new JobStatus(JobStatus.State.RUNNING)).build(); expect(bigquery.getJob(JOB_INFO.jobId(), expectedOptions)).andReturn(runningJob); expect(bigquery.getJob(JOB_INFO.jobId(), expectedOptions)).andReturn(null); expect(bigquery.getJob(JOB_INFO.jobId())).andReturn(null); - replay(bigquery, timeUnit); + replay(bigquery, timeUnit, mockOptions); + initializeJob(); + assertNull(job.waitFor(WaitForOption.checkEvery(42, timeUnit))); + verify(bigquery, timeUnit, mockOptions); + } + + @Test + public void testWaitForWithTimeout() throws InterruptedException, TimeoutException { + initializeExpectedJob(2); + BigQuery.JobOption[] expectedOptions = {BigQuery.JobOption.fields(BigQuery.JobField.STATUS)}; + TimeUnit timeUnit = createStrictMock(TimeUnit.class); + timeUnit.sleep(1); + EasyMock.expectLastCall(); + Clock clock = createStrictMock(Clock.class); + expect(clock.millis()).andReturn(0L); + expect(clock.millis()).andReturn(1L); + expect(clock.millis()).andReturn(3L); + JobStatus status = createStrictMock(JobStatus.class); + expect(status.state()).andReturn(JobStatus.State.RUNNING); + expect(status.state()).andReturn(JobStatus.State.RUNNING); + expect(bigquery.options()).andReturn(mockOptions); + expect(mockOptions.clock()).andReturn(clock); + Job runningJob = expectedJob.toBuilder().status(status).build(); + expect(bigquery.getJob(JOB_INFO.jobId(), expectedOptions)).andReturn(runningJob); + expect(bigquery.getJob(JOB_INFO.jobId(), expectedOptions)).andReturn(runningJob); + replay(status, bigquery, timeUnit, clock, mockOptions); initializeJob(); - assertNull(job.waitFor(42, timeUnit)); - verify(bigquery, timeUnit); + thrown.expect(TimeoutException.class); + job.waitFor(WaitForOption.checkEvery(1, timeUnit), + WaitForOption.timeout(3, TimeUnit.MILLISECONDS)); + verify(status, timeUnit, clock, mockOptions); } @Test diff --git a/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/it/ITBigQueryTest.java b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/it/ITBigQueryTest.java index 92151ea8bc4c..dde170f87859 100644 --- a/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/it/ITBigQueryTest.java +++ b/gcloud-java-bigquery/src/test/java/com/google/cloud/bigquery/it/ITBigQueryTest.java @@ -82,6 +82,7 @@ import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.logging.Level; import java.util.logging.Logger; @@ -169,7 +170,7 @@ public class ITBigQueryTest { public Timeout globalTimeout = Timeout.seconds(300); @BeforeClass - public static void beforeClass() throws InterruptedException { + public static void beforeClass() throws InterruptedException, TimeoutException { RemoteBigQueryHelper bigqueryHelper = RemoteBigQueryHelper.create(); RemoteStorageHelper storageHelper = RemoteStorageHelper.create(); bigquery = bigqueryHelper.options().service(); @@ -783,7 +784,7 @@ public void testCreateAndGetJobWithSelectedFields() { } @Test - public void testCopyJob() throws InterruptedException { + public void testCopyJob() throws InterruptedException, TimeoutException { String sourceTableName = "test_copy_job_source_table"; String destinationTableName = "test_copy_job_destination_table"; TableId sourceTable = TableId.of(DATASET, sourceTableName); @@ -808,7 +809,7 @@ public void testCopyJob() throws InterruptedException { } @Test - public void testQueryJob() throws InterruptedException { + public void testQueryJob() throws InterruptedException, TimeoutException { String tableName = "test_query_job_table"; String query = new StringBuilder() .append("SELECT TimestampField, StringField, BooleanField FROM ") @@ -851,7 +852,7 @@ public void testQueryJob() throws InterruptedException { } @Test - public void testExtractJob() throws InterruptedException { + public void testExtractJob() throws InterruptedException, TimeoutException { String tableName = "test_export_job_table"; TableId destinationTable = TableId.of(DATASET, tableName); LoadJobConfiguration configuration = @@ -875,7 +876,7 @@ public void testExtractJob() throws InterruptedException { } @Test - public void testCancelJob() throws InterruptedException { + public void testCancelJob() throws InterruptedException, TimeoutException { String destinationTableName = "test_cancel_query_job_table"; String query = "SELECT TimestampField, StringField, BooleanField FROM " + TABLE_ID.table(); TableId destinationTable = TableId.of(DATASET, destinationTableName); diff --git a/gcloud-java-compute/src/main/java/com/google/cloud/compute/Operation.java b/gcloud-java-compute/src/main/java/com/google/cloud/compute/Operation.java index cf8afb04cfbb..8351599dd1d7 100644 --- a/gcloud-java-compute/src/main/java/com/google/cloud/compute/Operation.java +++ b/gcloud-java-compute/src/main/java/com/google/cloud/compute/Operation.java @@ -16,8 +16,14 @@ package com.google.cloud.compute; +import static com.google.cloud.WaitForOption.Option.CHECKING_PERIOD; +import static com.google.cloud.WaitForOption.Option.TIMEOUT; +import static com.google.common.base.MoreObjects.firstNonNull; import static com.google.common.base.Preconditions.checkNotNull; +import com.google.cloud.Clock; +import com.google.cloud.WaitForOption; +import com.google.cloud.WaitForOption.CheckingPeriod; import com.google.cloud.compute.Compute.OperationOption; import com.google.common.base.Function; import com.google.common.base.MoreObjects; @@ -37,6 +43,7 @@ import java.util.Map; import java.util.Objects; import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; /** * Google Compute Engine operations. Operation identity can be obtained via {@link #operationId()}. @@ -660,9 +667,13 @@ public boolean isDone() { } /** - * Blocks until this operation completes its execution, either failing or succeeding. The - * operation status is checked every 500 milliseconds. This method returns current operation's - * latest information. If the operation no longer exists, this method returns {@code null}. + * Blocks until this operation completes its execution, either failing or succeeding. This method + * returns current operation's latest information. If the operation no longer exists, this method + * returns {@code null}. By default, the operation status is checked every 500 milliseconds, to + * configure this value use {@link WaitForOption#checkEvery(long, TimeUnit)}. Use + * {@link WaitForOption#timeout(long, TimeUnit)} to set the maximum time to wait. + * + *

Example usage of {@code waitFor()}: *

 {@code
    * Operation completedOperation = operation.waitFor();
    * if (completedOperation == null) {
@@ -673,21 +684,11 @@ public boolean isDone() {
    *   // operation completed successfully
    * }}
* - * @throws ComputeException upon failure - * @throws InterruptedException if the current thread gets interrupted while waiting for the - * operation to complete - */ - public Operation waitFor() throws InterruptedException { - return waitFor(500, TimeUnit.MILLISECONDS); - } - - /** - * Blocks until this operation completes its execution, either failing or succeeding. The - * {@code checkEvery} and {@code unit} parameters determine how often the operation status is - * checked. This method returns current operation's latest information. If the operation no longer - * exists, this method returns {@code null}. + *

Example usage of {@code waitFor()} with checking period and timeout: *

 {@code
-   * Operation completedOperation = operation.waitFor(1, TimeUnit.SECONDS);
+   * Operation completedOperation =
+   *     operation.waitFor(WaitForOption.checkEvery(1, TimeUnit.SECONDS),
+   *         WaitForOption.timeout(60, TimeUnit.SECONDS));
    * if (completedOperation == null) {
    *   // operation no longer exists
    * } else if (completedOperation.errors() != null) {
@@ -696,13 +697,27 @@ public Operation waitFor() throws InterruptedException {
    *   // operation completed successfully
    * }}
* + * @param waitOptions options to configure checking period and timeout * @throws ComputeException upon failure * @throws InterruptedException if the current thread gets interrupted while waiting for the * operation to complete + * @throws TimeoutException if the timeout provided with + * {@link WaitForOption#timeout(long, TimeUnit)} is exceeded. If no such option is provided + * this exception is never thrown. */ - public Operation waitFor(int checkEvery, TimeUnit unit) throws InterruptedException { + public Operation waitFor(WaitForOption... waitOptions) + throws InterruptedException, TimeoutException { + Map optionMap = WaitForOption.asMap(waitOptions); + CheckingPeriod checkingPeriod = firstNonNull(CHECKING_PERIOD.getCheckingPeriod(optionMap), + CheckingPeriod.defaultInstance()); + long timeout = firstNonNull(TIMEOUT.getLong(optionMap), -1L); + Clock clock = options.clock(); + long startTime = clock.millis(); while (!isDone()) { - unit.sleep(checkEvery); + if (timeout != -1 && (clock.millis() - startTime) >= timeout) { + throw new TimeoutException(); + } + checkingPeriod.sleep(); } return reload(); } diff --git a/gcloud-java-compute/src/test/java/com/google/cloud/compute/OperationTest.java b/gcloud-java-compute/src/test/java/com/google/cloud/compute/OperationTest.java index 63a0fc511d26..975610f93609 100644 --- a/gcloud-java-compute/src/test/java/com/google/cloud/compute/OperationTest.java +++ b/gcloud-java-compute/src/test/java/com/google/cloud/compute/OperationTest.java @@ -28,6 +28,8 @@ import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; +import com.google.cloud.Clock; +import com.google.cloud.WaitForOption; import com.google.cloud.compute.Operation.OperationError; import com.google.cloud.compute.Operation.OperationWarning; import com.google.common.collect.ImmutableList; @@ -35,10 +37,13 @@ import org.easymock.EasyMock; import org.junit.After; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import java.util.List; import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; public class OperationTest { @@ -76,6 +81,9 @@ public class OperationTest { private static final RegionOperationId REGION_OPERATION_ID = RegionOperationId.of("project", "region", "op"); + @Rule + public final ExpectedException thrown = ExpectedException.none(); + private final Compute serviceMockReturnsOptions = createStrictMock(Compute.class); private final ComputeOptions mockOptions = createMock(ComputeOptions.class); private Compute compute; @@ -357,35 +365,39 @@ public void testIsDone_NotExists() throws Exception { } @Test - public void testWaitFor() throws InterruptedException { + public void testWaitFor() throws InterruptedException, TimeoutException { initializeExpectedOperation(4); Compute.OperationOption[] expectedOptions = {Compute.OperationOption.fields(Compute.OperationField.STATUS)}; Operation successOperation = Operation.fromPb(serviceMockReturnsOptions, globalOperation.toPb().setError(null)); expect(compute.options()).andReturn(mockOptions); + expect(mockOptions.clock()).andReturn(Clock.defaultClock()); expect(compute.getOperation(GLOBAL_OPERATION_ID, expectedOptions)).andReturn(successOperation); expect(compute.getOperation(GLOBAL_OPERATION_ID)).andReturn(successOperation); - replay(compute); + replay(compute, mockOptions); initializeOperation(); assertSame(successOperation, operation.waitFor()); + verify(mockOptions); } @Test - public void testWaitFor_Null() throws InterruptedException { + public void testWaitFor_Null() throws InterruptedException, TimeoutException { initializeExpectedOperation(3); Compute.OperationOption[] expectedOptions = {Compute.OperationOption.fields(Compute.OperationField.STATUS)}; expect(compute.options()).andReturn(mockOptions); + expect(mockOptions.clock()).andReturn(Clock.defaultClock()); expect(compute.getOperation(GLOBAL_OPERATION_ID, expectedOptions)).andReturn(null); expect(compute.getOperation(GLOBAL_OPERATION_ID)).andReturn(null); - replay(compute); + replay(compute, mockOptions); initializeOperation(); assertNull(operation.waitFor()); + verify(mockOptions); } @Test - public void testWaitForWithTimeUnit() throws InterruptedException { + public void testWaitForCheckingPeriod() throws InterruptedException, TimeoutException { initializeExpectedOperation(5); Compute.OperationOption[] expectedOptions = {Compute.OperationOption.fields(Compute.OperationField.STATUS)}; @@ -397,18 +409,19 @@ public void testWaitForWithTimeUnit() throws InterruptedException { Operation completedOperation = Operation.fromPb(serviceMockReturnsOptions, globalOperation.toPb().setError(null)); expect(compute.options()).andReturn(mockOptions); + expect(mockOptions.clock()).andReturn(Clock.defaultClock()); expect(compute.getOperation(GLOBAL_OPERATION_ID, expectedOptions)).andReturn(runningOperation); expect(compute.getOperation(GLOBAL_OPERATION_ID, expectedOptions)) .andReturn(completedOperation); expect(compute.getOperation(GLOBAL_OPERATION_ID)).andReturn(completedOperation); - replay(compute, timeUnit); + replay(compute, timeUnit, mockOptions); initializeOperation(); - assertSame(completedOperation, operation.waitFor(42, timeUnit)); - verify(timeUnit); + assertSame(completedOperation, operation.waitFor(WaitForOption.checkEvery(42, timeUnit))); + verify(timeUnit, mockOptions); } @Test - public void testWaitForWithTimeUnit_Null() throws InterruptedException { + public void testWaitForCheckingPeriod_Null() throws InterruptedException, TimeoutException { initializeExpectedOperation(4); Compute.OperationOption[] expectedOptions = {Compute.OperationOption.fields(Compute.OperationField.STATUS)}; @@ -418,13 +431,40 @@ public void testWaitForWithTimeUnit_Null() throws InterruptedException { Operation runningOperation = Operation.fromPb(serviceMockReturnsOptions, globalOperation.toPb().setError(null).setStatus("RUNNING")); expect(compute.options()).andReturn(mockOptions); + expect(mockOptions.clock()).andReturn(Clock.defaultClock()); expect(compute.getOperation(GLOBAL_OPERATION_ID, expectedOptions)).andReturn(runningOperation); expect(compute.getOperation(GLOBAL_OPERATION_ID, expectedOptions)).andReturn(null); expect(compute.getOperation(GLOBAL_OPERATION_ID)).andReturn(null); - replay(compute, timeUnit); + replay(compute, timeUnit, mockOptions); + initializeOperation(); + assertNull(operation.waitFor(WaitForOption.checkEvery(42, timeUnit))); + verify(compute, timeUnit, mockOptions); + } + + @Test + public void testWaitForWithTimeout() throws InterruptedException, TimeoutException { + initializeExpectedOperation(4); + Compute.OperationOption[] expectedOptions = + {Compute.OperationOption.fields(Compute.OperationField.STATUS)}; + TimeUnit timeUnit = createStrictMock(TimeUnit.class); + timeUnit.sleep(1); + EasyMock.expectLastCall(); + Clock clock = createStrictMock(Clock.class); + expect(clock.millis()).andReturn(0L); + expect(clock.millis()).andReturn(1L); + expect(clock.millis()).andReturn(3L); + Operation runningOperation = Operation.fromPb(serviceMockReturnsOptions, + globalOperation.toPb().setError(null).setStatus("RUNNING")); + expect(compute.options()).andReturn(mockOptions); + expect(mockOptions.clock()).andReturn(clock); + expect(compute.getOperation(GLOBAL_OPERATION_ID, expectedOptions)).andReturn(runningOperation); + expect(compute.getOperation(GLOBAL_OPERATION_ID, expectedOptions)).andReturn(runningOperation); + replay(compute, timeUnit, clock, mockOptions); initializeOperation(); - assertNull(operation.waitFor(42, timeUnit)); - verify(compute, timeUnit); + thrown.expect(TimeoutException.class); + operation.waitFor(WaitForOption.checkEvery(1, timeUnit), + WaitForOption.timeout(3, TimeUnit.MILLISECONDS)); + verify(compute, timeUnit, clock, mockOptions); } @Test diff --git a/gcloud-java-compute/src/test/java/com/google/cloud/compute/it/ITComputeTest.java b/gcloud-java-compute/src/test/java/com/google/cloud/compute/it/ITComputeTest.java index ba9b58d775e1..5ee58c6bfda4 100644 --- a/gcloud-java-compute/src/test/java/com/google/cloud/compute/it/ITComputeTest.java +++ b/gcloud-java-compute/src/test/java/com/google/cloud/compute/it/ITComputeTest.java @@ -88,6 +88,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.TimeoutException; public class ITComputeTest { @@ -681,7 +682,7 @@ public void testListZoneOperationsWithFilter() { } @Test - public void testCreateGetAndDeleteRegionAddress() throws InterruptedException { + public void testCreateGetAndDeleteRegionAddress() throws InterruptedException, TimeoutException { String name = BASE_RESOURCE_NAME + "create-and-get-region-address"; AddressId addressId = RegionAddressId.of(REGION, name); AddressInfo addressInfo = AddressInfo.of(addressId); @@ -712,7 +713,7 @@ public void testCreateGetAndDeleteRegionAddress() throws InterruptedException { } @Test - public void testListRegionAddresses() throws InterruptedException { + public void testListRegionAddresses() throws InterruptedException, TimeoutException { String prefix = BASE_RESOURCE_NAME + "list-region-address"; String[] addressNames = {prefix + "1", prefix + "2"}; AddressId firstAddressId = RegionAddressId.of(REGION, addressNames[0]); @@ -764,7 +765,7 @@ public void testListRegionAddresses() throws InterruptedException { } @Test - public void testAggregatedListAddresses() throws InterruptedException { + public void testAggregatedListAddresses() throws InterruptedException, TimeoutException { String prefix = BASE_RESOURCE_NAME + "aggregated-list-address"; String[] addressNames = {prefix + "1", prefix + "2"}; AddressId firstAddressId = RegionAddressId.of(REGION, addressNames[0]); @@ -795,7 +796,7 @@ public void testAggregatedListAddresses() throws InterruptedException { } @Test - public void testCreateGetAndDeleteGlobalAddress() throws InterruptedException { + public void testCreateGetAndDeleteGlobalAddress() throws InterruptedException, TimeoutException { String name = BASE_RESOURCE_NAME + "create-and-get-global-address"; AddressId addressId = GlobalAddressId.of(name); AddressInfo addressInfo = AddressInfo.of(addressId); @@ -824,7 +825,7 @@ public void testCreateGetAndDeleteGlobalAddress() throws InterruptedException { } @Test - public void testListGlobalAddresses() throws InterruptedException { + public void testListGlobalAddresses() throws InterruptedException, TimeoutException { String prefix = BASE_RESOURCE_NAME + "list-global-address"; String[] addressNames = {prefix + "1", prefix + "2"}; AddressId firstAddressId = GlobalAddressId.of(addressNames[0]); @@ -874,7 +875,7 @@ public void testListGlobalAddresses() throws InterruptedException { } @Test - public void testCreateGetResizeAndDeleteStandardDisk() throws InterruptedException { + public void testCreateGetResizeAndDeleteStandardDisk() throws InterruptedException, TimeoutException { String name = BASE_RESOURCE_NAME + "create-and-get-standard-disk"; DiskId diskId = DiskId.of(ZONE, name); DiskInfo diskInfo = @@ -917,7 +918,7 @@ public void testCreateGetResizeAndDeleteStandardDisk() throws InterruptedExcepti } @Test - public void testCreateGetAndDeleteImageDisk() throws InterruptedException { + public void testCreateGetAndDeleteImageDisk() throws InterruptedException, TimeoutException { String name = BASE_RESOURCE_NAME + "create-and-get-image-disk"; DiskId diskId = DiskId.of(ZONE, name); DiskInfo diskInfo = DiskInfo.of(diskId, ImageDiskConfiguration.of(IMAGE_ID)); @@ -962,7 +963,7 @@ public void testCreateGetAndDeleteImageDisk() throws InterruptedException { } @Test - public void testCreateGetAndDeleteSnapshotAndSnapshotDisk() throws InterruptedException { + public void testCreateGetAndDeleteSnapshotAndSnapshotDisk() throws InterruptedException, TimeoutException { String diskName = BASE_RESOURCE_NAME + "create-and-get-snapshot-disk1"; String snapshotDiskName = BASE_RESOURCE_NAME + "create-and-get-snapshot-disk2"; DiskId diskId = DiskId.of(ZONE, diskName); @@ -1048,7 +1049,7 @@ public void testCreateGetAndDeleteSnapshotAndSnapshotDisk() throws InterruptedEx } @Test - public void testListDisksAndSnapshots() throws InterruptedException { + public void testListDisksAndSnapshots() throws InterruptedException, TimeoutException { String prefix = BASE_RESOURCE_NAME + "list-disks-and-snapshots-disk"; String[] diskNames = {prefix + "1", prefix + "2"}; DiskId firstDiskId = DiskId.of(ZONE, diskNames[0]); @@ -1159,7 +1160,7 @@ public void testListDisksAndSnapshots() throws InterruptedException { } @Test - public void testAggregatedListDisks() throws InterruptedException { + public void testAggregatedListDisks() throws InterruptedException, TimeoutException { String prefix = BASE_RESOURCE_NAME + "list-aggregated-disk"; String[] diskZones = {"us-central1-a", "us-east1-c"}; String[] diskNames = {prefix + "1", prefix + "2"}; @@ -1198,7 +1199,7 @@ public void testAggregatedListDisks() throws InterruptedException { } @Test - public void testCreateGetAndDeprecateImage() throws InterruptedException { + public void testCreateGetAndDeprecateImage() throws InterruptedException, TimeoutException { String diskName = BASE_RESOURCE_NAME + "create-and-get-image-disk"; String imageName = BASE_RESOURCE_NAME + "create-and-get-image"; DiskId diskId = DiskId.of(ZONE, diskName); @@ -1316,7 +1317,7 @@ public void testListImagesWithFilter() { } @Test - public void testCreateAndGetNetwork() throws InterruptedException { + public void testCreateAndGetNetwork() throws InterruptedException, TimeoutException { String name = BASE_RESOURCE_NAME + "create-and-get-network"; NetworkId networkId = NetworkId.of(name); NetworkInfo networkInfo = @@ -1347,7 +1348,7 @@ public void testCreateAndGetNetwork() throws InterruptedException { } @Test - public void testListNetworks() throws InterruptedException { + public void testListNetworks() throws InterruptedException, TimeoutException { String name = BASE_RESOURCE_NAME + "list-network"; NetworkId networkId = NetworkId.of(name); NetworkInfo networkInfo = @@ -1393,7 +1394,7 @@ public void testListNetworks() throws InterruptedException { } @Test - public void testCreateNetworkAndSubnetwork() throws InterruptedException { + public void testCreateNetworkAndSubnetwork() throws InterruptedException, TimeoutException { String networkName = BASE_RESOURCE_NAME + "create-subnetwork-network"; NetworkId networkId = NetworkId.of(networkName); NetworkInfo networkInfo = NetworkInfo.of(networkId, SubnetNetworkConfiguration.of(false)); @@ -1474,7 +1475,7 @@ public void testCreateNetworkAndSubnetwork() throws InterruptedException { } @Test - public void testAggregatedListSubnetworks() throws InterruptedException { + public void testAggregatedListSubnetworks() throws InterruptedException, TimeoutException { String networkName = BASE_RESOURCE_NAME + "list-subnetwork-network"; NetworkId networkId = NetworkId.of(networkName); NetworkInfo networkInfo = NetworkInfo.of(networkId, SubnetNetworkConfiguration.of(false)); @@ -1525,7 +1526,7 @@ public void testAggregatedListSubnetworks() throws InterruptedException { } @Test - public void testCreateGetAndDeleteInstance() throws InterruptedException { + public void testCreateGetAndDeleteInstance() throws InterruptedException, TimeoutException { String instanceName = BASE_RESOURCE_NAME + "create-and-get-instance"; String addressName = BASE_RESOURCE_NAME + "create-and-get-instance-address"; // Create an address to assign to the instance @@ -1608,7 +1609,7 @@ public void testCreateGetAndDeleteInstance() throws InterruptedException { } @Test - public void testStartStopAndResetInstance() throws InterruptedException { + public void testStartStopAndResetInstance() throws InterruptedException, TimeoutException { String instanceName = BASE_RESOURCE_NAME + "start-stop-reset-instance"; InstanceId instanceId = InstanceId.of(ZONE, instanceName); NetworkId networkId = NetworkId.of("default"); @@ -1644,7 +1645,7 @@ public void testStartStopAndResetInstance() throws InterruptedException { } @Test - public void testSetInstanceProperties() throws InterruptedException { + public void testSetInstanceProperties() throws InterruptedException, TimeoutException { String instanceName = BASE_RESOURCE_NAME + "set-properties-instance"; InstanceId instanceId = InstanceId.of(ZONE, instanceName); NetworkId networkId = NetworkId.of("default"); @@ -1689,7 +1690,7 @@ public void testSetInstanceProperties() throws InterruptedException { } @Test - public void testAttachAndDetachDisk() throws InterruptedException { + public void testAttachAndDetachDisk() throws InterruptedException, TimeoutException { String instanceName = BASE_RESOURCE_NAME + "attach-and-detach-disk-instance"; String diskName = BASE_RESOURCE_NAME + "attach-and-detach-disk"; InstanceId instanceId = InstanceId.of(ZONE, instanceName); @@ -1739,7 +1740,7 @@ public void testAttachAndDetachDisk() throws InterruptedException { } @Test - public void testAddAndRemoveAccessConfig() throws InterruptedException { + public void testAddAndRemoveAccessConfig() throws InterruptedException, TimeoutException { String instanceName = BASE_RESOURCE_NAME + "add-and-remove-access-instance"; String addressName = BASE_RESOURCE_NAME + "add-and-remove-access-address"; InstanceId instanceId = InstanceId.of(ZONE, instanceName); diff --git a/gcloud-java-core/src/main/java/com/google/cloud/WaitForOption.java b/gcloud-java-core/src/main/java/com/google/cloud/WaitForOption.java new file mode 100644 index 000000000000..85be6bdd0eb1 --- /dev/null +++ b/gcloud-java-core/src/main/java/com/google/cloud/WaitForOption.java @@ -0,0 +1,204 @@ +/* + * 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; + +import static com.google.cloud.WaitForOption.Option.CHECKING_PERIOD; +import static com.google.cloud.WaitForOption.Option.TIMEOUT; +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.common.base.MoreObjects; +import com.google.common.collect.Maps; + +import java.io.Serializable; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.TimeUnit; + +/** + * This class represents options for methods that wait for changes in the status of a resource. + */ +public final class WaitForOption implements Serializable { + + private static final long serialVersionUID = 8443451708032349243L; + + private final Option option; + private final Object value; + + /** + * This class holds the actual period and related time unit for the checking period. + */ + public static final class CheckingPeriod implements Serializable { + + private static final long serialVersionUID = -2481062893220539210L; + + private final long period; + private final TimeUnit unit; + + private CheckingPeriod(long period, TimeUnit unit) { + this.period = period; + this.unit = unit; + } + + /** + * Returns the checking period. + */ + public long period() { + return period; + } + + /** + * Returns the time unit for {@link #period()}. + */ + public TimeUnit unit() { + return unit; + } + + /** + * Blocks the current thread for the amount of time specified by this object. + * + * @throws InterruptedException if the current thread was interrupted + */ + public void sleep() throws InterruptedException { + unit.sleep(period); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj == null || !(obj instanceof CheckingPeriod)) { + return false; + } + CheckingPeriod other = (CheckingPeriod) obj; + return Objects.equals(period, other.period) && Objects.equals(unit, other.unit); + } + + @Override + public int hashCode() { + return Objects.hash(period, unit); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("period", period) + .add("unit", unit) + .toString(); + } + + /** + * Returns the default checking period (500 milliseconds). + */ + public static CheckingPeriod defaultInstance() { + return new CheckingPeriod(500, TimeUnit.MILLISECONDS); + } + } + + public enum Option { + CHECKING_PERIOD, + TIMEOUT; + + @SuppressWarnings("unchecked") + T get(Map options) { + return (T) options.get(this); + } + + public Long getLong(Map options) { + return get(options); + } + + public CheckingPeriod getCheckingPeriod(Map options) { + return get(options); + } + } + + private WaitForOption(Option option, Object value) { + this.option = option; + this.value = value; + } + + /** + * Returns the option's type. + */ + public Option option() { + return option; + } + + /** + * Returns the option's value. + */ + public Object value() { + return value; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj == null || !(obj instanceof WaitForOption)) { + return false; + } + WaitForOption other = (WaitForOption) obj; + return Objects.equals(option, other.option) && Objects.equals(value, other.value); + } + + @Override + public int hashCode() { + return Objects.hash(option, value); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("name", option.name().toLowerCase()) + .add("value", value) + .toString(); + } + + /** + * Returns an option to set how frequently the resource status should be checked. + * + * @param checkEvery the checking period + * @param unit the time unit of the checking period + */ + public static WaitForOption checkEvery(long checkEvery, TimeUnit unit) { + checkArgument(checkEvery >= 0, "checkEvery must be >= 0"); + return new WaitForOption(CHECKING_PERIOD, new CheckingPeriod(checkEvery, unit)); + } + + /** + * Returns an option to set the maximum time to wait. + * + * @param timeout the maximum time to wait, expressed in {@code unit} + * @param unit the time unit of the timeout + */ + public static WaitForOption timeout(long timeout, TimeUnit unit) { + checkArgument(timeout >= 0, "timeout must be >= 0"); + return new WaitForOption(TIMEOUT, TimeUnit.MILLISECONDS.convert(timeout, unit)); + } + + public static Map asMap(WaitForOption... options) { + Map optionMap = Maps.newEnumMap(Option.class); + for (WaitForOption waitOption : options) { + Object prev = optionMap.put(waitOption.option(), waitOption.value()); + checkArgument(prev == null, "Duplicate option %s", waitOption); + } + return optionMap; + } +} diff --git a/gcloud-java-core/src/test/java/com/google/cloud/SerializationTest.java b/gcloud-java-core/src/test/java/com/google/cloud/SerializationTest.java index 7fa778a524eb..53baa57b96ca 100644 --- a/gcloud-java-core/src/test/java/com/google/cloud/SerializationTest.java +++ b/gcloud-java-core/src/test/java/com/google/cloud/SerializationTest.java @@ -23,6 +23,7 @@ import java.io.IOException; import java.io.Serializable; import java.util.Date; +import java.util.concurrent.TimeUnit; public class SerializationTest extends BaseSerializationTest { @@ -36,6 +37,8 @@ public class SerializationTest extends BaseSerializationTest { new SigningException("message", BASE_SERVICE_EXCEPTION); private static final RetryParams RETRY_PARAMS = RetryParams.defaultInstance(); private static final SomeIamPolicy SOME_IAM_POLICY = new SomeIamPolicy.Builder().build(); + private static final WaitForOption CHECKING_PERIOD = + WaitForOption.checkEvery(42, TimeUnit.SECONDS); private static final String JSON_KEY = "{\n" + " \"private_key_id\": \"somekeyid\",\n" + " \"private_key\": \"-----BEGIN PRIVATE KEY-----\\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggS" @@ -88,7 +91,7 @@ public Builder toBuilder() { @Override protected Serializable[] serializableObjects() { return new Serializable[]{BASE_SERVICE_EXCEPTION, EXCEPTION_HANDLER, IDENTITY, PAGE, - RETRY_PARAMS, SOME_IAM_POLICY, SIGNING_EXCEPTION}; + RETRY_PARAMS, SOME_IAM_POLICY, SIGNING_EXCEPTION, CHECKING_PERIOD}; } @Override diff --git a/gcloud-java-core/src/test/java/com/google/cloud/WaitForOptionTest.java b/gcloud-java-core/src/test/java/com/google/cloud/WaitForOptionTest.java new file mode 100644 index 000000000000..c1003bbe467c --- /dev/null +++ b/gcloud-java-core/src/test/java/com/google/cloud/WaitForOptionTest.java @@ -0,0 +1,115 @@ +/* + * 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; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; + +import com.google.cloud.WaitForOption.CheckingPeriod; +import com.google.cloud.WaitForOption.Option; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import java.util.Map; +import java.util.concurrent.TimeUnit; + +public class WaitForOptionTest { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + private static final WaitForOption CHECKING_PERIOD_OPTION = + WaitForOption.checkEvery(42, TimeUnit.MILLISECONDS); + private static final WaitForOption TIMEOUT_OPTION = + WaitForOption.timeout(43, TimeUnit.MILLISECONDS); + + @Test + public void testCheckEvery() { + assertEquals(Option.CHECKING_PERIOD, CHECKING_PERIOD_OPTION.option()); + assertTrue(CHECKING_PERIOD_OPTION.value() instanceof CheckingPeriod); + CheckingPeriod checkingPeriod = (CheckingPeriod) CHECKING_PERIOD_OPTION.value(); + assertEquals(42, checkingPeriod.period()); + assertEquals(TimeUnit.MILLISECONDS, checkingPeriod.unit()); + } + + @Test + public void testCheckEvery_InvalidPeriod() { + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("checkEvery must be >= 0"); + WaitForOption.checkEvery(-1, TimeUnit.MILLISECONDS); + } + + @Test + public void testTimeout() { + assertEquals(Option.TIMEOUT, TIMEOUT_OPTION.option()); + assertEquals(43L, TIMEOUT_OPTION.value()); + } + + @Test + public void testTimeout_InvalidTimeout() { + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("timeout must be >= 0"); + WaitForOption.timeout(-1, TimeUnit.MILLISECONDS); + } + + @Test + public void testEqualsAndHashCode() { + assertEquals(CHECKING_PERIOD_OPTION, CHECKING_PERIOD_OPTION); + assertEquals(TIMEOUT_OPTION, TIMEOUT_OPTION); + assertEquals(CHECKING_PERIOD_OPTION.hashCode(), CHECKING_PERIOD_OPTION.hashCode()); + assertEquals(TIMEOUT_OPTION.hashCode(), TIMEOUT_OPTION.hashCode()); + WaitForOption checkingPeriodOption = WaitForOption.checkEvery(42, TimeUnit.MILLISECONDS); + assertEquals(CHECKING_PERIOD_OPTION, checkingPeriodOption); + assertEquals(CHECKING_PERIOD_OPTION.hashCode(), checkingPeriodOption.hashCode()); + WaitForOption timeoutOption = WaitForOption.timeout(43, TimeUnit.MILLISECONDS); + assertEquals(TIMEOUT_OPTION, timeoutOption); + assertEquals(TIMEOUT_OPTION.hashCode(), timeoutOption.hashCode()); + assertNotEquals(CHECKING_PERIOD_OPTION, TIMEOUT_OPTION); + assertNotEquals(CHECKING_PERIOD_OPTION.hashCode(), TIMEOUT_OPTION.hashCode()); + checkingPeriodOption = WaitForOption.checkEvery(43, TimeUnit.MILLISECONDS); + assertNotEquals(CHECKING_PERIOD_OPTION, checkingPeriodOption); + assertNotEquals(CHECKING_PERIOD_OPTION.hashCode(), checkingPeriodOption.hashCode()); + checkingPeriodOption = WaitForOption.checkEvery(42, TimeUnit.SECONDS); + assertNotEquals(CHECKING_PERIOD_OPTION, checkingPeriodOption); + assertNotEquals(CHECKING_PERIOD_OPTION.hashCode(), checkingPeriodOption.hashCode()); + timeoutOption = WaitForOption.timeout(42, TimeUnit.MILLISECONDS); + assertNotEquals(TIMEOUT_OPTION, timeoutOption); + assertNotEquals(TIMEOUT_OPTION.hashCode(), timeoutOption.hashCode()); + timeoutOption = WaitForOption.timeout(43, TimeUnit.SECONDS); + assertNotEquals(TIMEOUT_OPTION, timeoutOption); + assertNotEquals(TIMEOUT_OPTION.hashCode(), timeoutOption.hashCode()); + } + + @Test + public void testAsMap() { + Map optionMap = WaitForOption.asMap(CHECKING_PERIOD_OPTION, TIMEOUT_OPTION); + CheckingPeriod checkingPeriod = Option.CHECKING_PERIOD.getCheckingPeriod(optionMap); + assertEquals(42, checkingPeriod.period()); + assertEquals(TimeUnit.MILLISECONDS, checkingPeriod.unit()); + assertEquals(43, (long) Option.TIMEOUT.getLong(optionMap)); + } + + @Test + public void testAsMap_DuplicateOption() { + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage(String.format("Duplicate option %s", CHECKING_PERIOD_OPTION)); + WaitForOption.asMap(CHECKING_PERIOD_OPTION, CHECKING_PERIOD_OPTION); + } +} diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/bigquery/snippets/CreateTableAndLoadData.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/bigquery/snippets/CreateTableAndLoadData.java index 8dd76b0265f8..6772d79a73ca 100644 --- a/gcloud-java-examples/src/main/java/com/google/cloud/examples/bigquery/snippets/CreateTableAndLoadData.java +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/bigquery/snippets/CreateTableAndLoadData.java @@ -33,6 +33,8 @@ import com.google.cloud.bigquery.TableId; import com.google.cloud.bigquery.TableInfo; +import java.util.concurrent.TimeoutException; + /** * A snippet for Google Cloud BigQuery showing how to get a BigQuery table or create it if it does * not exist. The snippet also starts a BigQuery job to load data into the table from a Cloud @@ -40,7 +42,7 @@ */ public class CreateTableAndLoadData { - public static void main(String... args) throws InterruptedException { + public static void main(String... args) throws InterruptedException, TimeoutException { BigQuery bigquery = BigQueryOptions.defaultInstance().service(); TableId tableId = TableId.of("dataset", "table"); Table table = bigquery.getTable(tableId); diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/snippets/CreateAddressDiskAndInstance.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/snippets/CreateAddressDiskAndInstance.java index 8b87302398e9..0a63865904e0 100644 --- a/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/snippets/CreateAddressDiskAndInstance.java +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/snippets/CreateAddressDiskAndInstance.java @@ -35,13 +35,15 @@ import com.google.cloud.compute.Operation; import com.google.cloud.compute.RegionAddressId; +import java.util.concurrent.TimeoutException; + /** * A snippet for Google Cloud Compute Engine showing how to create a disk and an address. The * snippet also shows how to create a virtual machine instance using the created disk and address. */ public class CreateAddressDiskAndInstance { - public static void main(String... args) throws InterruptedException { + public static void main(String... args) throws InterruptedException, TimeoutException { // Create a service object // Credentials are inferred from the environment. Compute compute = ComputeOptions.defaultInstance().service(); diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/snippets/CreateInstance.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/snippets/CreateInstance.java index 3608c29e8ee9..66c10bace269 100644 --- a/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/snippets/CreateInstance.java +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/snippets/CreateInstance.java @@ -28,12 +28,14 @@ import com.google.cloud.compute.NetworkInterface; import com.google.cloud.compute.Operation; +import java.util.concurrent.TimeoutException; + /** * A snippet for Google Cloud Compute Engine showing how to create a virtual machine instance. */ public class CreateInstance { - public static void main(String... args) throws InterruptedException { + public static void main(String... args) throws InterruptedException, TimeoutException { Compute compute = ComputeOptions.defaultInstance().service(); ImageId imageId = ImageId.of("debian-cloud", "debian-8-jessie-v20160329"); NetworkId networkId = NetworkId.of("default"); diff --git a/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/snippets/CreateSnapshot.java b/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/snippets/CreateSnapshot.java index 9e14904e86cc..35d19e38e18e 100644 --- a/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/snippets/CreateSnapshot.java +++ b/gcloud-java-examples/src/main/java/com/google/cloud/examples/compute/snippets/CreateSnapshot.java @@ -23,13 +23,15 @@ import com.google.cloud.compute.Operation; import com.google.cloud.compute.Snapshot; +import java.util.concurrent.TimeoutException; + /** * A snippet for Google Cloud Compute Engine showing how to create a snapshot of a disk if the disk * exists. */ public class CreateSnapshot { - public static void main(String... args) throws InterruptedException { + public static void main(String... args) throws InterruptedException, TimeoutException { Compute compute = ComputeOptions.defaultInstance().service(); DiskId diskId = DiskId.of("us-central1-a", "disk-name"); Disk disk = compute.getDisk(diskId, Compute.DiskOption.fields()); From 71cd86896039815e45c8e68105a00778a19869a7 Mon Sep 17 00:00:00 2001 From: Marco Ziccardi Date: Wed, 1 Jun 2016 22:19:27 +0200 Subject: [PATCH 6/7] Add CheckingPeriod and Timeout subclasses to WaitForOption --- .../java/com/google/cloud/bigquery/Job.java | 14 +- .../com/google/cloud/compute/Operation.java | 12 +- .../java/com/google/cloud/WaitForOption.java | 160 +++++++++++------- .../com/google/cloud/WaitForOptionTest.java | 49 +++--- 4 files changed, 137 insertions(+), 98 deletions(-) diff --git a/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/Job.java b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/Job.java index 158ed25b518a..df0849d4b6f4 100644 --- a/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/Job.java +++ b/gcloud-java-bigquery/src/main/java/com/google/cloud/bigquery/Job.java @@ -16,18 +16,15 @@ package com.google.cloud.bigquery; -import static com.google.cloud.WaitForOption.Option.CHECKING_PERIOD; -import static com.google.cloud.WaitForOption.Option.TIMEOUT; -import static com.google.common.base.MoreObjects.firstNonNull; import static com.google.common.base.Preconditions.checkNotNull; import com.google.cloud.Clock; import com.google.cloud.WaitForOption; import com.google.cloud.WaitForOption.CheckingPeriod; +import com.google.cloud.WaitForOption.Timeout; import java.io.IOException; import java.io.ObjectInputStream; -import java.util.Map; import java.util.Objects; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -192,14 +189,13 @@ public boolean isDone() { * this exception is never thrown. */ public Job waitFor(WaitForOption... waitOptions) throws InterruptedException, TimeoutException { - Map optionMap = WaitForOption.asMap(waitOptions); - CheckingPeriod checkingPeriod = firstNonNull(CHECKING_PERIOD.getCheckingPeriod(optionMap), - CheckingPeriod.defaultInstance()); - long timeout = firstNonNull(TIMEOUT.getLong(optionMap), -1L); + Timeout timeout = Timeout.getOrDefault(waitOptions); + CheckingPeriod checkingPeriod = CheckingPeriod.getOrDefault(waitOptions); + long timeoutMillis = timeout.timeoutMillis(); Clock clock = options.clock(); long startTime = clock.millis(); while (!isDone()) { - if (timeout != -1 && (clock.millis() - startTime) >= timeout) { + if (timeoutMillis != -1 && (clock.millis() - startTime) >= timeoutMillis) { throw new TimeoutException(); } checkingPeriod.sleep(); diff --git a/gcloud-java-compute/src/main/java/com/google/cloud/compute/Operation.java b/gcloud-java-compute/src/main/java/com/google/cloud/compute/Operation.java index 8351599dd1d7..78752e9cdaeb 100644 --- a/gcloud-java-compute/src/main/java/com/google/cloud/compute/Operation.java +++ b/gcloud-java-compute/src/main/java/com/google/cloud/compute/Operation.java @@ -16,9 +16,6 @@ package com.google.cloud.compute; -import static com.google.cloud.WaitForOption.Option.CHECKING_PERIOD; -import static com.google.cloud.WaitForOption.Option.TIMEOUT; -import static com.google.common.base.MoreObjects.firstNonNull; import static com.google.common.base.Preconditions.checkNotNull; import com.google.cloud.Clock; @@ -707,14 +704,13 @@ public boolean isDone() { */ public Operation waitFor(WaitForOption... waitOptions) throws InterruptedException, TimeoutException { - Map optionMap = WaitForOption.asMap(waitOptions); - CheckingPeriod checkingPeriod = firstNonNull(CHECKING_PERIOD.getCheckingPeriod(optionMap), - CheckingPeriod.defaultInstance()); - long timeout = firstNonNull(TIMEOUT.getLong(optionMap), -1L); + WaitForOption.Timeout timeout = WaitForOption.Timeout.getOrDefault(waitOptions); + CheckingPeriod checkingPeriod = CheckingPeriod.getOrDefault(waitOptions); + long timeoutMillis = timeout.timeoutMillis(); Clock clock = options.clock(); long startTime = clock.millis(); while (!isDone()) { - if (timeout != -1 && (clock.millis() - startTime) >= timeout) { + if (timeoutMillis != -1 && (clock.millis() - startTime) >= timeoutMillis) { throw new TimeoutException(); } checkingPeriod.sleep(); diff --git a/gcloud-java-core/src/main/java/com/google/cloud/WaitForOption.java b/gcloud-java-core/src/main/java/com/google/cloud/WaitForOption.java index 85be6bdd0eb1..bcdbe9961478 100644 --- a/gcloud-java-core/src/main/java/com/google/cloud/WaitForOption.java +++ b/gcloud-java-core/src/main/java/com/google/cloud/WaitForOption.java @@ -16,39 +16,47 @@ package com.google.cloud; -import static com.google.cloud.WaitForOption.Option.CHECKING_PERIOD; -import static com.google.cloud.WaitForOption.Option.TIMEOUT; +import static com.google.common.base.MoreObjects.firstNonNull; import static com.google.common.base.Preconditions.checkArgument; import com.google.common.base.MoreObjects; -import com.google.common.collect.Maps; import java.io.Serializable; -import java.util.Map; import java.util.Objects; import java.util.concurrent.TimeUnit; /** * This class represents options for methods that wait for changes in the status of a resource. */ -public final class WaitForOption implements Serializable { +public abstract class WaitForOption implements Serializable { private static final long serialVersionUID = 8443451708032349243L; - private final Option option; - private final Object value; + private final OptionType optionType; + + enum OptionType { + CHECKING_PERIOD, + TIMEOUT + } + + private WaitForOption(OptionType optionType) { + this.optionType = optionType; + } /** - * This class holds the actual period and related time unit for the checking period. + * This class represents an option to set how frequently the resource status should be checked. + * Objects of this class keep the actual period and related time unit for the checking period. */ - public static final class CheckingPeriod implements Serializable { + public static final class CheckingPeriod extends WaitForOption { private static final long serialVersionUID = -2481062893220539210L; + private static final CheckingPeriod DEFAULT = new CheckingPeriod(500, TimeUnit.MILLISECONDS); private final long period; private final TimeUnit unit; private CheckingPeriod(long period, TimeUnit unit) { + super(OptionType.CHECKING_PERIOD); this.period = period; this.unit = unit; } @@ -85,12 +93,14 @@ public boolean equals(Object obj) { return false; } CheckingPeriod other = (CheckingPeriod) obj; - return Objects.equals(period, other.period) && Objects.equals(unit, other.unit); + return baseEquals(other) + && Objects.equals(period, other.period) + && Objects.equals(unit, other.unit); } @Override public int hashCode() { - return Objects.hash(period, unit); + return Objects.hash(baseHashCode(), period, unit); } @Override @@ -102,48 +112,77 @@ public String toString() { } /** - * Returns the default checking period (500 milliseconds). + * Returns the {@code CheckingPeriod} option specified in {@code options}. If no + * {@code CheckingPeriod} could be found among {@code options}, the default checking period (500 + * milliseconds) is used. */ - public static CheckingPeriod defaultInstance() { - return new CheckingPeriod(500, TimeUnit.MILLISECONDS); + public static CheckingPeriod getOrDefault(WaitForOption... options) { + return getOrDefaultInternal(OptionType.CHECKING_PERIOD, DEFAULT, options); } } - public enum Option { - CHECKING_PERIOD, - TIMEOUT; + /** + * This class represents an option to set the maximum time to wait for the resource's status to + * reach the desired state. + */ + public static final class Timeout extends WaitForOption { + + private static final long serialVersionUID = -7120401111985321932L; + private static final Timeout DEFAULT = new Timeout(-1); - @SuppressWarnings("unchecked") - T get(Map options) { - return (T) options.get(this); + private final long timeoutMillis; + + private Timeout(long timeoutMillis) { + super(OptionType.TIMEOUT); + this.timeoutMillis = timeoutMillis; } - public Long getLong(Map options) { - return get(options); + /** + * Returns the timeout in milliseconds. + */ + public long timeoutMillis() { + return timeoutMillis; } - public CheckingPeriod getCheckingPeriod(Map options) { - return get(options); + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj == null || !(obj instanceof Timeout)) { + return false; + } + Timeout other = (Timeout) obj; + return baseEquals(other) && Objects.equals(timeoutMillis, other.timeoutMillis); } - } - private WaitForOption(Option option, Object value) { - this.option = option; - this.value = value; + @Override + public int hashCode() { + return Objects.hash(baseHashCode(), timeoutMillis); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("timeoutMillis", timeoutMillis) + .toString(); + } + + /** + * Returns the {@code Timeout} option specified in {@code options}. If no {@code Timeout} could + * be found among {@code options}, no timeout will be used. + */ + public static Timeout getOrDefault(WaitForOption... options) { + return getOrDefaultInternal(OptionType.TIMEOUT, DEFAULT, options); + } } - /** - * Returns the option's type. - */ - public Option option() { - return option; + OptionType optionType() { + return optionType; } - /** - * Returns the option's value. - */ - public Object value() { - return value; + final boolean baseEquals(WaitForOption option) { + return Objects.equals(option.optionType, option.optionType); } @Override @@ -151,24 +190,32 @@ public boolean equals(Object obj) { if (obj == this) { return true; } - if (obj == null || !(obj instanceof WaitForOption)) { + if (obj == null || !(obj.getClass().equals(WaitForOption.class))) { return false; } - WaitForOption other = (WaitForOption) obj; - return Objects.equals(option, other.option) && Objects.equals(value, other.value); + return baseEquals((WaitForOption) obj); + } + + final int baseHashCode() { + return Objects.hash(optionType); } @Override public int hashCode() { - return Objects.hash(option, value); + return baseHashCode(); } - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("name", option.name().toLowerCase()) - .add("value", value) - .toString(); + @SuppressWarnings("unchecked") + private static T getOrDefaultInternal(OptionType optionType, + T defaultValue, WaitForOption... options) { + T foundOption = null; + for (WaitForOption option : options) { + if (option.optionType.equals(optionType)) { + checkArgument(foundOption == null, "Duplicate option %s", option); + foundOption = (T) option; + } + } + return firstNonNull(foundOption, defaultValue); } /** @@ -177,9 +224,9 @@ public String toString() { * @param checkEvery the checking period * @param unit the time unit of the checking period */ - public static WaitForOption checkEvery(long checkEvery, TimeUnit unit) { + public static CheckingPeriod checkEvery(long checkEvery, TimeUnit unit) { checkArgument(checkEvery >= 0, "checkEvery must be >= 0"); - return new WaitForOption(CHECKING_PERIOD, new CheckingPeriod(checkEvery, unit)); + return new CheckingPeriod(checkEvery, unit); } /** @@ -188,17 +235,8 @@ public static WaitForOption checkEvery(long checkEvery, TimeUnit unit) { * @param timeout the maximum time to wait, expressed in {@code unit} * @param unit the time unit of the timeout */ - public static WaitForOption timeout(long timeout, TimeUnit unit) { + public static Timeout timeout(long timeout, TimeUnit unit) { checkArgument(timeout >= 0, "timeout must be >= 0"); - return new WaitForOption(TIMEOUT, TimeUnit.MILLISECONDS.convert(timeout, unit)); - } - - public static Map asMap(WaitForOption... options) { - Map optionMap = Maps.newEnumMap(Option.class); - for (WaitForOption waitOption : options) { - Object prev = optionMap.put(waitOption.option(), waitOption.value()); - checkArgument(prev == null, "Duplicate option %s", waitOption); - } - return optionMap; + return new Timeout(unit.toMillis(timeout)); } } diff --git a/gcloud-java-core/src/test/java/com/google/cloud/WaitForOptionTest.java b/gcloud-java-core/src/test/java/com/google/cloud/WaitForOptionTest.java index c1003bbe467c..82996e1ca3f8 100644 --- a/gcloud-java-core/src/test/java/com/google/cloud/WaitForOptionTest.java +++ b/gcloud-java-core/src/test/java/com/google/cloud/WaitForOptionTest.java @@ -18,16 +18,15 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertTrue; import com.google.cloud.WaitForOption.CheckingPeriod; -import com.google.cloud.WaitForOption.Option; +import com.google.cloud.WaitForOption.OptionType; +import com.google.cloud.WaitForOption.Timeout; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; -import java.util.Map; import java.util.concurrent.TimeUnit; public class WaitForOptionTest { @@ -35,18 +34,15 @@ public class WaitForOptionTest { @Rule public ExpectedException thrown = ExpectedException.none(); - private static final WaitForOption CHECKING_PERIOD_OPTION = + private static final CheckingPeriod CHECKING_PERIOD_OPTION = WaitForOption.checkEvery(42, TimeUnit.MILLISECONDS); - private static final WaitForOption TIMEOUT_OPTION = - WaitForOption.timeout(43, TimeUnit.MILLISECONDS); + private static final Timeout TIMEOUT_OPTION = WaitForOption.timeout(43, TimeUnit.MILLISECONDS); @Test public void testCheckEvery() { - assertEquals(Option.CHECKING_PERIOD, CHECKING_PERIOD_OPTION.option()); - assertTrue(CHECKING_PERIOD_OPTION.value() instanceof CheckingPeriod); - CheckingPeriod checkingPeriod = (CheckingPeriod) CHECKING_PERIOD_OPTION.value(); - assertEquals(42, checkingPeriod.period()); - assertEquals(TimeUnit.MILLISECONDS, checkingPeriod.unit()); + assertEquals(OptionType.CHECKING_PERIOD, CHECKING_PERIOD_OPTION.optionType()); + assertEquals(42, CHECKING_PERIOD_OPTION.period()); + assertEquals(TimeUnit.MILLISECONDS, CHECKING_PERIOD_OPTION.unit()); } @Test @@ -58,8 +54,10 @@ public void testCheckEvery_InvalidPeriod() { @Test public void testTimeout() { - assertEquals(Option.TIMEOUT, TIMEOUT_OPTION.option()); - assertEquals(43L, TIMEOUT_OPTION.value()); + assertEquals(OptionType.TIMEOUT, TIMEOUT_OPTION.optionType()); + assertEquals(43, TIMEOUT_OPTION.timeoutMillis()); + Timeout timeoutOption = WaitForOption.timeout(43, TimeUnit.SECONDS); + assertEquals(43_000, timeoutOption.timeoutMillis()); } @Test @@ -98,18 +96,29 @@ public void testEqualsAndHashCode() { } @Test - public void testAsMap() { - Map optionMap = WaitForOption.asMap(CHECKING_PERIOD_OPTION, TIMEOUT_OPTION); - CheckingPeriod checkingPeriod = Option.CHECKING_PERIOD.getCheckingPeriod(optionMap); - assertEquals(42, checkingPeriod.period()); + public void testGetOrDefault() { + assertEquals(CHECKING_PERIOD_OPTION, + CheckingPeriod.getOrDefault(CHECKING_PERIOD_OPTION, TIMEOUT_OPTION)); + assertEquals(TIMEOUT_OPTION, + Timeout.getOrDefault(CHECKING_PERIOD_OPTION, TIMEOUT_OPTION)); + CheckingPeriod checkingPeriod = CheckingPeriod.getOrDefault(TIMEOUT_OPTION); + assertEquals(500, checkingPeriod.period()); assertEquals(TimeUnit.MILLISECONDS, checkingPeriod.unit()); - assertEquals(43, (long) Option.TIMEOUT.getLong(optionMap)); + Timeout timeout = Timeout.getOrDefault(CHECKING_PERIOD_OPTION); + assertEquals(-1, timeout.timeoutMillis()); } @Test - public void testAsMap_DuplicateOption() { + public void testCheckingPeriodGetOrDefault_DuplicateOption() { thrown.expect(IllegalArgumentException.class); thrown.expectMessage(String.format("Duplicate option %s", CHECKING_PERIOD_OPTION)); - WaitForOption.asMap(CHECKING_PERIOD_OPTION, CHECKING_PERIOD_OPTION); + CheckingPeriod.getOrDefault(CHECKING_PERIOD_OPTION, CHECKING_PERIOD_OPTION); + } + + @Test + public void testTimeoutGetOrDefault_DuplicateOption() { + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage(String.format("Duplicate option %s", TIMEOUT_OPTION)); + Timeout.getOrDefault(TIMEOUT_OPTION, TIMEOUT_OPTION); } } From 996bae5b4251c7f5794f6f1eb4735c8ebf83b9a8 Mon Sep 17 00:00:00 2001 From: Marco Ziccardi Date: Fri, 3 Jun 2016 08:19:33 +0200 Subject: [PATCH 7/7] Remove hashCode and equals from base class, support null default value --- .../java/com/google/cloud/WaitForOption.java | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/gcloud-java-core/src/main/java/com/google/cloud/WaitForOption.java b/gcloud-java-core/src/main/java/com/google/cloud/WaitForOption.java index bcdbe9961478..8af7a074ab4d 100644 --- a/gcloud-java-core/src/main/java/com/google/cloud/WaitForOption.java +++ b/gcloud-java-core/src/main/java/com/google/cloud/WaitForOption.java @@ -16,7 +16,6 @@ package com.google.cloud; -import static com.google.common.base.MoreObjects.firstNonNull; import static com.google.common.base.Preconditions.checkArgument; import com.google.common.base.MoreObjects; @@ -185,26 +184,10 @@ final boolean baseEquals(WaitForOption option) { return Objects.equals(option.optionType, option.optionType); } - @Override - public boolean equals(Object obj) { - if (obj == this) { - return true; - } - if (obj == null || !(obj.getClass().equals(WaitForOption.class))) { - return false; - } - return baseEquals((WaitForOption) obj); - } - final int baseHashCode() { return Objects.hash(optionType); } - @Override - public int hashCode() { - return baseHashCode(); - } - @SuppressWarnings("unchecked") private static T getOrDefaultInternal(OptionType optionType, T defaultValue, WaitForOption... options) { @@ -215,7 +198,7 @@ private static T getOrDefaultInternal(OptionType optio foundOption = (T) option; } } - return firstNonNull(foundOption, defaultValue); + return foundOption != null ? foundOption : defaultValue; } /**