diff --git a/README.md b/README.md
index 76dff042d27d..f851112f415a 100644
--- a/README.md
+++ b/README.md
@@ -173,9 +173,7 @@ 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);
-}
+loadJob = loadJob.waitFor();
if (loadJob.status().error() != null) {
System.out.println("Job completed with errors");
} else {
@@ -203,7 +201,6 @@ 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.Snapshot;
Compute compute = ComputeOptions.defaultInstance().service();
@@ -212,12 +209,10 @@ Disk disk = compute.getDisk(diskId, Compute.DiskOption.fields());
if (disk != null) {
String snapshotName = "disk-name-snapshot";
Operation operation = disk.createSnapshot(snapshotName);
- while (!operation.isDone()) {
- Thread.sleep(1000L);
- }
+ operation = operation.waitFor();
if (operation.errors() == null) {
// use snapshot
- Snapshot snapshot = compute.getSnapshot("disk-name-snapshot");
+ Snapshot snapshot = compute.getSnapshot(snapshotName);
}
}
```
@@ -234,8 +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.NetworkInterface;
-import com.google.cloud.compute.Operation;
Compute compute = ComputeOptions.defaultInstance().service();
ImageId imageId = ImageId.of("debian-cloud", "debian-8-jessie-v20160329");
@@ -246,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 bfcca5b5388a..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
@@ -18,9 +18,16 @@
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.Objects;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
/**
* A Google BigQuery Job.
@@ -143,6 +150,59 @@ public boolean isDone() {
return job == null || job.status().state() == JobStatus.State.DONE;
}
+ /**
+ * 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) {
+ * // job no longer exists
+ * } else if (completedJob.status().error() != null) {
+ * // job failed, handle error
+ * } else {
+ * // job completed successfully
+ * }}
+ *
+ * Example usage of {@code waitFor()} with checking period and timeout:
+ *
{@code
+ * 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) {
+ * // job failed, handle error
+ * } else {
+ * // 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(WaitForOption... waitOptions) throws InterruptedException, TimeoutException {
+ 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 (timeoutMillis != -1 && (clock.millis() - startTime) >= timeoutMillis) {
+ throw new TimeoutException();
+ }
+ checkingPeriod.sleep();
+ }
+ return reload();
+ }
+
/**
* Fetches current job's latest information. Returns {@code null} if the job does not exist.
*
@@ -151,7 +211,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..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,9 +33,7 @@
* }
* System.out.println("Loading data into table " + tableId);
* Job loadJob = table.load(FormatOptions.csv(), "gs://bucket/path");
- * while (!loadJob.isDone()) {
- * Thread.sleep(1000L);
- * }
+ * loadJob = loadJob.waitFor();
* if (loadJob.status().error() != null) {
* System.out.println("Job completed with errors");
* } else {
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..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,10 +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 {
@@ -66,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);
@@ -177,13 +188,113 @@ public void testIsDone_NotExists() throws Exception {
assertTrue(job.isDone());
}
+ @Test
+ 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, mockOptions);
+ initializeJob();
+ assertSame(completedJob, job.waitFor());
+ verify(status, mockOptions);
+ }
+
+ @Test
+ 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, mockOptions);
+ initializeJob();
+ assertNull(job.waitFor());
+ verify(mockOptions);
+ }
+
+ @Test
+ public void testWaitForWithCheckingPeriod() throws InterruptedException, TimeoutException {
+ initializeExpectedJob(3);
+ BigQuery.JobOption[] expectedOptions = {BigQuery.JobOption.fields(BigQuery.JobField.STATUS)};
+ 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(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, mockOptions);
+ initializeJob();
+ assertSame(completedJob, job.waitFor(WaitForOption.checkEvery(42, timeUnit)));
+ verify(status, timeUnit, mockOptions);
+ }
+
+ @Test
+ 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, 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();
+ thrown.expect(TimeoutException.class);
+ job.waitFor(WaitForOption.checkEvery(1, timeUnit),
+ WaitForOption.timeout(3, TimeUnit.MILLISECONDS));
+ verify(status, timeUnit, clock, mockOptions);
+ }
+
@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 +305,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 +317,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..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
@@ -71,7 +71,6 @@
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;
@@ -83,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;
@@ -170,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();
@@ -188,9 +188,7 @@ public static void beforeClass() throws InterruptedException {
.schema(TABLE_SCHEMA)
.build();
Job job = bigquery.create(JobInfo.of(configuration));
- while (!job.isDone()) {
- Thread.sleep(1000);
- }
+ job = job.waitFor();
assertNull(job.status().error());
}
@@ -786,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);
@@ -799,9 +797,7 @@ 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);
- }
+ remoteJob = remoteJob.waitFor();
assertNull(remoteJob.status().error());
Table remoteTable = bigquery.getTable(DATASET, destinationTableName);
assertNotNull(remoteTable);
@@ -813,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 ")
@@ -825,9 +821,7 @@ public void testQueryJob() throws InterruptedException {
.destinationTable(destinationTable)
.build();
Job remoteJob = bigquery.create(JobInfo.of(configuration));
- while (!remoteJob.isDone()) {
- Thread.sleep(1000);
- }
+ remoteJob = remoteJob.waitFor();
assertNull(remoteJob.status().error());
QueryResponse response = bigquery.getQueryResults(remoteJob.jobId());
@@ -858,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 =
@@ -866,9 +860,7 @@ public void testExtractJob() throws InterruptedException {
.schema(SIMPLE_SCHEMA)
.build();
Job remoteLoadJob = bigquery.create(JobInfo.of(configuration));
- while (!remoteLoadJob.isDone()) {
- Thread.sleep(1000);
- }
+ remoteLoadJob = remoteLoadJob.waitFor();
assertNull(remoteLoadJob.status().error());
ExtractJobConfiguration extractConfiguration =
@@ -876,9 +868,7 @@ public void testExtractJob() throws InterruptedException {
.printHeader(false)
.build();
Job remoteExtractJob = bigquery.create(JobInfo.of(extractConfiguration));
- while (!remoteExtractJob.isDone()) {
- Thread.sleep(1000);
- }
+ remoteExtractJob = remoteExtractJob.waitFor();
assertNull(remoteExtractJob.status().error());
assertEquals(CSV_CONTENT,
new String(storage.readAllBytes(BUCKET, EXTRACT_FILE), StandardCharsets.UTF_8));
@@ -886,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);
@@ -896,9 +886,7 @@ public void testCancelJob() throws InterruptedException {
.build();
Job remoteJob = bigquery.create(JobInfo.of(configuration));
assertTrue(remoteJob.cancel());
- while (!remoteJob.isDone()) {
- Thread.sleep(1000);
- }
+ remoteJob = remoteJob.waitFor();
assertNull(remoteJob.status().error());
}
diff --git a/gcloud-java-compute/README.md b/gcloud-java-compute/README.md
index 81d46fd1270d..19c0d56b5b41 100644
--- a/gcloud-java-compute/README.md
+++ b/gcloud-java-compute/README.md
@@ -114,10 +114,8 @@ succeeded:
```java
RegionAddressId addressId = RegionAddressId.of("us-central1", "test-address");
Operation operation = compute.create(AddressInfo.of(addressId));
-while (!operation.isDone()) {
- Thread.sleep(1000L);
-}
-operation = operation.reload();
+// Wait for operation to complete
+operation = operation.waitFor();
if (operation.errors() == null) {
System.out.println("Address " + addressId + " was successfully created");
} else {
@@ -150,10 +148,8 @@ 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();
+// Wait for operation to complete
+operation = operation.waitFor();
if (operation.errors() == null) {
System.out.println("Disk " + diskId + " was successfully created");
} else {
@@ -198,10 +194,8 @@ 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();
+// Wait for operation to complete
+operation = operation.waitFor();
if (operation.errors() == null) {
System.out.println("Instance " + instanceId + " was successfully created");
} else {
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..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
@@ -18,6 +18,9 @@
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;
@@ -36,13 +39,15 @@
import java.util.List;
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()}.
* {@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 {
@@ -635,7 +640,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 +657,67 @@ 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;
}
+ /**
+ * 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) {
+ * // operation no longer exists
+ * } else if (completedOperation.errors() != null) {
+ * // operation failed, handle error
+ * } else {
+ * // operation completed successfully
+ * }}
+ *
+ * Example usage of {@code waitFor()} with checking period and timeout:
+ *
{@code
+ * 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) {
+ * // operation failed, handle error
+ * } else {
+ * // 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(WaitForOption... waitOptions)
+ throws InterruptedException, TimeoutException {
+ 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 (timeoutMillis != -1 && (clock.millis() - startTime) >= timeoutMillis) {
+ throw new TimeoutException();
+ }
+ checkingPeriod.sleep();
+ }
+ return reload();
+ }
+
/**
* Fetches current operation's latest information. Returns {@code null} if the operation does not
* exist.
@@ -666,7 +726,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 +737,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..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
@@ -28,12 +28,10 @@
* if (disk != null) {
* String snapshotName = "disk-name-snapshot";
* Operation operation = disk.createSnapshot(snapshotName);
- * while (!operation.isDone()) {
- * Thread.sleep(1000L);
- * }
+ * operation = operation.waitFor();
* if (operation.errors() == null) {
* // use snapshot
- * Snapshot snapshot = compute.getSnapshot("disk-name-snapshot");
+ * Snapshot snapshot = compute.getSnapshot(snapshotName);
* }
* }}
* This second example shows how to create a virtual machine instance. Complete source code can
@@ -49,10 +47,8 @@
* 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);
- * }
+ * compute.create(InstanceInfo.of(instanceId, machineTypeId, attachedDisk, networkInterface));
+ * operation = operation.waitFor();
* if (operation.errors() == null) {
* // use instance
* Instance instance = compute.getInstance(instanceId);
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..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,24 +28,33 @@
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;
import com.google.common.collect.ImmutableMap;
+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 {
- 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 +67,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";
@@ -72,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;
@@ -352,6 +364,109 @@ public void testIsDone_NotExists() throws Exception {
verify(compute);
}
+ @Test
+ 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, mockOptions);
+ initializeOperation();
+ assertSame(successOperation, operation.waitFor());
+ verify(mockOptions);
+ }
+
+ @Test
+ 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, mockOptions);
+ initializeOperation();
+ assertNull(operation.waitFor());
+ verify(mockOptions);
+ }
+
+ @Test
+ public void testWaitForCheckingPeriod() throws InterruptedException, TimeoutException {
+ initializeExpectedOperation(5);
+ 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"));
+ 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, mockOptions);
+ initializeOperation();
+ assertSame(completedOperation, operation.waitFor(WaitForOption.checkEvery(42, timeUnit)));
+ verify(timeUnit, mockOptions);
+ }
+
+ @Test
+ public void testWaitForCheckingPeriod_Null() throws InterruptedException, TimeoutException {
+ 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(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, 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();
+ thrown.expect(TimeoutException.class);
+ operation.waitFor(WaitForOption.checkEvery(1, timeUnit),
+ WaitForOption.timeout(3, TimeUnit.MILLISECONDS));
+ verify(compute, timeUnit, clock, mockOptions);
+ }
+
@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..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,14 +682,12 @@ 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);
Operation operation = compute.create(addressInfo);
- while (!operation.isDone()) {
- Thread.sleep(1000L);
- }
+ operation.waitFor();
// test get
Address remoteAddress = compute.getAddress(addressId);
assertNotNull(remoteAddress);
@@ -709,26 +708,20 @@ public void testCreateGetAndDeleteRegionAddress() throws InterruptedException {
assertNull(remoteAddress.creationTimestamp());
assertNull(remoteAddress.generatedId());
operation = remoteAddress.delete();
- while (!operation.isDone()) {
- Thread.sleep(1000L);
- }
+ operation.waitFor();
assertNull(compute.getAddress(addressId));
}
@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]);
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.waitFor();
+ secondOperation.waitFor();
Set addressSet = ImmutableSet.copyOf(addressNames);
// test list
Compute.AddressFilter filter =
@@ -772,19 +765,15 @@ 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]);
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.waitFor();
+ secondOperation.waitFor();
Set addressSet = ImmutableSet.copyOf(addressNames);
Compute.AddressFilter filter =
Compute.AddressFilter.equals(Compute.AddressField.NAME, prefix + "\\d");
@@ -807,14 +796,12 @@ 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);
Operation operation = compute.create(addressInfo);
- while (!operation.isDone()) {
- Thread.sleep(1000L);
- }
+ operation.waitFor();
// test get
Address remoteAddress = compute.getAddress(addressId);
assertNotNull(remoteAddress);
@@ -833,26 +820,20 @@ public void testCreateGetAndDeleteGlobalAddress() throws InterruptedException {
assertNull(remoteAddress.creationTimestamp());
assertNull(remoteAddress.generatedId());
operation = remoteAddress.delete();
- while (!operation.isDone()) {
- Thread.sleep(1000L);
- }
+ operation.waitFor();
assertNull(compute.getAddress(addressId));
}
@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]);
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.waitFor();
+ secondOperation.waitFor();
Set addressSet = ImmutableSet.copyOf(addressNames);
// test list
Compute.AddressFilter filter =
@@ -894,15 +875,13 @@ 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 =
DiskInfo.of(diskId, StandardDiskConfiguration.of(DiskTypeId.of(ZONE, "pd-ssd"), 100L));
Operation operation = compute.create(diskInfo);
- while (!operation.isDone()) {
- Thread.sleep(1000L);
- }
+ operation.waitFor();
// test get
Disk remoteDisk = compute.getDisk(diskId);
assertNotNull(remoteDisk);
@@ -918,9 +897,7 @@ public void testCreateGetResizeAndDeleteStandardDisk() throws InterruptedExcepti
assertNull(remoteDisk.lastAttachTimestamp());
assertNull(remoteDisk.lastDetachTimestamp());
operation = remoteDisk.resize(200L);
- while (!operation.isDone()) {
- Thread.sleep(1000L);
- }
+ operation.waitFor();
// test resize and get with selected fields
remoteDisk = compute.getDisk(diskId, Compute.DiskOption.fields(Compute.DiskField.SIZE_GB));
assertNotNull(remoteDisk);
@@ -936,21 +913,17 @@ public void testCreateGetResizeAndDeleteStandardDisk() throws InterruptedExcepti
assertNull(remoteDisk.lastAttachTimestamp());
assertNull(remoteDisk.lastDetachTimestamp());
operation = remoteDisk.delete();
- while (!operation.isDone()) {
- Thread.sleep(1000L);
- }
+ operation.waitFor();
assertNull(compute.getDisk(diskId));
}
@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));
Operation operation = compute.create(diskInfo);
- while (!operation.isDone()) {
- Thread.sleep(1000L);
- }
+ operation.waitFor();
// test get
Disk remoteDisk = compute.getDisk(diskId);
assertNotNull(remoteDisk);
@@ -985,14 +958,12 @@ public void testCreateGetAndDeleteImageDisk() throws InterruptedException {
assertNull(remoteDisk.lastAttachTimestamp());
assertNull(remoteDisk.lastDetachTimestamp());
operation = remoteDisk.delete();
- while (!operation.isDone()) {
- Thread.sleep(1000L);
- }
+ operation.waitFor();
assertNull(compute.getDisk(diskId));
}
@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);
@@ -1001,14 +972,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.waitFor();
Disk remoteDisk = compute.getDisk(diskId);
operation = remoteDisk.createSnapshot(snapshotName);
- while (!operation.isDone()) {
- Thread.sleep(1000L);
- }
+ operation.waitFor();
// test get snapshot with selected fields
Snapshot snapshot = compute.getSnapshot(snapshotName,
Compute.SnapshotOption.fields(Compute.SnapshotField.CREATION_TIMESTAMP));
@@ -1038,9 +1005,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.waitFor();
// test get disk
remoteDisk = compute.getDisk(snapshotDiskId);
assertNotNull(remoteDisk);
@@ -1076,19 +1041,15 @@ public void testCreateGetAndDeleteSnapshotAndSnapshotDisk() throws InterruptedEx
assertNull(remoteDisk.lastAttachTimestamp());
assertNull(remoteDisk.lastDetachTimestamp());
operation = remoteDisk.delete();
- while (!operation.isDone()) {
- Thread.sleep(1000L);
- }
+operation.waitFor();
assertNull(compute.getDisk(snapshotDiskId));
operation = snapshot.delete();
- while (!operation.isDone()) {
- Thread.sleep(1000L);
- }
+operation.waitFor();
assertNull(compute.getSnapshot(snapshotName));
}
@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]);
@@ -1097,12 +1058,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.waitFor();
+ secondOperation.waitFor();
Set diskSet = ImmutableSet.copyOf(diskNames);
// test list disks
Compute.DiskFilter diskFilter =
@@ -1154,12 +1111,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.waitFor();
+ secondOperation.waitFor();
// test list snapshots
Compute.SnapshotFilter snapshotFilter =
Compute.SnapshotFilter.equals(Compute.SnapshotField.NAME, prefix + "\\d");
@@ -1207,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"};
@@ -1217,12 +1170,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.waitFor();
+ secondOperation.waitFor();
Set zoneSet = ImmutableSet.copyOf(diskZones);
Set diskSet = ImmutableSet.copyOf(diskNames);
Compute.DiskFilter diskFilter =
@@ -1250,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);
@@ -1258,15 +1207,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.waitFor();
Disk remoteDisk = compute.getDisk(diskId);
ImageInfo imageInfo = ImageInfo.of(imageId, DiskImageConfiguration.of(diskId));
operation = compute.create(imageInfo);
- while (!operation.isDone()) {
- Thread.sleep(1000L);
- }
+operation.waitFor();
// test get image with selected fields
Image image = compute.getImage(imageId,
Compute.ImageOption.fields(Compute.ImageField.CREATION_TIMESTAMP));
@@ -1302,16 +1247,12 @@ public void testCreateGetAndDeprecateImage() throws InterruptedException {
.deprecated(System.currentTimeMillis())
.build();
operation = image.deprecate(deprecationStatus);
- while (!operation.isDone()) {
- Thread.sleep(1000L);
- }
+operation.waitFor();
image = compute.getImage(imageId);
assertEquals(deprecationStatus, image.deprecationStatus());
remoteDisk.delete();
operation = image.delete();
- while (!operation.isDone()) {
- Thread.sleep(1000L);
- }
+operation.waitFor();
assertNull(compute.getImage(imageId));
}
@@ -1376,15 +1317,13 @@ 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 =
NetworkInfo.of(networkId, StandardNetworkConfiguration.of("192.168.0.0/16"));
Operation operation = compute.create(networkInfo);
- while (!operation.isDone()) {
- Thread.sleep(1000L);
- }
+operation.waitFor();
// test get network with selected fields
Network network = compute.getNetwork(networkId.network(),
Compute.NetworkOption.fields(Compute.NetworkField.CREATION_TIMESTAMP));
@@ -1404,22 +1343,18 @@ 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.waitFor();
assertNull(compute.getNetwork(name));
}
@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 =
NetworkInfo.of(networkId, StandardNetworkConfiguration.of("192.168.0.0/16"));
Operation operation = compute.create(networkInfo);
- while (!operation.isDone()) {
- Thread.sleep(1000L);
- }
+operation.waitFor();
// test list
Compute.NetworkFilter filter = Compute.NetworkFilter.equals(Compute.NetworkField.NAME, name);
Page networkPage = compute.listNetworks(Compute.NetworkListOption.filter(filter));
@@ -1454,21 +1389,17 @@ public void testListNetworks() throws InterruptedException {
}
assertEquals(1, count);
operation = compute.deleteNetwork(networkId);
- while (!operation.isDone()) {
- Thread.sleep(1000L);
- }
+operation.waitFor();
assertNull(compute.getNetwork(name));
}
@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));
Operation operation = compute.create(networkInfo);
- while (!operation.isDone()) {
- Thread.sleep(1000L);
- }
+operation.waitFor();
// test get network
Network network = compute.getNetwork(networkId.network());
assertEquals(networkId.network(), network.networkId().network());
@@ -1481,9 +1412,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.waitFor();
// test get subnetwork with selected fields
Subnetwork subnetwork = compute.getSubnetwork(subnetworkId,
Compute.SubnetworkOption.fields(Compute.SubnetworkField.CREATION_TIMESTAMP));
@@ -1538,26 +1467,20 @@ public void testCreateNetworkAndSubnetwork() throws InterruptedException {
}
assertEquals(1, count);
operation = subnetwork.delete();
- while (!operation.isDone()) {
- Thread.sleep(1000L);
- }
+operation.waitFor();
operation = compute.deleteNetwork(networkId);
- while (!operation.isDone()) {
- Thread.sleep(1000L);
- }
+operation.waitFor();
assertNull(compute.getSubnetwork(subnetworkId));
assertNull(compute.getNetwork(networkName));
}
@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));
Operation operation = compute.create(networkInfo);
- while (!operation.isDone()) {
- Thread.sleep(1000L);
- }
+operation.waitFor();
String prefix = BASE_RESOURCE_NAME + "list-subnetwork";
String[] regionNames = {"us-central1", "us-east1"};
String[] subnetworkNames = {prefix + "1", prefix + "2"};
@@ -1570,12 +1493,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.waitFor();
+ secondOperation.waitFor();
Set regionSet = ImmutableSet.copyOf(regionNames);
Set subnetworkSet = ImmutableSet.copyOf(subnetworkNames);
Set rangeSet = ImmutableSet.copyOf(ipRanges);
@@ -1599,30 +1518,22 @@ 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.waitFor();
+ secondOperation.waitFor();
operation = compute.deleteNetwork(networkId);
- while (!operation.isDone()) {
- Thread.sleep(1000L);
- }
+operation.waitFor();
assertNull(compute.getNetwork(networkName));
}
@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
AddressId addressId = RegionAddressId.of(REGION, addressName);
AddressInfo addressInfo = AddressInfo.of(addressId);
Operation operation = compute.create(addressInfo);
- while (!operation.isDone()) {
- Thread.sleep(1000L);
- }
+operation.waitFor();
Address address = compute.getAddress(addressId);
// Create an instance
InstanceId instanceId = InstanceId.of(ZONE, instanceName);
@@ -1640,9 +1551,7 @@ public void testCreateGetAndDeleteInstance() throws InterruptedException {
.networkInterfaces(networkInterface)
.build();
operation = compute.create(instanceInfo);
- while (!operation.isDone()) {
- Thread.sleep(1000L);
- }
+operation.waitFor();
// test get
Instance remoteInstance = compute.getInstance(instanceId);
assertEquals(instanceName, remoteInstance.instanceId().instance());
@@ -1694,15 +1603,13 @@ public void testCreateGetAndDeleteInstance() throws InterruptedException {
String newSerialPortOutput = remoteInstance.getSerialPortOutput(1);
assertTrue(newSerialPortOutput.contains(serialPortOutput));
operation = remoteInstance.delete();
- while (!operation.isDone()) {
- Thread.sleep(1000L);
- }
+operation.waitFor();
assertNull(compute.getInstance(instanceId));
address.delete();
}
@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");
@@ -1715,30 +1622,22 @@ public void testStartStopAndResetInstance() throws InterruptedException {
.networkInterfaces(networkInterface)
.build();
Operation operation = compute.create(instanceInfo);
- while (!operation.isDone()) {
- Thread.sleep(1000L);
- }
+operation.waitFor();
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.waitFor();
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.waitFor();
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.waitFor();
remoteInstance = compute.getInstance(instanceId,
Compute.InstanceOption.fields(Compute.InstanceField.STATUS));
assertEquals(InstanceInfo.Status.RUNNING, remoteInstance.status());
@@ -1746,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");
@@ -1759,51 +1658,39 @@ public void testSetInstanceProperties() throws InterruptedException {
.networkInterfaces(networkInterface)
.build();
Operation operation = compute.create(instanceInfo);
- while (!operation.isDone()) {
- Thread.sleep(1000L);
- }
+operation.waitFor();
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.waitFor();
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.waitFor();
remoteInstance = compute.getInstance(instanceId);
assertEquals(metadata, remoteInstance.metadata().values());
// test set machine type
operation = remoteInstance.stop();
- while (!operation.isDone()) {
- Thread.sleep(1000L);
- }
+operation.waitFor();
operation = remoteInstance.setMachineType(MachineTypeId.of(ZONE, "n1-standard-1"));
- while (!operation.isDone()) {
- Thread.sleep(1000L);
- }
+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);
- while (!operation.isDone()) {
- Thread.sleep(1000L);
- }
+operation.waitFor();
remoteInstance = compute.getInstance(instanceId);
assertEquals(options, remoteInstance.schedulingOptions());
remoteInstance.delete();
}
@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);
@@ -1820,19 +1707,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.waitFor();
+ diskOperation.waitFor();
Instance remoteInstance = compute.getInstance(instanceId);
// test attach disk
instanceOperation = remoteInstance.attachDisk("dev1",
AttachedDisk.PersistentDiskConfiguration.builder(diskId).build());
- while (!instanceOperation.isDone()) {
- Thread.sleep(1000L);
- }
+ instanceOperation.waitFor();
remoteInstance = compute.getInstance(instanceId);
Set deviceSet = ImmutableSet.of("dev0", "dev1");
assertEquals(2, remoteInstance.attachedDisks().size());
@@ -1841,9 +1722,7 @@ public void testAttachAndDetachDisk() throws InterruptedException {
}
// test set disk auto-delete
instanceOperation = remoteInstance.setDiskAutoDelete("dev1", true);
- while (!instanceOperation.isDone()) {
- Thread.sleep(1000L);
- }
+ instanceOperation.waitFor();
remoteInstance = compute.getInstance(instanceId);
assertEquals(2, remoteInstance.attachedDisks().size());
for (AttachedDisk remoteAttachedDisk : remoteInstance.attachedDisks()) {
@@ -1852,9 +1731,7 @@ public void testAttachAndDetachDisk() throws InterruptedException {
}
// test detach disk
instanceOperation = remoteInstance.detachDisk("dev1");
- while (!instanceOperation.isDone()) {
- Thread.sleep(1000L);
- }
+ instanceOperation.waitFor();
remoteInstance = compute.getInstance(instanceId);
assertEquals(1, remoteInstance.attachedDisks().size());
assertEquals("dev0", remoteInstance.attachedDisks().get(0).deviceName());
@@ -1863,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);
@@ -1880,15 +1757,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.waitFor();
+ instanceOperation.waitFor();
Address remoteAddress = compute.getAddress(addressId);
Instance remoteInstance = compute.getInstance(instanceId);
String networkInterfaceName = remoteInstance.networkInterfaces().get(0).name();
@@ -1898,9 +1768,7 @@ public void testAddAndRemoveAccessConfig() throws InterruptedException {
.name("NAT")
.build();
instanceOperation = remoteInstance.addAccessConfig(networkInterfaceName, accessConfig);
- while (!instanceOperation.isDone()) {
- Thread.sleep(1000L);
- }
+ instanceOperation.waitFor();
remoteInstance = compute.getInstance(instanceId);
List accessConfigurations =
remoteInstance.networkInterfaces().get(0).accessConfigurations();
@@ -1908,9 +1776,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.waitFor();
remoteInstance = compute.getInstance(instanceId);
assertTrue(remoteInstance.networkInterfaces().get(0).accessConfigurations().isEmpty());
remoteInstance.delete();
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..8af7a074ab4d
--- /dev/null
+++ b/gcloud-java-core/src/main/java/com/google/cloud/WaitForOption.java
@@ -0,0 +1,225 @@
+/*
+ * 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.common.base.Preconditions.checkArgument;
+
+import com.google.common.base.MoreObjects;
+
+import java.io.Serializable;
+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 abstract class WaitForOption implements Serializable {
+
+ private static final long serialVersionUID = 8443451708032349243L;
+
+ private final OptionType optionType;
+
+ enum OptionType {
+ CHECKING_PERIOD,
+ TIMEOUT
+ }
+
+ private WaitForOption(OptionType optionType) {
+ this.optionType = optionType;
+ }
+
+ /**
+ * 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 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;
+ }
+
+ /**
+ * 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 baseEquals(other)
+ && Objects.equals(period, other.period)
+ && Objects.equals(unit, other.unit);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(baseHashCode(), period, unit);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("period", period)
+ .add("unit", unit)
+ .toString();
+ }
+
+ /**
+ * 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 getOrDefault(WaitForOption... options) {
+ return getOrDefaultInternal(OptionType.CHECKING_PERIOD, DEFAULT, options);
+ }
+ }
+
+ /**
+ * 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);
+
+ private final long timeoutMillis;
+
+ private Timeout(long timeoutMillis) {
+ super(OptionType.TIMEOUT);
+ this.timeoutMillis = timeoutMillis;
+ }
+
+ /**
+ * Returns the timeout in milliseconds.
+ */
+ public long timeoutMillis() {
+ return timeoutMillis;
+ }
+
+ @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);
+ }
+
+ @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);
+ }
+ }
+
+ OptionType optionType() {
+ return optionType;
+ }
+
+ final boolean baseEquals(WaitForOption option) {
+ return Objects.equals(option.optionType, option.optionType);
+ }
+
+ final int baseHashCode() {
+ return Objects.hash(optionType);
+ }
+
+ @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 foundOption != null ? foundOption : defaultValue;
+ }
+
+ /**
+ * 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 CheckingPeriod checkEvery(long checkEvery, TimeUnit unit) {
+ checkArgument(checkEvery >= 0, "checkEvery must be >= 0");
+ return 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 Timeout timeout(long timeout, TimeUnit unit) {
+ checkArgument(timeout >= 0, "timeout must be >= 0");
+ return new Timeout(unit.toMillis(timeout));
+ }
+}
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..82996e1ca3f8
--- /dev/null
+++ b/gcloud-java-core/src/test/java/com/google/cloud/WaitForOptionTest.java
@@ -0,0 +1,124 @@
+/*
+ * 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 com.google.cloud.WaitForOption.CheckingPeriod;
+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.concurrent.TimeUnit;
+
+public class WaitForOptionTest {
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ private static final CheckingPeriod CHECKING_PERIOD_OPTION =
+ WaitForOption.checkEvery(42, TimeUnit.MILLISECONDS);
+ private static final Timeout TIMEOUT_OPTION = WaitForOption.timeout(43, TimeUnit.MILLISECONDS);
+
+ @Test
+ public void testCheckEvery() {
+ assertEquals(OptionType.CHECKING_PERIOD, CHECKING_PERIOD_OPTION.optionType());
+ assertEquals(42, CHECKING_PERIOD_OPTION.period());
+ assertEquals(TimeUnit.MILLISECONDS, CHECKING_PERIOD_OPTION.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(OptionType.TIMEOUT, TIMEOUT_OPTION.optionType());
+ assertEquals(43, TIMEOUT_OPTION.timeoutMillis());
+ Timeout timeoutOption = WaitForOption.timeout(43, TimeUnit.SECONDS);
+ assertEquals(43_000, timeoutOption.timeoutMillis());
+ }
+
+ @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 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());
+ Timeout timeout = Timeout.getOrDefault(CHECKING_PERIOD_OPTION);
+ assertEquals(-1, timeout.timeoutMillis());
+ }
+
+ @Test
+ public void testCheckingPeriodGetOrDefault_DuplicateOption() {
+ thrown.expect(IllegalArgumentException.class);
+ thrown.expectMessage(String.format("Duplicate option %s", 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);
+ }
+}
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..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
@@ -528,6 +528,7 @@ void run(BigQuery bigquery, JobInfo job) throws Exception {
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 {
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..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);
@@ -52,9 +54,7 @@ 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);
- }
+ loadJob = loadJob.waitFor();
if (loadJob.status().error() != null) {
System.out.println("Job completed with errors");
} else {
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..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();
@@ -50,11 +52,7 @@ public static void main(String... args) throws InterruptedException {
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();
+ operation = operation.waitFor();
if (operation.errors() == null) {
System.out.println("Address " + addressId + " was successfully created");
} else {
@@ -69,11 +67,7 @@ public static void main(String... args) throws InterruptedException {
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();
+ operation = operation.waitFor();
if (operation.errors() == null) {
System.out.println("Disk " + diskId + " was successfully created");
} else {
@@ -96,11 +90,7 @@ 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();
+ operation = operation.waitFor();
if (operation.errors() == null) {
System.out.println("Instance " + instanceId + " was successfully created");
} else {
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..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");
@@ -43,9 +45,7 @@ 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));
- 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-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..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,25 +23,25 @@
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());
if (disk != null) {
String snapshotName = "disk-name-snapshot";
Operation operation = disk.createSnapshot(snapshotName);
- while (!operation.isDone()) {
- Thread.sleep(1000L);
- }
+ operation = operation.waitFor();
if (operation.errors() == null) {
// use snapshot
- Snapshot snapshot = compute.getSnapshot("disk-name-snapshot");
+ Snapshot snapshot = compute.getSnapshot(snapshotName);
}
}
}