diff --git a/README.md b/README.md index 1b9867fd198f..5ed44e91adfd 100644 --- a/README.md +++ b/README.md @@ -20,14 +20,22 @@ This client supports the following Google Cloud Platform services: Quickstart ---------- -Add this to your pom.xml file +If you are using Maven, add this to your pom.xml file ```xml com.google.gcloud gcloud-java - 0.0.10 + 0.0.11 ``` +If you are using Gradle, add this to your dependencies +```Groovy +compile 'com.google.gcloud:gcloud-java:jar:0.0.11' +``` +If you are using SBT, add this to your dependencies +```Scala +libraryDependencies += "com.google.gcloud" % "gcloud-java" % "0.0.11" +``` Example Applications -------------------- @@ -37,6 +45,33 @@ Example Applications - [`StorageExample`](https://github.com/GoogleCloudPlatform/gcloud-java/blob/master/gcloud-java-examples/src/main/java/com/google/gcloud/examples/StorageExample.java) - A simple command line interface providing some of Cloud Storage's functionality - Read more about using this application on the [`gcloud-java-examples` docs page](http://googlecloudplatform.github.io/gcloud-java/apidocs/?com/google/gcloud/examples/StorageExample.html). +Specifying a Project ID +----------------------- + +Most `gcloud-java` libraries require a project ID. There are multiple ways to specify this project ID. + +1. When using `gcloud-java` libraries from within Compute/App Engine, there's no need to specify a project ID. It is automatically inferred from the production environment. +2. When using `gcloud-java` elsewhere, you can do one of the following: + * Supply the project ID when building the service options. For example, to use Datastore from a project with ID "PROJECT_ID", you can write: + + ```java + Datastore datastore = DatastoreOptions.builder().projectId("PROJECT_ID").build().service(); + ``` + * Specify the environment variable `GCLOUD_PROJECT` to be your desired project ID. + * Set the project ID using the [Google Cloud SDK](https://cloud.google.com/sdk/?hl=en). To use the SDK, [download the SDK](https://cloud.google.com/sdk/?hl=en) if you haven't already, and set the project ID from the command line. For example: + + ``` + gcloud config set project PROJECT_ID + ``` + +`gcloud-java` determines the project ID from the following sources in the listed order, stopping once it finds a value: + +1. Project ID supplied when building the service options +2. Project ID specified by the environment variable `GCLOUD_PROJECT` +3. App Engine project ID +4. Compute Engine project ID +5. Google Cloud SDK project ID + Authentication -------------- @@ -44,8 +79,27 @@ There are multiple ways to authenticate to use Google Cloud services. 1. When using `gcloud-java` libraries from within Compute/App Engine, no additional authentication steps are necessary. 2. When using `gcloud-java` libraries elsewhere, there are two options: - * [Generate a JSON service account key](https://cloud.google.com/storage/docs/authentication?hl=en#service_accounts). Supply a path to the downloaded JSON credentials file when building the options supplied to datastore/storage constructor. - * If running locally for development/testing, you can use use [Google Cloud SDK](https://cloud.google.com/sdk/?hl=en). To use the SDK authentication, [download the SDK](https://cloud.google.com/sdk/?hl=en) if you haven't already. Then login using the SDK (`gcloud auth login` in command line), and set your current project using `gcloud config set project PROJECT_ID`. + * [Generate a JSON service account key](https://cloud.google.com/storage/docs/authentication?hl=en#service_accounts). After downloading that key, you must do one of the following: + * Define the environment variable GOOGLE_APPLICATION_CREDENTIALS to be the location of the key. For example: + ```bash + export GOOGLE_APPLICATION_CREDENTIALS=/path/to/my/key.json + ``` + * Supply the JSON credentials file when building the service options. For example, this Storage object has the necessary permissions to interact with your Google Cloud Storage data: + ```java + Storage storage = StorageOptions.builder() + .authCredentials(AuthCredentials.createForJson(new FileInputStream("/path/to/my/key.json")) + .build() + .service(); + ``` + * If running locally for development/testing, you can use use Google Cloud SDK. Download the SDK if you haven't already, then login using the SDK (`gcloud auth login` in command line). Be sure to set your project ID as described above. + +`gcloud-java` looks for credentials in the following order, stopping once it finds credentials: + +1. Credentials supplied when building the service options +2. App Engine credentials +3. Key file pointed to by the GOOGLE_APPLICATION_CREDENTIALS environment variable +4. Google Cloud SDK credentials +5. Compute Engine credentials Google Cloud Datastore ---------------------- @@ -67,7 +121,7 @@ import com.google.gcloud.datastore.Entity; import com.google.gcloud.datastore.Key; import com.google.gcloud.datastore.KeyFactory; -Datastore datastore = DatastoreOptions.getDefaultInstance().service(); +Datastore datastore = DatastoreOptions.defaultInstance().service(); KeyFactory keyFactory = datastore.newKeyFactory().kind(KIND); Key key = keyFactory.newKey(keyName); Entity entity = datastore.get(key); diff --git a/gcloud-java-core/README.md b/gcloud-java-core/README.md index 2a3be300f4ac..f84fb33993e7 100644 --- a/gcloud-java-core/README.md +++ b/gcloud-java-core/README.md @@ -12,14 +12,22 @@ This module provides common functionality required by service-specific modules o Quickstart ---------- -Add this to your pom.xml file +If you are using Maven, add this to your pom.xml file ```xml com.google.gcloud gcloud-java-core - 0.0.10 + 0.0.11 ``` +If you are using Gradle, add this to your dependencies +```Groovy +compile 'com.google.gcloud:gcloud-java-core:jar:0.0.11' +``` +If you are using SBT, add this to your dependencies +```Scala +libraryDependencies += "com.google.gcloud" % "gcloud-java-core" % "0.0.11" +``` Java Versions ------------- diff --git a/gcloud-java-core/pom.xml b/gcloud-java-core/pom.xml index e849594226db..e13933bd2beb 100644 --- a/gcloud-java-core/pom.xml +++ b/gcloud-java-core/pom.xml @@ -11,7 +11,7 @@ com.google.gcloud gcloud-java-pom - 0.0.11-SNAPSHOT + 0.0.12-SNAPSHOT gcloud-java-core diff --git a/gcloud-java-core/src/main/java/com/google/gcloud/AuthCredentials.java b/gcloud-java-core/src/main/java/com/google/gcloud/AuthCredentials.java index 73c66279ea53..800fcf340689 100644 --- a/gcloud-java-core/src/main/java/com/google/gcloud/AuthCredentials.java +++ b/gcloud-java-core/src/main/java/com/google/gcloud/AuthCredentials.java @@ -252,7 +252,7 @@ public boolean equals(Object obj) { @Override protected HttpRequestInitializer httpRequestInitializer(HttpTransport transport, Set scopes) { - return new HttpCredentialsAdapter(googleCredentials); + return new HttpCredentialsAdapter(googleCredentials.createScoped(scopes)); } @Override diff --git a/gcloud-java-core/src/main/java/com/google/gcloud/BaseServiceException.java b/gcloud-java-core/src/main/java/com/google/gcloud/BaseServiceException.java new file mode 100644 index 000000000000..cd0933426756 --- /dev/null +++ b/gcloud-java-core/src/main/java/com/google/gcloud/BaseServiceException.java @@ -0,0 +1,54 @@ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.gcloud; + +/** + * Base class for all service exceptions. + */ +public class BaseServiceException extends RuntimeException { + + private static final long serialVersionUID = 5028833760039966178L; + + private final int code; + private final boolean retryable; + + public BaseServiceException(int code, String message, boolean retryable) { + super(message); + this.code = code; + this.retryable = retryable; + } + + public BaseServiceException(int code, String message, boolean retryable, Exception cause) { + super(message, cause); + this.code = code; + this.retryable = retryable; + } + + /** + * Returns the code associated with this exception. + */ + public int code() { + return code; + } + + /** + * Returns {@code true} when it is safe to retry the operation that caused this exception. + */ + public boolean retryable() { + return retryable; + } +} diff --git a/gcloud-java-core/src/main/java/com/google/gcloud/ExceptionHandler.java b/gcloud-java-core/src/main/java/com/google/gcloud/ExceptionHandler.java index a0fab3dca566..c1f068594443 100644 --- a/gcloud-java-core/src/main/java/com/google/gcloud/ExceptionHandler.java +++ b/gcloud-java-core/src/main/java/com/google/gcloud/ExceptionHandler.java @@ -231,11 +231,11 @@ void verifyCaller(Callable callable) { } } - public Set> getRetriableExceptions() { + public Set> retriableExceptions() { return retriableExceptions; } - public Set> getNonRetriableExceptions() { + public Set> nonRetriableExceptions() { return nonRetriableExceptions; } @@ -262,7 +262,7 @@ boolean shouldRetry(Exception ex) { /** * Returns an instance which retry any checked exception and abort on any runtime exception. */ - public static ExceptionHandler getDefaultInstance() { + public static ExceptionHandler defaultInstance() { return DEFAULT_INSTANCE; } diff --git a/gcloud-java-core/src/main/java/com/google/gcloud/Page.java b/gcloud-java-core/src/main/java/com/google/gcloud/Page.java new file mode 100644 index 000000000000..2819b56a17a0 --- /dev/null +++ b/gcloud-java-core/src/main/java/com/google/gcloud/Page.java @@ -0,0 +1,67 @@ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.gcloud; + +import java.util.Iterator; + +/** + * Interface for Google Cloud paginated results. + * + *

+ * Use {@code Page} to iterate through all values (also in next pages): + *

 {@code
+ * Page page = ...; // get a Page instance
+ * Iterator iterator = page.iterateAll();
+ * while (iterator.hasNext()) {
+ *   T value = iterator.next();
+ *   // do something with value
+ * }}
+ *

+ * Or handle pagination explicitly: + *

 {@code
+ * Page page = ...; // get a Page instance
+ * while (page != null) {
+ *   for (T value : page.values()) {
+ *     // do something with value
+ *   }
+ *   page = page.nextPage();
+ * }}
+ */ +public interface Page { + + /** + * Returns the values contained in this page. + */ + Iterable values(); + + /** + * Returns an iterator for all values, possibly also in the next pages. Once current page's values + * are traversed the iterator fetches next page, if any. + */ + Iterator iterateAll(); + + /** + * Returns the cursor for the nextPage or {@code null} if no more results. + */ + String nextPageCursor(); + + /** + * Returns the next page of results or {@code null} if no more result. + */ + Page nextPage(); + +} diff --git a/gcloud-java-core/src/main/java/com/google/gcloud/PageImpl.java b/gcloud-java-core/src/main/java/com/google/gcloud/PageImpl.java new file mode 100644 index 000000000000..5e83b53b33a9 --- /dev/null +++ b/gcloud-java-core/src/main/java/com/google/gcloud/PageImpl.java @@ -0,0 +1,111 @@ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.gcloud; + +import com.google.common.collect.AbstractIterator; + +import java.io.Serializable; +import java.util.Collections; +import java.util.Iterator; +import java.util.Objects; + +/** + * Base implementation for Google Cloud paginated results. + */ +public class PageImpl implements Page, Serializable { + + private static final long serialVersionUID = 3914827379823557934L; + + private final String cursor; + private final Iterable results; + private final NextPageFetcher pageFetcher; + + public interface NextPageFetcher extends Serializable { + Page nextPage(); + } + + static class PageIterator extends AbstractIterator { + + private Iterator currentPageIterator; + private Page currentPage; + + PageIterator(Page currentPage) { + this.currentPageIterator = currentPage.values().iterator(); + this.currentPage = currentPage; + } + + @Override + protected T computeNext() { + while (!currentPageIterator.hasNext()) { + currentPage = currentPage.nextPage(); + if (currentPage == null) { + return endOfData(); + } + currentPageIterator = currentPage.values().iterator(); + } + return currentPageIterator.next(); + } + } + + /** + * Creates a {@code PageImpl} object. In order for the object to be serializable the {@code + * results} parameter must be serializable. + */ + public PageImpl(NextPageFetcher pageFetcher, String cursor, Iterable results) { + this.pageFetcher = pageFetcher; + this.cursor = cursor; + this.results = results; + } + + @Override + public Iterable values() { + return results == null ? Collections.EMPTY_LIST : results; + } + + @Override + public Iterator iterateAll() { + return new PageIterator(this); + } + + @Override + public String nextPageCursor() { + return cursor; + } + + @Override + public Page nextPage() { + if (cursor == null || pageFetcher == null) { + return null; + } + return pageFetcher.nextPage(); + } + + @Override + public int hashCode() { + return Objects.hash(cursor, results); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof PageImpl)) { + return false; + } + PageImpl other = (PageImpl) obj; + return Objects.equals(cursor, other.cursor) + && Objects.equals(results, other.results); + } +} diff --git a/gcloud-java-core/src/main/java/com/google/gcloud/RetryHelper.java b/gcloud-java-core/src/main/java/com/google/gcloud/RetryHelper.java index 7b47209cd3ff..9b9c1f6a3124 100644 --- a/gcloud-java-core/src/main/java/com/google/gcloud/RetryHelper.java +++ b/gcloud-java-core/src/main/java/com/google/gcloud/RetryHelper.java @@ -194,9 +194,9 @@ private V doRetry() throws RetryHelperException { } exception = e; } - if (attemptNumber >= params.getRetryMaxAttempts() - || attemptNumber >= params.getRetryMinAttempts() - && stopwatch.elapsed(MILLISECONDS) >= params.getTotalRetryPeriodMillis()) { + if (attemptNumber >= params.retryMaxAttempts() + || attemptNumber >= params.retryMinAttempts() + && stopwatch.elapsed(MILLISECONDS) >= params.totalRetryPeriodMillis()) { throw new RetriesExhaustedException(this + ": Too many failures, giving up", exception); } long sleepDurationMillis = getSleepDuration(params, attemptNumber); @@ -215,9 +215,9 @@ private V doRetry() throws RetryHelperException { @VisibleForTesting static long getSleepDuration(RetryParams retryParams, int attemptsSoFar) { - long initialDelay = retryParams.getInitialRetryDelayMillis(); - double backoffFactor = retryParams.getRetryDelayBackoffFactor(); - long maxDelay = retryParams.getMaxRetryDelayMillis(); + long initialDelay = retryParams.initialRetryDelayMillis(); + double backoffFactor = retryParams.retryDelayBackoffFactor(); + long maxDelay = retryParams.maxRetryDelayMillis(); long retryDelay = getExponentialValue(initialDelay, backoffFactor, maxDelay, attemptsSoFar); return (long) ((random() / 2.0 + .75) * retryDelay); } @@ -228,8 +228,8 @@ private static long getExponentialValue(long initialDelay, double backoffFactor, } public static V runWithRetries(Callable callable) throws RetryHelperException { - return runWithRetries(callable, RetryParams.getDefaultInstance(), - ExceptionHandler.getDefaultInstance()); + return runWithRetries(callable, RetryParams.defaultInstance(), + ExceptionHandler.defaultInstance()); } public static V runWithRetries(Callable callable, RetryParams params, diff --git a/gcloud-java-core/src/main/java/com/google/gcloud/RetryParams.java b/gcloud-java-core/src/main/java/com/google/gcloud/RetryParams.java index 461dbac77ff2..ab3644c6d747 100644 --- a/gcloud-java-core/src/main/java/com/google/gcloud/RetryParams.java +++ b/gcloud-java-core/src/main/java/com/google/gcloud/RetryParams.java @@ -38,8 +38,8 @@ * {@code RetryParams}, first create a {@link RetryParams.Builder}. The builder is mutable and each * of the parameters can be set (any unset parameters will fallback to the defaults). The * {@code Builder} can be then used to create an immutable {@code RetryParams} object. For default - * {@code RetryParams} use {@link #getDefaultInstance}. Default settings are subject to change - * release to release. If you require specific settings, explicitly create an instance of + * {@code RetryParams} use {@link #defaultInstance}. Default settings are subject to change release + * to release. If you require specific settings, explicitly create an instance of * {@code RetryParams} with all the required settings. * * @see RetryHelper @@ -91,12 +91,12 @@ private Builder() { retryDelayBackoffFactor = DEFAULT_RETRY_DELAY_BACKOFF_FACTOR; totalRetryPeriodMillis = DEFAULT_TOTAL_RETRY_PERIOD_MILLIS; } else { - retryMinAttempts = retryParams.getRetryMinAttempts(); - retryMaxAttempts = retryParams.getRetryMaxAttempts(); - initialRetryDelayMillis = retryParams.getInitialRetryDelayMillis(); - maxRetryDelayMillis = retryParams.getMaxRetryDelayMillis(); - retryDelayBackoffFactor = retryParams.getRetryDelayBackoffFactor(); - totalRetryPeriodMillis = retryParams.getTotalRetryPeriodMillis(); + retryMinAttempts = retryParams.retryMinAttempts(); + retryMaxAttempts = retryParams.retryMaxAttempts(); + initialRetryDelayMillis = retryParams.initialRetryDelayMillis(); + maxRetryDelayMillis = retryParams.maxRetryDelayMillis(); + retryDelayBackoffFactor = retryParams.retryDelayBackoffFactor(); + totalRetryPeriodMillis = retryParams.totalRetryPeriodMillis(); } } @@ -201,7 +201,7 @@ private RetryParams(Builder builder) { /** * Returns an instance with the default parameters. */ - public static RetryParams getDefaultInstance() { + public static RetryParams defaultInstance() { return DEFAULT_INSTANCE; } @@ -216,14 +216,14 @@ public static RetryParams noRetries() { /** * Returns the retryMinAttempts. Default value is {@value #DEFAULT_RETRY_MIN_ATTEMPTS}. */ - public int getRetryMinAttempts() { + public int retryMinAttempts() { return retryMinAttempts; } /** * Returns the retryMaxAttempts. Default value is {@value #DEFAULT_RETRY_MAX_ATTEMPTS}. */ - public int getRetryMaxAttempts() { + public int retryMaxAttempts() { return retryMaxAttempts; } @@ -231,14 +231,14 @@ public int getRetryMaxAttempts() { * Returns the initialRetryDelayMillis. Default value is * {@value #DEFAULT_INITIAL_RETRY_DELAY_MILLIS}. */ - public long getInitialRetryDelayMillis() { + public long initialRetryDelayMillis() { return initialRetryDelayMillis; } /** * Returns the maxRetryDelayMillis. Default values is {@value #DEFAULT_MAX_RETRY_DELAY_MILLIS}. */ - public long getMaxRetryDelayMillis() { + public long maxRetryDelayMillis() { return maxRetryDelayMillis; } @@ -246,7 +246,7 @@ public long getMaxRetryDelayMillis() { * Returns the maxRetryDelayBackoffFactor. Default values is * {@value #DEFAULT_RETRY_DELAY_BACKOFF_FACTOR}. */ - public double getRetryDelayBackoffFactor() { + public double retryDelayBackoffFactor() { return retryDelayBackoffFactor; } @@ -254,7 +254,7 @@ public double getRetryDelayBackoffFactor() { * Returns the totalRetryPeriodMillis. Default value is * {@value #DEFAULT_TOTAL_RETRY_PERIOD_MILLIS}. */ - public long getTotalRetryPeriodMillis() { + public long totalRetryPeriodMillis() { return totalRetryPeriodMillis; } diff --git a/gcloud-java-core/src/main/java/com/google/gcloud/ServiceOptions.java b/gcloud-java-core/src/main/java/com/google/gcloud/ServiceOptions.java index 1be1f16115ad..0793470ade83 100644 --- a/gcloud-java-core/src/main/java/com/google/gcloud/ServiceOptions.java +++ b/gcloud-java-core/src/main/java/com/google/gcloud/ServiceOptions.java @@ -17,7 +17,7 @@ package com.google.gcloud; import static com.google.common.base.MoreObjects.firstNonNull; -import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkArgument; import static java.nio.charset.StandardCharsets.UTF_8; import com.google.api.client.extensions.appengine.http.UrlFetchTransport; @@ -30,6 +30,7 @@ import java.io.BufferedReader; import java.io.File; +import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; @@ -305,7 +306,13 @@ public B readTimeout(int readTimeout) { protected ServiceOptions(Class> serviceFactoryClass, Class> rpcFactoryClass, Builder builder) { - projectId = checkNotNull(builder.projectId != null ? builder.projectId : defaultProject()); + projectId = builder.projectId != null ? builder.projectId : defaultProject(); + if (projectIdRequired()) { + checkArgument( + projectId != null, + "A project ID is required for this service but could not be determined from the builder or " + + "the environment. Please set a project ID using the builder."); + } host = firstNonNull(builder.host, defaultHost()); httpTransportFactory = firstNonNull(builder.httpTransportFactory, getFromServiceLoader(HttpTransportFactory.class, DefaultHttpTransportFactory.INSTANCE)); @@ -324,6 +331,16 @@ protected ServiceOptions(Class> ser clock = firstNonNull(builder.clock, Clock.defaultClock()); } + /** + * Returns whether a service requires a project ID. This method may be overridden in + * service-specific Options objects. + * + * @return true if a project ID is required to use the service, false if not. + */ + protected boolean projectIdRequired() { + return true; + } + private static AuthCredentials defaultAuthCredentials() { // Consider App Engine. This will not be needed once issue #21 is fixed. if (appEngineAppId() != null) { @@ -387,8 +404,18 @@ protected static String googleCloudProjectId() { } else { configDir = new File(System.getProperty("user.home"), ".config/gcloud"); } - try (BufferedReader reader = - new BufferedReader(new FileReader(new File(configDir, "properties")))) { + FileReader fileReader; + try { + fileReader = new FileReader(new File(configDir, "configurations/config_default")); + } catch (FileNotFoundException newConfigFileNotFoundEx) { + try { + fileReader = new FileReader(new File(configDir, "properties")); + } catch (FileNotFoundException oldConfigFileNotFoundEx) { + // return null if we can't find config file + return null; + } + } + try (BufferedReader reader = new BufferedReader(fileReader)) { String line; String section = null; Pattern projectPattern = Pattern.compile("^project\\s*=\\s*(.*)$"); @@ -451,6 +478,8 @@ public ServiceRpcT rpc() { /** * Returns the project id. + * + * Return value can be null (for services that don't require a project id). */ public String projectId() { return projectId; diff --git a/gcloud-java-core/src/test/java/com/google/gcloud/BaseServiceExceptionTest.java b/gcloud-java-core/src/test/java/com/google/gcloud/BaseServiceExceptionTest.java new file mode 100644 index 000000000000..f30fd3abfb79 --- /dev/null +++ b/gcloud-java-core/src/test/java/com/google/gcloud/BaseServiceExceptionTest.java @@ -0,0 +1,45 @@ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.gcloud; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +/** + * Tests for {@link BaseServiceException}. + */ +public class BaseServiceExceptionTest { + + private final int code = 1; + private final String message = "some message"; + private final boolean retryable = true; + + @Test + public void testBaseServiceException() { + BaseServiceException serviceException = new BaseServiceException(code, message, retryable); + assertEquals(serviceException.code(), code); + assertEquals(serviceException.getMessage(), message); + assertEquals(serviceException.getCause(), null); + + Exception cause = new RuntimeException(); + serviceException = new BaseServiceException(code, message, retryable, cause); + assertEquals(serviceException.code(), code); + assertEquals(serviceException.getMessage(), message); + assertEquals(serviceException.getCause(), cause); + } +} diff --git a/gcloud-java-core/src/test/java/com/google/gcloud/ExceptionHandlerTest.java b/gcloud-java-core/src/test/java/com/google/gcloud/ExceptionHandlerTest.java index c182515dbb16..cedc995ddbd0 100644 --- a/gcloud-java-core/src/test/java/com/google/gcloud/ExceptionHandlerTest.java +++ b/gcloud-java-core/src/test/java/com/google/gcloud/ExceptionHandlerTest.java @@ -82,7 +82,7 @@ public Object call() throws Error { } // using default exception handler (retry upon any non-runtime exceptions) - ExceptionHandler handler = ExceptionHandler.getDefaultInstance(); + ExceptionHandler handler = ExceptionHandler.defaultInstance(); assertValidCallable(new A(), handler); assertValidCallable(new B(), handler); assertValidCallable(new C(), handler); diff --git a/gcloud-java-core/src/test/java/com/google/gcloud/PageImplTest.java b/gcloud-java-core/src/test/java/com/google/gcloud/PageImplTest.java new file mode 100644 index 000000000000..fb289186de8d --- /dev/null +++ b/gcloud-java-core/src/test/java/com/google/gcloud/PageImplTest.java @@ -0,0 +1,63 @@ +/* + * Copyright 2015 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.gcloud; + +import static org.junit.Assert.assertEquals; + +import com.google.common.collect.ImmutableList; + +import org.junit.Test; + +import java.util.Collections; + +public class PageImplTest { + + private static final ImmutableList VALUES = ImmutableList.of("1", "2"); + private static final ImmutableList NEXT_VALUES = ImmutableList.of("3", "4"); + private static final ImmutableList ALL_VALUES = ImmutableList.builder() + .addAll(VALUES) + .addAll(NEXT_VALUES) + .build(); + + @Test + public void testPage() { + final PageImpl nextResult = new PageImpl<>(null, "c", NEXT_VALUES); + PageImpl.NextPageFetcher fetcher = new PageImpl.NextPageFetcher() { + @Override + public PageImpl nextPage() { + return nextResult; + } + }; + PageImpl result = new PageImpl<>(fetcher, "c", VALUES); + assertEquals(nextResult, result.nextPage()); + assertEquals("c", result.nextPageCursor()); + assertEquals(VALUES, result.values()); + } + + @Test + public void testIterateAll() { + final PageImpl nextResult = new PageImpl<>(null, "c", NEXT_VALUES); + PageImpl.NextPageFetcher fetcher = new PageImpl.NextPageFetcher() { + @Override + public PageImpl nextPage() { + return nextResult; + } + }; + PageImpl result = new PageImpl<>(fetcher, "c", VALUES); + assertEquals(ALL_VALUES, ImmutableList.copyOf(result.iterateAll())); + } +} diff --git a/gcloud-java-core/src/test/java/com/google/gcloud/RetryHelperTest.java b/gcloud-java-core/src/test/java/com/google/gcloud/RetryHelperTest.java index dfd933bcae46..9a7cc2104f4a 100644 --- a/gcloud-java-core/src/test/java/com/google/gcloud/RetryHelperTest.java +++ b/gcloud-java-core/src/test/java/com/google/gcloud/RetryHelperTest.java @@ -118,13 +118,13 @@ public void testTriesAtLeastMinTimes() { @Override public Integer call() throws IOException { timesCalled++; assertEquals(timesCalled, RetryHelper.getContext().getAttemptNumber()); - assertEquals(10, RetryHelper.getContext().getRetryParams().getRetryMaxAttempts()); + assertEquals(10, RetryHelper.getContext().getRetryParams().retryMaxAttempts()); if (timesCalled <= timesToFail) { throw new IOException(); } return timesCalled; } - }, params, ExceptionHandler.getDefaultInstance()); + }, params, ExceptionHandler.defaultInstance()); assertEquals(timesToFail + 1, attempted); assertNull(RetryHelper.getContext()); } diff --git a/gcloud-java-core/src/test/java/com/google/gcloud/RetryParamsTest.java b/gcloud-java-core/src/test/java/com/google/gcloud/RetryParamsTest.java index d1d5e3c076d8..eae44693929b 100644 --- a/gcloud-java-core/src/test/java/com/google/gcloud/RetryParamsTest.java +++ b/gcloud-java-core/src/test/java/com/google/gcloud/RetryParamsTest.java @@ -41,15 +41,15 @@ public class RetryParamsTest { @Test public void testDefaults() { - RetryParams params1 = RetryParams.getDefaultInstance(); + RetryParams params1 = RetryParams.defaultInstance(); RetryParams params2 = RetryParams.builder().build(); for (RetryParams params : Arrays.asList(params1, params2)) { - assertEquals(DEFAULT_INITIAL_RETRY_DELAY_MILLIS, params.getInitialRetryDelayMillis()); - assertEquals(DEFAULT_MAX_RETRY_DELAY_MILLIS, params.getMaxRetryDelayMillis()); - assertEquals(DEFAULT_RETRY_DELAY_BACKOFF_FACTOR, params.getRetryDelayBackoffFactor(), 0); - assertEquals(DEFAULT_RETRY_MAX_ATTEMPTS, params.getRetryMaxAttempts()); - assertEquals(DEFAULT_RETRY_MIN_ATTEMPTS, params.getRetryMinAttempts()); - assertEquals(DEFAULT_TOTAL_RETRY_PERIOD_MILLIS, params.getTotalRetryPeriodMillis()); + assertEquals(DEFAULT_INITIAL_RETRY_DELAY_MILLIS, params.initialRetryDelayMillis()); + assertEquals(DEFAULT_MAX_RETRY_DELAY_MILLIS, params.maxRetryDelayMillis()); + assertEquals(DEFAULT_RETRY_DELAY_BACKOFF_FACTOR, params.retryDelayBackoffFactor(), 0); + assertEquals(DEFAULT_RETRY_MAX_ATTEMPTS, params.retryMaxAttempts()); + assertEquals(DEFAULT_RETRY_MIN_ATTEMPTS, params.retryMinAttempts()); + assertEquals(DEFAULT_TOTAL_RETRY_PERIOD_MILLIS, params.totalRetryPeriodMillis()); } } @@ -65,12 +65,12 @@ public void testSetAndCopy() { RetryParams params1 = builder.build(); RetryParams params2 = new RetryParams.Builder(params1).build(); for (RetryParams params : Arrays.asList(params1, params2)) { - assertEquals(101, params.getInitialRetryDelayMillis()); - assertEquals(102, params.getMaxRetryDelayMillis()); - assertEquals(103, params.getRetryDelayBackoffFactor(), 0); - assertEquals(107, params.getRetryMinAttempts()); - assertEquals(108, params.getRetryMaxAttempts()); - assertEquals(109, params.getTotalRetryPeriodMillis()); + assertEquals(101, params.initialRetryDelayMillis()); + assertEquals(102, params.maxRetryDelayMillis()); + assertEquals(103, params.retryDelayBackoffFactor(), 0); + assertEquals(107, params.retryMinAttempts()); + assertEquals(108, params.retryMaxAttempts()); + assertEquals(109, params.totalRetryPeriodMillis()); } } @@ -79,19 +79,19 @@ public void testBadSettings() { RetryParams.Builder builder = RetryParams.builder(); builder.initialRetryDelayMillis(-1); builder = assertFailure(builder); - builder.maxRetryDelayMillis(RetryParams.getDefaultInstance().getInitialRetryDelayMillis() - 1); + builder.maxRetryDelayMillis(RetryParams.defaultInstance().initialRetryDelayMillis() - 1); builder = assertFailure(builder); builder.retryDelayBackoffFactor(-1); builder = assertFailure(builder); builder.retryMinAttempts(-1); builder = assertFailure(builder); - builder.retryMaxAttempts(RetryParams.getDefaultInstance().getRetryMinAttempts() - 1); + builder.retryMaxAttempts(RetryParams.defaultInstance().retryMinAttempts() - 1); builder = assertFailure(builder); builder.totalRetryPeriodMillis(-1); builder = assertFailure(builder); // verify that it is OK for min and max to be equal - builder.retryMaxAttempts(RetryParams.getDefaultInstance().getRetryMinAttempts()); - builder.maxRetryDelayMillis(RetryParams.getDefaultInstance().getInitialRetryDelayMillis()); + builder.retryMaxAttempts(RetryParams.defaultInstance().retryMinAttempts()); + builder.maxRetryDelayMillis(RetryParams.defaultInstance().initialRetryDelayMillis()); builder.build(); } diff --git a/gcloud-java-datastore/README.md b/gcloud-java-datastore/README.md index bbcdd9d8857c..6d9fc0e8c4d6 100644 --- a/gcloud-java-datastore/README.md +++ b/gcloud-java-datastore/README.md @@ -15,14 +15,22 @@ Java idiomatic client for [Google Cloud Datastore] (https://cloud.google.com/dat Quickstart ---------- -Add this to your pom.xml file +If you are using Maven, add this to your pom.xml file ```xml com.google.gcloud gcloud-java-datastore - 0.0.10 + 0.0.11 ``` +If you are using Gradle, add this to your dependencies +```Groovy +compile 'com.google.gcloud:gcloud-java-datastore:jar:0.0.11' +``` +If you are using SBT, add this to your dependencies +```Scala +libraryDependencies += "com.google.gcloud" % "gcloud-java-datastore" % "0.0.11" +``` Example Application -------------------- @@ -58,7 +66,7 @@ import com.google.gcloud.datastore.Entity; import com.google.gcloud.datastore.Key; import com.google.gcloud.datastore.KeyFactory; -Datastore datastore = DatastoreOptions.getDefaultInstance().service(); +Datastore datastore = DatastoreOptions.defaultInstance().service(); KeyFactory keyFactory = datastore.newKeyFactory().kind(KIND); Key key = keyFactory.newKey(keyName); Entity entity = datastore.get(key); diff --git a/gcloud-java-datastore/pom.xml b/gcloud-java-datastore/pom.xml index c7c57d91ae1d..b58e9e0ffc74 100644 --- a/gcloud-java-datastore/pom.xml +++ b/gcloud-java-datastore/pom.xml @@ -11,7 +11,7 @@ com.google.gcloud gcloud-java-pom - 0.0.11-SNAPSHOT + 0.0.12-SNAPSHOT gcloud-java-datastore diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreException.java b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreException.java index 562578a26428..dded1d11875e 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreException.java +++ b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreException.java @@ -18,6 +18,7 @@ import com.google.common.base.MoreObjects; import com.google.common.collect.ImmutableMap; +import com.google.gcloud.BaseServiceException; import com.google.gcloud.RetryHelper; import com.google.gcloud.RetryHelper.RetryHelperException; import com.google.gcloud.spi.DatastoreRpc.DatastoreRpcException; @@ -26,21 +27,21 @@ import java.util.HashMap; import java.util.Map; -public class DatastoreException extends RuntimeException { +public class DatastoreException extends BaseServiceException { - private static final long serialVersionUID = 8170357898917041899L; - private static final ImmutableMap REASON_TO_CODE; - private static final ImmutableMap HTTP_TO_CODE; + private static final long serialVersionUID = -2336749234060754893L; + private static final ImmutableMap REASON_TO_ERROR; + private static final ImmutableMap HTTP_TO_ERROR; - private final Code code; + private final DatastoreError error; /** - * An error code to represent the failure. + * Represents Datastore errors. * * @see Google Cloud * Datastore error codes */ - public enum Code { + public enum DatastoreError { ABORTED(Reason.ABORTED), DEADLINE_EXCEEDED(Reason.DEADLINE_EXCEEDED), @@ -57,29 +58,25 @@ public enum Code { private final String description; private final int httpStatus; - Code(Reason reason) { + DatastoreError(Reason reason) { this(reason.retryable(), reason.description(), reason.httpStatus()); } - Code(boolean retryable, String description, int httpStatus) { + DatastoreError(boolean retryable, String description, int httpStatus) { this.retryable = retryable; this.description = description; this.httpStatus = httpStatus; } - public String description() { + String description() { return description; } - public int httpStatus() { + int httpStatus() { return httpStatus; } - /** - * Returns {@code true} if this exception is transient and the same request could be retried. - * For any retry it is highly recommended to apply an exponential backoff. - */ - public boolean retryable() { + boolean retryable() { return retryable; } @@ -89,30 +86,31 @@ DatastoreException translate(DatastoreRpcException exception, String message) { } static { - ImmutableMap.Builder builder = ImmutableMap.builder(); - Map httpCodes = new HashMap<>(); - for (Code code : Code.values()) { - builder.put(code.name(), code); - httpCodes.put(code.httpStatus(), code); + ImmutableMap.Builder builder = ImmutableMap.builder(); + Map httpCodes = new HashMap<>(); + for (DatastoreError error : DatastoreError.values()) { + builder.put(error.name(), error); + httpCodes.put(error.httpStatus(), error); } - REASON_TO_CODE = builder.build(); - HTTP_TO_CODE = ImmutableMap.copyOf(httpCodes); + REASON_TO_ERROR = builder.build(); + HTTP_TO_ERROR = ImmutableMap.copyOf(httpCodes); } - public DatastoreException(Code code, String message, Exception cause) { - super(MoreObjects.firstNonNull(message, code.description), cause); - this.code = code; + public DatastoreException(DatastoreError error, String message, Exception cause) { + super(error.httpStatus(), MoreObjects.firstNonNull(message, error.description), + error.retryable(), cause); + this.error = error; } - public DatastoreException(Code code, String message) { - this(code, message, null); + public DatastoreException(DatastoreError error, String message) { + this(error, message, null); } /** - * Returns the code associated with this exception. + * Returns the DatastoreError associated with this exception. */ - public Code code() { - return code; + public DatastoreError datastoreError() { + return error; } static DatastoreException translateAndThrow(RetryHelperException ex) { @@ -122,35 +120,36 @@ static DatastoreException translateAndThrow(RetryHelperException ex) { if (ex instanceof RetryHelper.RetryInterruptedException) { RetryHelper.RetryInterruptedException.propagate(); } - throw new DatastoreException(Code.UNKNOWN, ex.getMessage(), ex); + throw new DatastoreException(DatastoreError.UNKNOWN, ex.getMessage(), ex); } /** - * Translate DatastoreException to DatastoreException based on their + * Translate DatastoreRpcExceptions to DatastoreExceptions based on their * HTTP error codes. This method will always throw a new DatastoreException. * * @throws DatastoreException every time */ static DatastoreException translateAndThrow(DatastoreRpcException exception) { String message = exception.getMessage(); - Code code = REASON_TO_CODE.get(exception.reason()); - if (code == null) { - code = MoreObjects.firstNonNull(HTTP_TO_CODE.get(exception.httpStatus()), Code.UNKNOWN); + DatastoreError error = REASON_TO_ERROR.get(exception.reason()); + if (error == null) { + error = MoreObjects.firstNonNull( + HTTP_TO_ERROR.get(exception.httpStatus()), DatastoreError.UNKNOWN); } - throw code.translate(exception, message); + throw error.translate(exception, message); } /** - * Throw a DatastoreException with {@code FAILED_PRECONDITION} code and the {@code message} + * Throw a DatastoreException with {@code FAILED_PRECONDITION} error and the {@code message} * in a nested exception. * * @throws DatastoreException every time */ static DatastoreException throwInvalidRequest(String massage, Object... params) { - throw new DatastoreException(Code.FAILED_PRECONDITION, String.format(massage, params)); + throw new DatastoreException(DatastoreError.FAILED_PRECONDITION, String.format(massage, params)); } static DatastoreException propagateUserException(Exception ex) { - throw new DatastoreException(Code.UNKNOWN, ex.getMessage(), ex); + throw new DatastoreException(DatastoreError.UNKNOWN, ex.getMessage(), ex); } } diff --git a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreOptions.java b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreOptions.java index 5338c03a6d56..ee15393a8048 100644 --- a/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreOptions.java +++ b/gcloud-java-datastore/src/main/java/com/google/gcloud/datastore/DatastoreOptions.java @@ -171,6 +171,13 @@ public String namespace() { return namespace; } + /** + * Returns a default {@code DatastoreOptions} instance. + */ + public static DatastoreOptions defaultInstance() { + return builder().build(); + } + private static String defaultNamespace() { try { Class clazz = Class.forName("com.google.appengine.api.NamespaceManager"); diff --git a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DatastoreExceptionTest.java b/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DatastoreExceptionTest.java index a64a3531c19d..9ad836b15a4e 100644 --- a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DatastoreExceptionTest.java +++ b/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DatastoreExceptionTest.java @@ -19,7 +19,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; -import com.google.gcloud.datastore.DatastoreException.Code; +import com.google.gcloud.datastore.DatastoreException.DatastoreError; import com.google.gcloud.spi.DatastoreRpc.DatastoreRpcException; import com.google.gcloud.spi.DatastoreRpc.DatastoreRpcException.Reason; @@ -28,16 +28,16 @@ public class DatastoreExceptionTest { @Test - public void testCode() throws Exception { + public void testDatastoreError() throws Exception { for (Reason reason : Reason.values()) { - Code code = Code.valueOf(reason.name()); - assertEquals(reason.retryable(), code.retryable()); - assertEquals(reason.description(), code.description()); - assertEquals(reason.httpStatus(), code.httpStatus()); + DatastoreError error = DatastoreError.valueOf(reason.name()); + assertEquals(reason.retryable(), error.retryable()); + assertEquals(reason.description(), error.description()); + assertEquals(reason.httpStatus(), error.httpStatus()); } - DatastoreException exception = new DatastoreException(Code.ABORTED, "bla"); - assertEquals(Code.ABORTED, exception.code()); + DatastoreException exception = new DatastoreException(DatastoreError.ABORTED, "bla"); + assertEquals(DatastoreError.ABORTED, exception.datastoreError()); } @Test @@ -47,7 +47,7 @@ public void testTranslateAndThrow() throws Exception { DatastoreException.translateAndThrow(new DatastoreRpcException(reason)); fail("Exception expected"); } catch (DatastoreException ex) { - assertEquals(reason.name(), ex.code().name()); + assertEquals(reason.name(), ex.datastoreError().name()); } } } @@ -58,7 +58,7 @@ public void testThrowInvalidRequest() throws Exception { DatastoreException.throwInvalidRequest("message %s %d", "a", 1); fail("Exception expected"); } catch (DatastoreException ex) { - assertEquals(Code.FAILED_PRECONDITION, ex.code()); + assertEquals(DatastoreError.FAILED_PRECONDITION, ex.datastoreError()); assertEquals("message a 1", ex.getMessage()); } } diff --git a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DatastoreTest.java b/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DatastoreTest.java index f639ca3fdac0..162104c0dd84 100644 --- a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DatastoreTest.java +++ b/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/DatastoreTest.java @@ -197,7 +197,7 @@ public void testTransactionWithRead() { transaction.commit(); fail("Expecting a failure"); } catch (DatastoreException expected) { - assertEquals(DatastoreException.Code.ABORTED, expected.code()); + assertEquals(DatastoreException.DatastoreError.ABORTED, expected.datastoreError()); } } @@ -225,7 +225,7 @@ public void testTransactionWithQuery() { transaction.commit(); fail("Expecting a failure"); } catch (DatastoreException expected) { - assertEquals(DatastoreException.Code.ABORTED, expected.code()); + assertEquals(DatastoreException.DatastoreError.ABORTED, expected.datastoreError()); } } @@ -625,7 +625,7 @@ private Datastore createDatastoreForDeferredLookup() throws DatastoreRpcExceptio EasyMock.replay(rpcFactoryMock, rpcMock); DatastoreOptions options = this.options.toBuilder() - .retryParams(RetryParams.getDefaultInstance()) + .retryParams(RetryParams.defaultInstance()) .serviceRpcFactory(rpcFactoryMock) .build(); return options.service(); @@ -738,7 +738,7 @@ public void testRetryableException() throws Exception { .andReturn(responsePb); EasyMock.replay(rpcFactoryMock, rpcMock); DatastoreOptions options = this.options.toBuilder() - .retryParams(RetryParams.getDefaultInstance()) + .retryParams(RetryParams.defaultInstance()) .serviceRpcFactory(rpcFactoryMock) .build(); Datastore datastore = options.service(); @@ -784,7 +784,7 @@ public void testRuntimeException() throws Exception { .andThrow(new RuntimeException(exceptionMessage)); EasyMock.replay(rpcFactoryMock, rpcMock); DatastoreOptions options = this.options.toBuilder() - .retryParams(RetryParams.getDefaultInstance()) + .retryParams(RetryParams.defaultInstance()) .serviceRpcFactory(rpcFactoryMock) .build(); Datastore datastore = options.service(); diff --git a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/SerializationTest.java b/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/SerializationTest.java index 1ad690938ef5..32e14fb47ea0 100644 --- a/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/SerializationTest.java +++ b/gcloud-java-datastore/src/test/java/com/google/gcloud/datastore/SerializationTest.java @@ -143,7 +143,7 @@ public void testServiceOptions() throws Exception { options = options.toBuilder() .namespace("ns1") - .retryParams(RetryParams.getDefaultInstance()) + .retryParams(RetryParams.defaultInstance()) .authCredentials(AuthCredentials.noCredentials()) .force(true) .build(); diff --git a/gcloud-java-examples/README.md b/gcloud-java-examples/README.md index 366acd5de929..bc738de41b51 100644 --- a/gcloud-java-examples/README.md +++ b/gcloud-java-examples/README.md @@ -12,14 +12,22 @@ Examples for gcloud-java (Java idiomatic client for [Google Cloud Platform][clou Quickstart ---------- -Add this to your pom.xml file +If you are using Maven, add this to your pom.xml file ```xml com.google.gcloud gcloud-java-examples - 0.0.10 + 0.0.11 ``` +If you are using Gradle, add this to your dependencies +```Groovy +compile 'com.google.gcloud:gcloud-java-examples:jar:0.0.11' +``` +If you are using SBT, add this to your dependencies +```Scala +libraryDependencies += "com.google.gcloud" % "gcloud-java-examples" % "0.0.11" +``` To run examples from your command line: diff --git a/gcloud-java-examples/pom.xml b/gcloud-java-examples/pom.xml index 55548e756be3..c461846acab2 100644 --- a/gcloud-java-examples/pom.xml +++ b/gcloud-java-examples/pom.xml @@ -11,7 +11,7 @@ com.google.gcloud gcloud-java-pom - 0.0.11-SNAPSHOT + 0.0.12-SNAPSHOT gcloud-java-examples diff --git a/gcloud-java-examples/src/main/java/com/google/gcloud/examples/StorageExample.java b/gcloud-java-examples/src/main/java/com/google/gcloud/examples/StorageExample.java index fb207023203f..0e7d6bbbf3ea 100644 --- a/gcloud-java-examples/src/main/java/com/google/gcloud/examples/StorageExample.java +++ b/gcloud-java-examples/src/main/java/com/google/gcloud/examples/StorageExample.java @@ -52,6 +52,7 @@ import java.security.cert.CertificateException; import java.util.Arrays; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -213,8 +214,9 @@ String parse(String... args) { public void run(Storage storage, String bucketName) { if (bucketName == null) { // list buckets - for (BucketInfo b : storage.list()) { - System.out.println(b); + Iterator bucketInfoIterator = storage.list().iterateAll(); + while (bucketInfoIterator.hasNext()) { + System.out.println(bucketInfoIterator.next()); } } else { // list a bucket's blobs @@ -223,8 +225,9 @@ public void run(Storage storage, String bucketName) { System.out.println("No such bucket"); return; } - for (Blob b : bucket.list()) { - System.out.println(b.info()); + Iterator blobIterator = bucket.list().iterateAll(); + while (blobIterator.hasNext()) { + System.out.println(blobIterator.next().info()); } } } @@ -542,7 +545,7 @@ public static void main(String... args) throws Exception { return; } StorageOptions.Builder optionsBuilder = - StorageOptions.builder().retryParams(RetryParams.getDefaultInstance()); + StorageOptions.builder().retryParams(RetryParams.defaultInstance()); StorageAction action; String actionName; if (args.length >= 2 && !ACTIONS.containsKey(args[0])) { diff --git a/gcloud-java-storage/README.md b/gcloud-java-storage/README.md index 717fd1f1f3e4..f2b99388ff0f 100644 --- a/gcloud-java-storage/README.md +++ b/gcloud-java-storage/README.md @@ -15,14 +15,22 @@ Java idiomatic client for [Google Cloud Storage] (https://cloud.google.com/stora Quickstart ---------- -Add this to your pom.xml file +If you are using Maven, add this to your pom.xml file ```xml com.google.gcloud gcloud-java-storage - 0.0.10 + 0.0.11 ``` +If you are using Gradle, add this to your dependencies +```Groovy +compile 'com.google.gcloud:gcloud-java-storage:jar:0.0.11' +``` +If you are using SBT, add this to your dependencies +```Scala +libraryDependencies += "com.google.gcloud" % "gcloud-java-storage" % "0.0.11" +``` Example Application ------------------- diff --git a/gcloud-java-storage/pom.xml b/gcloud-java-storage/pom.xml index 82b67277f4fe..ef3ddec79816 100644 --- a/gcloud-java-storage/pom.xml +++ b/gcloud-java-storage/pom.xml @@ -11,7 +11,7 @@ com.google.gcloud gcloud-java-pom - 0.0.11-SNAPSHOT + 0.0.12-SNAPSHOT gcloud-java-storage diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/spi/DefaultStorageRpc.java b/gcloud-java-storage/src/main/java/com/google/gcloud/spi/DefaultStorageRpc.java index 70cad8c7773e..b1e188f1d1fb 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/spi/DefaultStorageRpc.java +++ b/gcloud-java-storage/src/main/java/com/google/gcloud/spi/DefaultStorageRpc.java @@ -15,6 +15,7 @@ package com.google.gcloud.spi; import static com.google.gcloud.spi.StorageRpc.Option.DELIMITER; +import static com.google.gcloud.spi.StorageRpc.Option.FIELDS; import static com.google.gcloud.spi.StorageRpc.Option.IF_GENERATION_MATCH; import static com.google.gcloud.spi.StorageRpc.Option.IF_GENERATION_NOT_MATCH; import static com.google.gcloud.spi.StorageRpc.Option.IF_METAGENERATION_MATCH; @@ -150,6 +151,7 @@ public Tuple> list(Map options) { .setPrefix(PREFIX.getString(options)) .setMaxResults(MAX_RESULTS.getLong(options)) .setPageToken(PAGE_TOKEN.getString(options)) + .setFields(FIELDS.getString(options)) .execute(); return Tuple.>of(buckets.getNextPageToken(), buckets.getItems()); } catch (IOException ex) { @@ -168,6 +170,7 @@ public Tuple> list(String bucket, Map .setPrefix(PREFIX.getString(options)) .setMaxResults(MAX_RESULTS.getLong(options)) .setPageToken(PAGE_TOKEN.getString(options)) + .setFields(FIELDS.getString(options)) .execute(); return Tuple.>of( objects.getNextPageToken(), objects.getItems()); @@ -184,6 +187,7 @@ public Bucket get(Bucket bucket, Map options) { .setProjection(DEFAULT_PROJECTION) .setIfMetagenerationMatch(IF_METAGENERATION_MATCH.getLong(options)) .setIfMetagenerationNotMatch(IF_METAGENERATION_NOT_MATCH.getLong(options)) + .setFields(FIELDS.getString(options)) .execute(); } catch (IOException ex) { throw translate(ex); @@ -207,7 +211,8 @@ private Storage.Objects.Get getRequest(StorageObject object, Map opti .setIfMetagenerationMatch(IF_METAGENERATION_MATCH.getLong(options)) .setIfMetagenerationNotMatch(IF_METAGENERATION_NOT_MATCH.getLong(options)) .setIfGenerationMatch(IF_GENERATION_MATCH.getLong(options)) - .setIfGenerationNotMatch(IF_GENERATION_NOT_MATCH.getLong(options)); + .setIfGenerationNotMatch(IF_GENERATION_NOT_MATCH.getLong(options)) + .setFields(FIELDS.getString(options)); } @Override diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/spi/StorageRpc.java b/gcloud-java-storage/src/main/java/com/google/gcloud/spi/StorageRpc.java index 40382a857fca..e4b1be785951 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/spi/StorageRpc.java +++ b/gcloud-java-storage/src/main/java/com/google/gcloud/spi/StorageRpc.java @@ -47,7 +47,8 @@ enum Option { MAX_RESULTS("maxResults"), PAGE_TOKEN("pageToken"), DELIMITER("delimiter"), - VERSIONS("versions"); + VERSIONS("versions"), + FIELDS("fields"); private final String value; diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Acl.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Acl.java index 3d9731352400..fd75e10d92fa 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Acl.java +++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Acl.java @@ -23,7 +23,10 @@ import java.util.Objects; /** - * Access Control List on for buckets or blobs. + * Access Control List for buckets or blobs. + * + * @see + * About Access Control Lists */ public final class Acl implements Serializable { @@ -36,6 +39,9 @@ public enum Role { OWNER, READER, WRITER } + /** + * Base class for Access Control List entities. + */ public abstract static class Entity implements Serializable { private static final long serialVersionUID = -2707407252771255840L; @@ -52,10 +58,16 @@ public enum Type { this.value = value; } + /** + * Returns the type of entity. + */ public Type type() { return type; } + /** + * Returns the entity's value. + */ protected String value() { return value; } @@ -112,42 +124,75 @@ static Entity fromPb(String entity) { } } + /** + * Class for ACL Domain entities. + */ public static final class Domain extends Entity { private static final long serialVersionUID = -3033025857280447253L; + /** + * Creates a domain entity. + * + * @param domain the domain associated to this entity + */ public Domain(String domain) { super(Type.DOMAIN, domain); } + /** + * Returns the domain associated to this entity. + */ public String domain() { return value(); } } + /** + * Class for ACL Group entities. + */ public static final class Group extends Entity { private static final long serialVersionUID = -1660987136294408826L; + /** + * Creates a group entity. + * + * @param email the group email + */ public Group(String email) { super(Type.GROUP, email); } + /** + * Returns the group email. + */ public String email() { return value(); } } + /** + * Class for ACL User entities. + */ public static final class User extends Entity { private static final long serialVersionUID = 3076518036392737008L; private static final String ALL_USERS = "allUsers"; private static final String ALL_AUTHENTICATED_USERS = "allAuthenticatedUsers"; + /** + * Creates a user entity. + * + * @param email the user email + */ public User(String email) { super(Type.USER, email); } + /** + * Returns the user email. + */ public String email() { return value(); } @@ -174,6 +219,9 @@ public static User ofAllAuthenticatedUsers() { } } + /** + * Class for ACL Project entities. + */ public static final class Project extends Entity { private static final long serialVersionUID = 7933776866530023027L; @@ -185,16 +233,28 @@ public enum ProjectRole { OWNERS, EDITORS, VIEWERS } + /** + * Creates a project entity. + * + * @param pRole a role in the project, used to select project's teams + * @param projectId id of the project + */ public Project(ProjectRole pRole, String projectId) { super(Type.PROJECT, pRole.name().toLowerCase() + "-" + projectId); this.pRole = pRole; this.projectId = projectId; } + /** + * Returns the role in the project for this entity. + */ public ProjectRole projectRole() { return pRole; } + /** + * Returns the project id for this entity. + */ public String projectId() { return projectId; } @@ -214,15 +274,27 @@ String toPb() { } } + /** + * Creats an ACL object. + * + * @param entity the entity for this ACL object + * @param role the role to associate to the {@code entity} object + */ public Acl(Entity entity, Role role) { this.entity = entity; this.role = role; } + /** + * Returns the entity for this ACL object. + */ public Entity entity() { return entity; } + /** + * Returns the role associated to the entity in this ACL object. + */ public Role role() { return role; } diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BaseListResult.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BaseListResult.java deleted file mode 100644 index fdcd84705555..000000000000 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BaseListResult.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gcloud.storage; - -import java.io.Serializable; -import java.util.Collections; -import java.util.Iterator; -import java.util.Objects; - -/** - * Base implementation for Google Cloud storage list result. - */ -public class BaseListResult implements ListResult, Serializable { - - private static final long serialVersionUID = -6937287874908527950L; - - private final String cursor; - private final Iterable results; - private final NextPageFetcher pageFetcher; - - public interface NextPageFetcher extends Serializable { - ListResult nextPage(); - } - - public BaseListResult(NextPageFetcher pageFetcher, String cursor, Iterable results) { - this.pageFetcher = pageFetcher; - this.cursor = cursor; - this.results = results; - } - - @Override - public String nextPageCursor() { - return cursor; - } - - @Override - public ListResult nextPage() { - if (cursor == null || pageFetcher == null) { - return null; - } - return pageFetcher.nextPage(); - } - - @Override - public Iterator iterator() { - return results == null ? Collections.emptyIterator() : results.iterator(); - } - - @Override - public int hashCode() { - return Objects.hash(cursor, results); - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof BaseListResult)) { - return false; - } - BaseListResult other = (BaseListResult) obj; - return Objects.equals(cursor, other.cursor) - && Objects.equals(results, other.results); - } -} diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BatchRequest.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BatchRequest.java index 6e815648497a..bf77c731754e 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BatchRequest.java +++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BatchRequest.java @@ -18,6 +18,7 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; +import com.google.gcloud.storage.Storage.BlobGetOption; import com.google.gcloud.storage.Storage.BlobSourceOption; import com.google.gcloud.storage.Storage.BlobTargetOption; @@ -35,13 +36,13 @@ public final class BatchRequest implements Serializable { private final Map> toDelete; private final Map> toUpdate; - private final Map> toGet; + private final Map> toGet; public static class Builder { private Map> toDelete = new LinkedHashMap<>(); private Map> toUpdate = new LinkedHashMap<>(); - private Map> toGet = new LinkedHashMap<>(); + private Map> toGet = new LinkedHashMap<>(); private Builder() {} @@ -72,7 +73,7 @@ public Builder update(BlobInfo blobInfo, BlobTargetOption... options) { /** * Retrieve metadata for the given blob. */ - public Builder get(String bucket, String blob, BlobSourceOption... options) { + public Builder get(String bucket, String blob, BlobGetOption... options) { toGet.put(BlobId.of(bucket, blob), Lists.newArrayList(options)); return this; } @@ -80,7 +81,7 @@ public Builder get(String bucket, String blob, BlobSourceOption... options) { /** * Retrieve metadata for the given blob. */ - public Builder get(BlobId blob, BlobSourceOption... options) { + public Builder get(BlobId blob, BlobGetOption... options) { toGet.put(blob, Lists.newArrayList(options)); return this; } @@ -120,7 +121,7 @@ public Map> toUpdate() { return toUpdate; } - public Map> toGet() { + public Map> toGet() { return toGet; } diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Blob.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Blob.java index 8f988922aad9..a8e315be0e45 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Blob.java +++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Blob.java @@ -18,7 +18,8 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.gcloud.storage.Blob.BlobSourceOption.convert; +import static com.google.gcloud.storage.Blob.BlobSourceOption.toSourceOptions; +import static com.google.gcloud.storage.Blob.BlobSourceOption.toGetOptions; import com.google.common.base.Function; import com.google.common.collect.Lists; @@ -29,6 +30,7 @@ import com.google.gcloud.storage.Storage.SignUrlOption; import java.net.URL; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Objects; @@ -48,6 +50,9 @@ public final class Blob { private final Storage storage; private final BlobInfo info; + /** + * Class for specifying blob source options when {@code Blob} methods are used. + */ public static class BlobSourceOption extends Option { private static final long serialVersionUID = 214616862061934846L; @@ -56,7 +61,7 @@ private BlobSourceOption(StorageRpc.Option rpcOption) { super(rpcOption, null); } - private Storage.BlobSourceOption convert(BlobInfo blobInfo) { + private Storage.BlobSourceOption toSourceOptions(BlobInfo blobInfo) { switch (rpcOption()) { case IF_GENERATION_MATCH: return Storage.BlobSourceOption.generationMatch(blobInfo.generation()); @@ -71,27 +76,68 @@ private Storage.BlobSourceOption convert(BlobInfo blobInfo) { } } + private Storage.BlobGetOption toGetOption(BlobInfo blobInfo) { + switch (rpcOption()) { + case IF_GENERATION_MATCH: + return Storage.BlobGetOption.generationMatch(blobInfo.generation()); + case IF_GENERATION_NOT_MATCH: + return Storage.BlobGetOption.generationNotMatch(blobInfo.generation()); + case IF_METAGENERATION_MATCH: + return Storage.BlobGetOption.metagenerationMatch(blobInfo.metageneration()); + case IF_METAGENERATION_NOT_MATCH: + return Storage.BlobGetOption.metagenerationNotMatch(blobInfo.metageneration()); + default: + throw new AssertionError("Unexpected enum value"); + } + } + + /** + * Returns an option for blob's generation match. If this option is used the request will fail + * if generation does not match. + */ public static BlobSourceOption generationMatch() { return new BlobSourceOption(StorageRpc.Option.IF_GENERATION_MATCH); } + /** + * Returns an option for blob's generation mismatch. If this option is used the request will + * fail if generation matches. + */ public static BlobSourceOption generationNotMatch() { return new BlobSourceOption(StorageRpc.Option.IF_GENERATION_NOT_MATCH); } + /** + * Returns an option for blob's metageneration match. If this option is used the request will + * fail if metageneration does not match. + */ public static BlobSourceOption metagenerationMatch() { return new BlobSourceOption(StorageRpc.Option.IF_METAGENERATION_MATCH); } + /** + * Returns an option for blob's metageneration mismatch. If this option is used the request will + * fail if metageneration matches. + */ public static BlobSourceOption metagenerationNotMatch() { return new BlobSourceOption(StorageRpc.Option.IF_METAGENERATION_NOT_MATCH); } - static Storage.BlobSourceOption[] convert(BlobInfo blobInfo, BlobSourceOption... options) { + static Storage.BlobSourceOption[] toSourceOptions(BlobInfo blobInfo, + BlobSourceOption... options) { Storage.BlobSourceOption[] convertedOptions = new Storage.BlobSourceOption[options.length]; int index = 0; for (BlobSourceOption option : options) { - convertedOptions[index++] = option.convert(blobInfo); + convertedOptions[index++] = option.toSourceOptions(blobInfo); + } + return convertedOptions; + } + + static Storage.BlobGetOption[] toGetOptions(BlobInfo blobInfo, BlobSourceOption... options) { + Storage.BlobGetOption[] convertedOptions = new Storage.BlobGetOption[options.length]; + int index = 0; + for (BlobSourceOption option : options) { + convertedOptions[index++] = option.toGetOption(blobInfo); } return convertedOptions; } @@ -100,7 +146,7 @@ static Storage.BlobSourceOption[] convert(BlobInfo blobInfo, BlobSourceOption... /** * Constructs a {@code Blob} object for the provided {@code BlobInfo}. The storage service is used * to issue requests. - * + * * @param storage the storage service used for issuing requests * @param info blob's info */ @@ -112,7 +158,7 @@ public Blob(Storage storage, BlobInfo info) { /** * Creates a {@code Blob} object for the provided bucket and blob names. Performs an RPC call to * get the latest blob information. - * + * * @param storage the storage service used for issuing requests * @param bucket bucket's name * @param blob blob's name @@ -126,7 +172,7 @@ public static Blob load(Storage storage, String bucket, String blob) { /** * Creates a {@code Blob} object for the provided {@code blobId}. Performs an RPC call to get the * latest blob information. - * + * * @param storage the storage service used for issuing requests * @param blobId blob's identifier * @return the {@code Blob} object or {@code null} if not found. @@ -159,7 +205,10 @@ public BlobId id() { * @throws StorageException upon failure */ public boolean exists(BlobSourceOption... options) { - return storage.get(info.blobId(), convert(info, options)) != null; + int length = options.length; + Storage.BlobGetOption[] getOptions = Arrays.copyOf(toGetOptions(info, options), length + 1); + getOptions[length] = Storage.BlobGetOption.fields(); + return storage.get(info.blobId(), getOptions) != null; } /** @@ -180,7 +229,7 @@ public byte[] content(Storage.BlobSourceOption... options) { * @throws StorageException upon failure */ public Blob reload(BlobSourceOption... options) { - return new Blob(storage, storage.get(info.blobId(), convert(info, options))); + return new Blob(storage, storage.get(info.blobId(), toGetOptions(info, options))); } /** @@ -224,7 +273,7 @@ public Blob update(BlobInfo blobInfo, BlobTargetOption... options) { */ public CopyWriter copyTo(BlobId targetBlob, BlobSourceOption... options) { CopyRequest copyRequest = CopyRequest.builder().source(info.bucket(), info.name()) - .sourceOptions(convert(info, options)).target(targetBlob).build(); + .sourceOptions(toSourceOptions(info, options)).target(targetBlob).build(); return storage.copy(copyRequest); } @@ -236,7 +285,7 @@ public CopyWriter copyTo(BlobId targetBlob, BlobSourceOption... options) { * @throws StorageException upon failure */ public boolean delete(BlobSourceOption... options) { - return storage.delete(info.blobId(), convert(info, options)); + return storage.delete(info.blobId(), toSourceOptions(info, options)); } /** @@ -275,7 +324,7 @@ public CopyWriter copyTo(String targetBucket, String targetBlob, BlobSourceOptio * @throws StorageException upon failure */ public BlobReadChannel reader(BlobSourceOption... options) { - return storage.reader(info.blobId(), convert(info, options)); + return storage.reader(info.blobId(), toSourceOptions(info, options)); } /** diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobId.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobId.java index eafebe09a4cb..d1209826cc3e 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobId.java +++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobId.java @@ -38,10 +38,16 @@ private BlobId(String bucket, String name) { this.name = name; } + /** + * Returns the name of the bucket containing the blob. + */ public String bucket() { return bucket; } + /** + * Returns the name of the blob. + */ public String name() { return name; } @@ -72,6 +78,12 @@ StorageObject toPb() { return storageObject; } + /** + * Creates a blob identifier. + * + * @param bucket the name of the bucket that contains the blob + * @param name the name of the blob + */ public static BlobId of(String bucket, String name) { return new BlobId(checkNotNull(bucket), checkNotNull(name)); } diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobInfo.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobInfo.java index 01711a53613e..65b87498b6cc 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobInfo.java +++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobInfo.java @@ -123,6 +123,9 @@ public static final class Builder { private Builder() {} + /** + * Sets the blob identity. + */ public Builder blobId(BlobId blobId) { this.blobId = checkNotNull(blobId); return this; @@ -133,21 +136,41 @@ Builder id(String id) { return this; } + /** + * Sets the blob's data content type. + * + * @see Content-Type + */ public Builder contentType(String contentType) { this.contentType = firstNonNull(contentType, Data.nullOf(String.class)); return this; } + /** + * Sets the blob's data content disposition. + * + * @see Content-Disposition + */ public Builder contentDisposition(String contentDisposition) { this.contentDisposition = firstNonNull(contentDisposition, Data.nullOf(String.class)); return this; } + /** + * Sets the blob's data content language. + * + * @see Content-Language + */ public Builder contentLanguage(String contentLanguage) { this.contentLanguage = firstNonNull(contentLanguage, Data.nullOf(String.class)); return this; } + /** + * Sets the blob's data content encoding. + * + * @see Content-Encoding + */ public Builder contentEncoding(String contentEncoding) { this.contentEncoding = firstNonNull(contentEncoding, Data.nullOf(String.class)); return this; @@ -158,11 +181,22 @@ Builder componentCount(Integer componentCount) { return this; } + /** + * Sets the blob's data cache control. + * + * @see Cache-Control + */ public Builder cacheControl(String cacheControl) { this.cacheControl = firstNonNull(cacheControl, Data.nullOf(String.class)); return this; } + /** + * Sets the blob's access control configuration. + * + * @see + * About Access Control Lists + */ public Builder acl(List acl) { this.acl = acl != null ? ImmutableList.copyOf(acl) : null; return this; @@ -188,11 +222,25 @@ Builder selfLink(String selfLink) { return this; } + /** + * Sets the MD5 hash of blob's data. MD5 value must be encoded in base64. + * + * @see + * Hashes and ETags: Best Practices + */ public Builder md5(String md5) { this.md5 = firstNonNull(md5, Data.nullOf(String.class)); return this; } + /** + * Sets the CRC32C checksum of blob's data as described in + * RFC 4960, Appendix B; encoded in + * base64 in big-endian order. + * + * @see + * Hashes and ETags: Best Practices + */ public Builder crc32c(String crc32c) { this.crc32c = firstNonNull(crc32c, Data.nullOf(String.class)); return this; @@ -203,6 +251,9 @@ Builder mediaLink(String mediaLink) { return this; } + /** + * Sets the blob's user provided metadata. + */ public Builder metadata(Map metadata) { this.metadata = metadata != null ? new HashMap(metadata) : Data.nullOf(ImmutableEmptyMap.class); @@ -229,6 +280,9 @@ Builder updateTime(Long updateTime) { return this; } + /** + * Creates a {@code BlobInfo} object. + */ public BlobInfo build() { checkNotNull(blobId); return new BlobInfo(this); @@ -259,98 +313,203 @@ private BlobInfo(Builder builder) { updateTime = builder.updateTime; } + /** + * Returns the blob's identity. + */ public BlobId blobId() { return blobId; } + /** + * Returns the name of the containing bucket. + */ public String bucket() { return blobId().bucket(); } + /** + * Returns the blob's id. + */ public String id() { return id; } + /** + * Returns the blob's name. + */ public String name() { return blobId().name(); } + /** + * Returns the blob's data cache control. + * + * @see Cache-Control + */ public String cacheControl() { return Data.isNull(cacheControl) ? null : cacheControl; } + /** + * Returns the blob's access control configuration. + * + * @see + * About Access Control Lists + */ public List acl() { return acl; } + /** + * Returns the blob's owner. This will always be the uploader of the blob. + */ public Acl.Entity owner() { return owner; } + /** + * Returns the content length of the data in bytes. + * + * @see Content-Length + */ public Long size() { return size; } + /** + * Returns the blob's data content type. + * + * @see Content-Type + */ public String contentType() { return Data.isNull(contentType) ? null : contentType; } + /** + * Returns the blob's data content encoding. + * + * @see Content-Encoding + */ public String contentEncoding() { return Data.isNull(contentEncoding) ? null : contentEncoding; } + /** + * Returns the blob's data content disposition. + * + * @see Content-Disposition + */ public String contentDisposition() { return Data.isNull(contentDisposition) ? null : contentDisposition; } + /** + * Returns the blob's data content language. + * + * @see Content-Language + */ public String contentLanguage() { return Data.isNull(contentLanguage) ? null : contentLanguage; } + /** + * Returns the number of components that make up this blob. Components are accumulated through + * the {@link Storage#compose(Storage.ComposeRequest)} operation and are limited to a count of + * 1024, counting 1 for each non-composite component blob and componentCount for each composite + * component blob. This value is set only for composite blobs. + * + * @see Component Count + * Property + */ public Integer componentCount() { return componentCount; } + /** + * Returns HTTP 1.1 Entity tag for the blob. + * + * @see Entity Tags + */ public String etag() { return etag; } + /** + * Returns the URI of this blob as a string. + */ public String selfLink() { return selfLink; } + /** + * Returns the MD5 hash of blob's data encoded in base64. + * + * @see + * Hashes and ETags: Best Practices + */ public String md5() { return Data.isNull(md5) ? null : md5; } + /** + * Returns the CRC32C checksum of blob's data as described in + * RFC 4960, Appendix B; encoded in + * base64 in big-endian order. + * + * @see + * Hashes and ETags: Best Practices + */ public String crc32c() { return Data.isNull(crc32c) ? null : crc32c; } + /** + * Returns the blob's media download link. + */ public String mediaLink() { return mediaLink; } + /** + * Returns blob's user provided metadata. + */ public Map metadata() { return metadata == null || Data.isNull(metadata) ? null : Collections.unmodifiableMap(metadata); } + /** + * Returns blob's data generation. Used for blob versioning. + */ public Long generation() { return generation; } + /** + * Returns blob's metageneration. Used for preconditions and for detecting changes in metadata. + * A metageneration number is only meaningful in the context of a particular generation of a + * particular blob. + */ public Long metageneration() { return metageneration; } + /** + * Returns the deletion time of the blob. + */ public Long deleteTime() { return deleteTime; } + /** + * Returns the last modification time of the blob's metadata. + */ public Long updateTime() { return updateTime; } + /** + * Returns a builder for the current blob. + */ public Builder toBuilder() { return new Builder() .blobId(blobId) @@ -444,14 +603,23 @@ public ObjectAccessControl apply(Acl acl) { return storageObject; } + /** + * Returns a {@code BlobInfo} builder where blob identity is set using the provided values. + */ public static Builder builder(BucketInfo bucketInfo, String name) { return builder(bucketInfo.name(), name); } + /** + * Returns a {@code BlobInfo} builder where blob identity is set using the provided values. + */ public static Builder builder(String bucket, String name) { return new Builder().blobId(BlobId.of(bucket, name)); } + /** + * Returns a {@code BlobInfo} builder where blob identity is set to the provided value. + */ public static Builder builder(BlobId blobId) { return new Builder().blobId(blobId); } diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobListResult.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobListResult.java deleted file mode 100644 index 9e6ec9dc5655..000000000000 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BlobListResult.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gcloud.storage; - -import static com.google.common.base.Preconditions.checkNotNull; - -import com.google.common.base.Function; -import com.google.common.collect.Iterators; - -import java.util.Iterator; -import java.util.Objects; - -/** - * Implementation of a paginated list of Google Cloud storage {@code Blob}. - */ -public class BlobListResult implements ListResult { - - private final ListResult infoList; - private final Storage storage; - - public BlobListResult(Storage storage, ListResult infoList) { - this.storage = checkNotNull(storage); - this.infoList = checkNotNull(infoList); - } - - @Override - public String nextPageCursor() { - return infoList.nextPageCursor(); - } - - @Override - public ListResult nextPage() { - ListResult nextPageInfoList = infoList.nextPage(); - if (nextPageInfoList == null) { - return null; - } - return new BlobListResult(storage, nextPageInfoList); - } - - @Override - public Iterator iterator() { - return Iterators.transform(infoList.iterator(), new Function() { - @Override - public Blob apply(BlobInfo info) { - return new Blob(storage, info); - } - }); - } - - @Override - public int hashCode() { - return Objects.hash(infoList); - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof BlobListResult)) { - return false; - } - BlobListResult other = (BlobListResult) obj; - return Objects.equals(infoList, other.infoList); - } -} diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Bucket.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Bucket.java index 8d696dc2ab6b..21aafd92b5d4 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Bucket.java +++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Bucket.java @@ -18,17 +18,28 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.gcloud.storage.Bucket.BucketSourceOption.toGetOptions; +import static com.google.gcloud.storage.Bucket.BucketSourceOption.toSourceOptions; +import com.google.common.base.Function; import com.google.common.base.MoreObjects; -import com.google.gcloud.storage.Storage.BlobSourceOption; +import com.google.common.collect.Iterators; +import com.google.gcloud.PageImpl; +import com.google.gcloud.Page; +import com.google.gcloud.spi.StorageRpc; +import com.google.gcloud.storage.Storage.BlobGetOption; import com.google.gcloud.storage.Storage.BlobTargetOption; import com.google.gcloud.storage.Storage.BlobWriteOption; -import com.google.gcloud.storage.Storage.BucketSourceOption; import com.google.gcloud.storage.Storage.BucketTargetOption; -import java.io.InputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.Serializable; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; +import java.util.Iterator; import java.util.List; import java.util.Objects; @@ -46,6 +57,142 @@ public final class Bucket { private final Storage storage; private final BucketInfo info; + private static class BlobPageFetcher implements PageImpl.NextPageFetcher { + + private static final long serialVersionUID = 3221100177471323801L; + + private final StorageOptions options; + private final Page infoPage; + + BlobPageFetcher(StorageOptions options, Page infoPage) { + this.options = options; + this.infoPage = infoPage; + } + + @Override + public Page nextPage() { + Page nextInfoPage = infoPage.nextPage(); + return new PageImpl(new BlobPageFetcher(options, nextInfoPage), + nextInfoPage.nextPageCursor(), new LazyBlobIterable(options, nextInfoPage.values())); + } + } + + private static class LazyBlobIterable implements Iterable, Serializable { + + private static final long serialVersionUID = -3092290247725378832L; + + private final StorageOptions options; + private Iterable infoIterable; + private transient Storage storage; + + public LazyBlobIterable(StorageOptions options, Iterable infoIterable) { + this.options = options; + this.infoIterable = infoIterable; + this.storage = options.service(); + } + + private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + this.storage = options.service(); + } + + @Override + public Iterator iterator() { + return Iterators.transform(infoIterable.iterator(), new Function() { + @Override + public Blob apply(BlobInfo blobInfo) { + return new Blob(storage, blobInfo); + } + }); + } + + @Override + public int hashCode() { + return Objects.hash(options, infoIterable); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof LazyBlobIterable)) { + return false; + } + LazyBlobIterable other = (LazyBlobIterable) obj; + return Objects.equals(options, other.options) + && Objects.equals(infoIterable, other.infoIterable); + } + } + + /** + * Class for specifying bucket source options when {@code Bucket} methods are used. + */ + public static class BucketSourceOption extends Option { + + private static final long serialVersionUID = 6928872234155522371L; + + private BucketSourceOption(StorageRpc.Option rpcOption) { + super(rpcOption, null); + } + + private Storage.BucketSourceOption toSourceOptions(BucketInfo bucketInfo) { + switch (rpcOption()) { + case IF_METAGENERATION_MATCH: + return Storage.BucketSourceOption.metagenerationMatch(bucketInfo.metageneration()); + case IF_METAGENERATION_NOT_MATCH: + return Storage.BucketSourceOption.metagenerationNotMatch(bucketInfo.metageneration()); + default: + throw new AssertionError("Unexpected enum value"); + } + } + + private Storage.BucketGetOption toGetOption(BucketInfo bucketInfo) { + switch (rpcOption()) { + case IF_METAGENERATION_MATCH: + return Storage.BucketGetOption.metagenerationMatch(bucketInfo.metageneration()); + case IF_METAGENERATION_NOT_MATCH: + return Storage.BucketGetOption.metagenerationNotMatch(bucketInfo.metageneration()); + default: + throw new AssertionError("Unexpected enum value"); + } + } + + /** + * Returns an option for bucket's metageneration match. If this option is used the request will + * fail if metageneration does not match. + */ + public static BucketSourceOption metagenerationMatch() { + return new BucketSourceOption(StorageRpc.Option.IF_METAGENERATION_MATCH); + } + + /** + * Returns an option for bucket's metageneration mismatch. If this option is used the request + * will fail if metageneration matches. + */ + public static BucketSourceOption metagenerationNotMatch() { + return new BucketSourceOption(StorageRpc.Option.IF_METAGENERATION_NOT_MATCH); + } + + static Storage.BucketSourceOption[] toSourceOptions(BucketInfo bucketInfo, + BucketSourceOption... options) { + Storage.BucketSourceOption[] convertedOptions = + new Storage.BucketSourceOption[options.length]; + int index = 0; + for (BucketSourceOption option : options) { + convertedOptions[index++] = option.toSourceOptions(bucketInfo); + } + return convertedOptions; + } + + static Storage.BucketGetOption[] toGetOptions(BucketInfo bucketInfo, + BucketSourceOption... options) { + Storage.BucketGetOption[] convertedOptions = new Storage.BucketGetOption[options.length]; + int index = 0; + for (BucketSourceOption option : options) { + convertedOptions[index++] = option.toGetOption(bucketInfo); + } + return convertedOptions; + } + } + /** * Constructs a {@code Bucket} object for the provided {@code BucketInfo}. The storage service is * used to issue requests. @@ -85,8 +232,11 @@ public BucketInfo info() { * @return true if this bucket exists, false otherwise * @throws StorageException upon failure */ - public boolean exists() { - return storage.get(info.name()) != null; + public boolean exists(BucketSourceOption... options) { + int length = options.length; + Storage.BucketGetOption[] getOptions = Arrays.copyOf(toGetOptions(info, options), length + 1); + getOptions[length] = Storage.BucketGetOption.fields(); + return storage.get(info.name(), getOptions) != null; } /** @@ -97,7 +247,7 @@ public boolean exists() { * @throws StorageException upon failure */ public Bucket reload(BucketSourceOption... options) { - return new Bucket(storage, storage.get(info.name(), options)); + return new Bucket(storage, storage.get(info.name(), toGetOptions(info, options))); } /** @@ -125,7 +275,7 @@ public Bucket update(BucketInfo bucketInfo, BucketTargetOption... options) { * @throws StorageException upon failure */ public boolean delete(BucketSourceOption... options) { - return storage.delete(info.name(), options); + return storage.delete(info.name(), toSourceOptions(info, options)); } /** @@ -134,8 +284,11 @@ public boolean delete(BucketSourceOption... options) { * @param options options for listing blobs * @throws StorageException upon failure */ - public ListResult list(Storage.BlobListOption... options) { - return new BlobListResult(storage, storage.list(info.name(), options)); + public Page list(Storage.BlobListOption... options) { + Page infoPage = storage.list(info.name(), options); + StorageOptions storageOptions = storage.options(); + return new PageImpl<>(new BlobPageFetcher(storageOptions, infoPage), infoPage.nextPageCursor(), + new LazyBlobIterable(storageOptions, infoPage.values())); } /** @@ -145,7 +298,7 @@ public ListResult list(Storage.BlobListOption... options) { * @param options blob search options * @throws StorageException upon failure */ - public Blob get(String blob, BlobSourceOption... options) { + public Blob get(String blob, BlobGetOption... options) { return new Blob(storage, storage.get(BlobId.of(info.name(), blob), options)); } diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BucketInfo.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BucketInfo.java index 5d69c54e0d96..d5a382446709 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BucketInfo.java +++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/BucketInfo.java @@ -33,7 +33,6 @@ import com.google.common.base.Function; import com.google.common.base.MoreObjects; import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; import com.google.gcloud.storage.Acl.Entity; import java.io.IOException; @@ -80,9 +79,15 @@ public com.google.api.services.storage.model.Bucket apply(BucketInfo bucketInfo) private final List cors; private final List acl; private final List defaultAcl; - private final Location location; - private final StorageClass storageClass; - + private final String location; + private final String storageClass; + + /** + * Base class for bucket's delete rules. Allows to configure automatic deletion of blobs and blobs + * versions. + * + * @see Object Lifecycle Management + */ public abstract static class DeleteRule implements Serializable { private static final long serialVersionUID = 3137971668395933033L; @@ -153,11 +158,23 @@ static DeleteRule fromPb(Rule rule) { } } + /** + * Delete rule class that sets a Time To Live for blobs in the bucket. + * + * @see Object Lifecycle Management + */ public static class AgeDeleteRule extends DeleteRule { private static final long serialVersionUID = 5697166940712116380L; private final int daysToLive; + /** + * Creates an {@code AgeDeleteRule} object. + * + * @param daysToLive blobs' Time To Live expressed in days. The time when the age condition is + * considered to be satisfied is computed by adding {@code daysToLive} days to the + * midnight following blob's creation time in UTC. + */ public AgeDeleteRule(int daysToLive) { super(Type.AGE); this.daysToLive = daysToLive; @@ -205,11 +222,22 @@ Rule toPb() { } } + /** + * Delete rule class for blobs in the bucket that have been created before a certain date. + * + * @see Object Lifecycle Management + */ public static class CreatedBeforeDeleteRule extends DeleteRule { private static final long serialVersionUID = 881692650279195867L; private final long timeMillis; + /** + * Creates an {@code CreatedBeforeDeleteRule} object. + * + * @param timeMillis a date in UTC. Blobs that have been created before midnight of the provided + * date meet the delete condition + */ public CreatedBeforeDeleteRule(long timeMillis) { super(Type.CREATE_BEFORE); this.timeMillis = timeMillis; @@ -225,11 +253,23 @@ void populateCondition(Rule.Condition condition) { } } + /** + * Delete rule class for versioned blobs. Specifies when to delete a blob's version according to + * the number of available newer versions for that blob. + * + * @see Object Lifecycle Management + */ public static class NumNewerVersionsDeleteRule extends DeleteRule { private static final long serialVersionUID = -1955554976528303894L; private final int numNewerVersions; + /** + * Creates an {@code NumNewerVersionsDeleteRule} object. + * + * @param numNewerVersions the number of newer versions. A blob's version meets the delete + * condition when {@code numNewerVersions} newer versions are available. + */ public NumNewerVersionsDeleteRule(int numNewerVersions) { super(Type.NUM_NEWER_VERSIONS); this.numNewerVersions = numNewerVersions; @@ -245,11 +285,22 @@ void populateCondition(Rule.Condition condition) { } } + /** + * Delete rule class to distinguish between live and archived blobs. + * + * @see Object Lifecycle Management + */ public static class IsLiveDeleteRule extends DeleteRule { private static final long serialVersionUID = -3502994563121313364L; private final boolean isLive; + /** + * Creates an {@code IsLiveDeleteRule} object. + * + * @param isLive if set to {@code true} live blobs meet the delete condition. If set to + * {@code false} delete condition is met by archived blobs. + */ public IsLiveDeleteRule(boolean isLive) { super(Type.IS_LIVE); this.isLive = isLive; @@ -265,134 +316,6 @@ void populateCondition(Rule.Condition condition) { } } - public static final class StorageClass implements Serializable { - - private static final long serialVersionUID = 374002156285326563L; - private static final ImmutableMap STRING_TO_OPTION; - private static final StorageClass NULL_VALUE = - new StorageClass(Data.nullOf(String.class)); - - private final String value; - - public enum Option { - DURABLE_REDUCED_AVAILABILITY, STANDARD; - - private final StorageClass storageClass; - - Option() { - storageClass = new StorageClass(name()); - } - } - - private StorageClass(String value) { - this.value = checkNotNull(value); - } - - @Override - public String toString() { - return value(); - } - - public String value() { - return value; - } - - public static StorageClass standard() { - return Option.STANDARD.storageClass; - } - - public static StorageClass durableReducedAvailability() { - return Option.DURABLE_REDUCED_AVAILABILITY.storageClass; - } - - public static StorageClass of(String value) { - Option option = STRING_TO_OPTION.get(value.toUpperCase()); - return option == null ? new StorageClass(value) : option.storageClass; - } - - static { - ImmutableMap.Builder map = ImmutableMap.builder(); - for (Option option : Option.values()) { - map.put(option.name(), option); - } - STRING_TO_OPTION = map.build(); - } - } - - public static final class Location implements Serializable { - - private static final long serialVersionUID = 9073107666838637662L; - private static final ImmutableMap STRING_TO_OPTION; - private static final Location NULL_VALUE = new Location(Data.nullOf(String.class)); - - private final String value; - - public enum Option { - US, EU, ASIA; - - private final Location location; - - Option() { - location = new Location(name()); - } - } - - private Location(String value) { - this.value = checkNotNull(value); - } - - @Override - public int hashCode() { - return Objects.hash(value); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null || getClass() != obj.getClass()) { - return false; - } - final Location other = (Location) obj; - return Objects.equals(this.value, other.value); - } - - @Override - public String toString() { - return value(); - } - - public String value() { - return value; - } - - public static Location us() { - return Option.US.location; - } - - public static Location eu() { - return Option.EU.location; - } - - public static Location asia() { - return Option.ASIA.location; - } - - public static Location of(String value) { - Option option = STRING_TO_OPTION.get(value.toUpperCase()); - return option == null ? new Location(value) : option.location; - } - - static { - ImmutableMap.Builder map = ImmutableMap.builder(); - for (Option option : Option.values()) { - map.put(option.name(), option); - } - STRING_TO_OPTION = map.build(); - } - } - public static final class Builder { private String id; @@ -403,8 +326,8 @@ public static final class Builder { private String indexPage; private String notFoundPage; private ImmutableList deleteRules; - private StorageClass storageClass; - private Location location; + private String storageClass; + private String location; private String etag; private Long createTime; private Long metageneration; @@ -414,6 +337,9 @@ public static final class Builder { private Builder() {} + /** + * Sets the bucket's name. + */ public Builder name(String name) { this.name = checkNotNull(name); return this; @@ -434,33 +360,59 @@ Builder selfLink(String selfLink) { return this; } + /** + * Sets whether versioning should be enabled for this bucket. When set to true, versioning is + * fully enabled. + */ public Builder versioningEnabled(Boolean enable) { this.versioningEnabled = firstNonNull(enable, Data.nullOf(Boolean.class)); return this; } + /** + * Sets the bucket's website index page. Behaves as the bucket's directory index where missing + * blobs are treated as potential directories. + */ public Builder indexPage(String indexPage) { this.indexPage = indexPage; return this; } + /** + * Sets the custom object to return when a requested resource is not found. + */ public Builder notFoundPage(String notFoundPage) { this.notFoundPage = notFoundPage; return this; } + /** + * Sets the bucket's lifecycle configuration as a number of delete rules. + * + * @see Lifecycle Management + */ public Builder deleteRules(Iterable rules) { this.deleteRules = rules != null ? ImmutableList.copyOf(rules) : null; return this; } - public Builder storageClass(StorageClass storageClass) { - this.storageClass = firstNonNull(storageClass, StorageClass.NULL_VALUE); + /** + * Sets the bucket's storage class. This defines how blobs in the bucket are stored and + * determines the SLA and the cost of storage. A list of supported values is available + * here. + */ + public Builder storageClass(String storageClass) { + this.storageClass = storageClass; return this; } - public Builder location(Location location) { - this.location = firstNonNull(location, Location.NULL_VALUE); + /** + * Sets the bucket's location. Data for blobs in the bucket resides in physical storage within + * this region. A list of supported values is available + * here. + */ + public Builder location(String location) { + this.location = location; return this; } @@ -479,21 +431,43 @@ Builder metageneration(Long metageneration) { return this; } + /** + * Sets the bucket's Cross-Origin Resource Sharing (CORS) configuration. + * + * @see + * Cross-Origin Resource Sharing (CORS) + */ public Builder cors(Iterable cors) { this.cors = cors != null ? ImmutableList.copyOf(cors) : null; return this; } + /** + * Sets the bucket's access control configuration. + * + * @see + * About Access Control Lists + */ public Builder acl(Iterable acl) { this.acl = acl != null ? ImmutableList.copyOf(acl) : null; return this; } + /** + * Sets the default access control configuration to apply to bucket's blobs when no other + * configuration is specified. + * + * @see + * About Access Control Lists + */ public Builder defaultAcl(Iterable acl) { this.defaultAcl = acl != null ? ImmutableList.copyOf(acl) : null; return this; } + /** + * Creates a {@code BucketInfo} object. + */ public BucketInfo build() { checkNotNull(name); return new BucketInfo(this); @@ -519,70 +493,141 @@ private BucketInfo(Builder builder) { deleteRules = builder.deleteRules; } + /** + * Returns the bucket's id. + */ public String id() { return id; } + /** + * Returns the bucket's name. + */ public String name() { return name; } + /** + * Returns the bucket's owner. This is always the project team's owner group. + */ public Entity owner() { return owner; } + /** + * Returns the URI of this bucket as a string. + */ public String selfLink() { return selfLink; } + /** + * Returns {@code true} if versioning is fully enabled for this bucket, {@code false} otherwise. + */ public Boolean versioningEnabled() { return Data.isNull(versioningEnabled) ? null : versioningEnabled; } + /** + * Returns bucket's website index page. Behaves as the bucket's directory index where missing + * blobs are treated as potential directories. + */ public String indexPage() { return indexPage; } + /** + * Returns the custom object to return when a requested resource is not found. + */ public String notFoundPage() { return notFoundPage; } + /** + * Returns bucket's lifecycle configuration as a number of delete rules. + * + * @see Lifecycle Management + */ public List deleteRules() { return deleteRules; } + /** + * Returns HTTP 1.1 Entity tag for the bucket. + * + * @see Entity Tags + */ public String etag() { return etag; } + /** + * Returns the time at which the bucket was created. + */ public Long createTime() { return createTime; } + /** + * Returns the metadata generation of this bucket. + */ public Long metageneration() { return metageneration; } - public Location location() { - return location == null || Data.isNull(location.value) ? null : location; - } - - public StorageClass storageClass() { - return storageClass == null || Data.isNull(storageClass.value) ? null : storageClass; - } - + /** + * Returns the bucket's location. Data for blobs in the bucket resides in physical storage within + * this region. + * + * @see Bucket Locations + */ + public String location() { + return location; + } + + /** + * Returns the bucket's storage class. This defines how blobs in the bucket are stored and + * determines the SLA and the cost of storage. + * + * @see Storage Classes + */ + public String storageClass() { + return storageClass; + } + + /** + * Returns the bucket's Cross-Origin Resource Sharing (CORS) configuration. + * + * @see + * Cross-Origin Resource Sharing (CORS) + */ public List cors() { return cors; } + /** + * Returns the bucket's access control configuration. + * + * @see + * About Access Control Lists + */ public List acl() { return acl; } + /** + * Returns the default access control configuration for this bucket's blobs. + * + * @see + * About Access Control Lists + */ public List defaultAcl() { return defaultAcl; } + /** + * Returns a builder for the current bucket. + */ public Builder toBuilder() { return new Builder() .name(name) @@ -633,10 +678,10 @@ com.google.api.services.storage.model.Bucket toPb() { bucketPb.setMetageneration(metageneration); } if (location != null) { - bucketPb.setLocation(location.value()); + bucketPb.setLocation(location); } if (storageClass != null) { - bucketPb.setStorageClass(storageClass.value()); + bucketPb.setStorageClass(storageClass); } if (cors != null) { bucketPb.setCors(transform(cors, Cors.TO_PB_FUNCTION)); @@ -683,10 +728,16 @@ public Rule apply(DeleteRule deleteRule) { return bucketPb; } + /** + * Creates a {@code BucketInfo} object for the provided bucket name. + */ public static BucketInfo of(String name) { return builder(name).build(); } + /** + * Returns a {@code BucketInfo} builder where the bucket's name is set to the provided name. + */ public static Builder builder(String name) { return new Builder().name(name); } @@ -709,10 +760,10 @@ static BucketInfo fromPb(com.google.api.services.storage.model.Bucket bucketPb) builder.createTime(bucketPb.getTimeCreated().getValue()); } if (bucketPb.getLocation() != null) { - builder.location(Location.of(bucketPb.getLocation())); + builder.location(bucketPb.getLocation()); } if (bucketPb.getStorageClass() != null) { - builder.storageClass(StorageClass.of(bucketPb.getStorageClass())); + builder.storageClass(bucketPb.getStorageClass()); } if (bucketPb.getCors() != null) { builder.cors(transform(bucketPb.getCors(), Cors.FROM_PB_FUNCTION)); diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/CopyWriter.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/CopyWriter.java index 142f8d4b6de7..5f2632b2acde 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/CopyWriter.java +++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/CopyWriter.java @@ -79,14 +79,14 @@ public long blobSize() { } /** - * Returns {@code true} of blob rewrite finished, {@code false} otherwise. + * Returns {@code true} if blob copy has finished, {@code false} otherwise. */ public boolean isDone() { return rewriteResponse.isDone; } /** - * Returns the number of bytes copied. + * Returns the number of bytes copied. */ public long totalBytesCopied() { return rewriteResponse.totalBytesRewritten; diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Cors.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Cors.java index a94359f17a79..bcbbd1030dbc 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Cors.java +++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Cors.java @@ -33,6 +33,9 @@ /** * Cross-Origin Resource Sharing (CORS) configuration for a bucket. + * + * @see + * Cross-Origin Resource Sharing (CORS) */ public final class Cors implements Serializable { @@ -57,6 +60,9 @@ public Bucket.Cors apply(Cors cors) { private final ImmutableList origins; private final ImmutableList responseHeaders; + /** + * Class for a CORS origin. + */ public static final class Origin implements Serializable { private static final long serialVersionUID = -4447958124895577993L; @@ -69,10 +75,16 @@ private Origin(String value) { this.value = checkNotNull(value); } + /** + * Returns an {@code Origin} object for all possible origins. + */ public static Origin any() { return ANY; } + /** + * Returns an {@code Origin} object for the given scheme, host and port. + */ public static Origin of(String scheme, String host, int port) { try { return of(new URI(scheme, null, host, port, null, null, null).toString()); @@ -81,6 +93,9 @@ public static Origin of(String scheme, String host, int port) { } } + /** + * Creates an {@code Origin} object for the provided value. + */ public static Origin of(String value) { if (ANY_URI.equals(value)) { return any(); @@ -111,6 +126,9 @@ public String value() { } } + /** + * CORS configuration builder. + */ public static final class Builder { private Integer maxAgeSeconds; @@ -120,26 +138,42 @@ public static final class Builder { private Builder() {} + /** + * Sets the max time in seconds in which a client can issue requests before sending a new + * preflight request. + */ public Builder maxAgeSeconds(Integer maxAgeSeconds) { this.maxAgeSeconds = maxAgeSeconds; return this; } + /** + * Sets the HTTP methods supported by this CORS configuration. + */ public Builder methods(Iterable methods) { this.methods = methods != null ? ImmutableList.copyOf(methods) : null; return this; } + /** + * Sets the origins for this CORS configuration. + */ public Builder origins(Iterable origins) { this.origins = origins != null ? ImmutableList.copyOf(origins) : null; return this; } + /** + * Sets the response headers supported by this CORS configuration. + */ public Builder responseHeaders(Iterable headers) { this.responseHeaders = headers != null ? ImmutableList.copyOf(headers) : null; return this; } + /** + * Creates a CORS configuration. + */ public Cors build() { return new Cors(this); } @@ -152,22 +186,38 @@ private Cors(Builder builder) { this.responseHeaders = builder.responseHeaders; } + /** + * Returns the max time in seconds in which a client can issue requests before sending a new + * preflight request. + */ public Integer maxAgeSeconds() { return maxAgeSeconds; } + /** + * Returns the HTTP methods supported by this CORS configuration. + */ public List methods() { return methods; } + /** + * Returns the origins in this CORS configuration. + */ public List origins() { return origins; } + /** + * Returns the response headers supported by this CORS configuration. + */ public List responseHeaders() { return responseHeaders; } + /** + * Returns a builder for this CORS configuration. + */ public Builder toBuilder() { return builder() .maxAgeSeconds(maxAgeSeconds) @@ -193,6 +243,9 @@ public boolean equals(Object obj) { && Objects.equals(responseHeaders, other.responseHeaders); } + /** + * Returns a CORS configuration builder. + */ public static Builder builder() { return new Builder(); } diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/ListResult.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/ListResult.java deleted file mode 100644 index 62b1f442310c..000000000000 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/ListResult.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gcloud.storage; - -/** - * Interface for Google Cloud storage list result. - */ -public interface ListResult extends Iterable { - - /** - * Returns the cursor for the nextPage or {@code null} if no more results. - */ - String nextPageCursor(); - - /** - * Returns the results of the nextPage or {@code null} if no more result. - */ - ListResult nextPage(); - -} diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Storage.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Storage.java index 9bc971a09dba..2e95e69aa445 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Storage.java +++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/Storage.java @@ -19,11 +19,14 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; +import com.google.common.base.Joiner; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; +import com.google.common.collect.Sets; import com.google.gcloud.AuthCredentials.ServiceAccountAuthCredentials; import com.google.gcloud.Service; +import com.google.gcloud.Page; import com.google.gcloud.spi.StorageRpc; import com.google.gcloud.spi.StorageRpc.Tuple; @@ -32,6 +35,7 @@ import java.net.URL; import java.util.Arrays; import java.util.Collections; +import java.util.HashSet; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; @@ -69,6 +73,92 @@ String entry() { } } + enum BucketField { + ID("id"), + SELF_LINK("selfLink"), + NAME("name"), + TIME_CREATED("timeCreated"), + METAGENERATION("metageneration"), + ACL("acl"), + DEFAULT_OBJECT_ACL("defaultObjectAcl"), + OWNER("owner"), + LOCATION("location"), + WEBSITE("website"), + VERSIONING("versioning"), + CORS("cors"), + STORAGE_CLASS("storageClass"), + ETAG("etag"); + + private final String selector; + + BucketField(String selector) { + this.selector = selector; + } + + public String selector() { + return selector; + } + + static String selector(BucketField... fields) { + HashSet fieldStrings = Sets.newHashSetWithExpectedSize(fields.length + 1); + fieldStrings.add(NAME.selector()); + for (BucketField field : fields) { + fieldStrings.add(field.selector()); + } + return Joiner.on(',').join(fieldStrings); + } + } + + enum BlobField { + ACL("acl"), + BUCKET("bucket"), + CACHE_CONTROL("cacheControl"), + COMPONENT_COUNT("componentCount"), + CONTENT_DISPOSITION("contentDisposition"), + CONTENT_ENCODING("contentEncoding"), + CONTENT_LANGUAGE("contentLanguage"), + CONTENT_TYPE("contentType"), + CRC32C("crc32c"), + ETAG("etag"), + GENERATION("generation"), + ID("id"), + KIND("kind"), + MD5HASH("md5Hash"), + MEDIA_LINK("mediaLink"), + METADATA("metadata"), + METAGENERATION("metageneration"), + NAME("name"), + OWNER("owner"), + SELF_LINK("selfLink"), + SIZE("size"), + STORAGE_CLASS("storageClass"), + TIME_DELETED("timeDeleted"), + UPDATED("updated"); + + private final String selector; + + BlobField(String selector) { + this.selector = selector; + } + + public String selector() { + return selector; + } + + static String selector(BlobField... fields) { + HashSet fieldStrings = Sets.newHashSetWithExpectedSize(fields.length + 2); + fieldStrings.add(BUCKET.selector()); + fieldStrings.add(NAME.selector()); + for (BlobField field : fields) { + fieldStrings.add(field.selector()); + } + return Joiner.on(',').join(fieldStrings); + } + } + + /** + * Class for specifying bucket target options. + */ class BucketTargetOption extends Option { private static final long serialVersionUID = -5880204616982900975L; @@ -81,23 +171,40 @@ private BucketTargetOption(StorageRpc.Option rpcOption) { this(rpcOption, null); } + /** + * Returns an option for specifying bucket's predefined ACL configuration. + */ public static BucketTargetOption predefinedAcl(PredefinedAcl acl) { return new BucketTargetOption(StorageRpc.Option.PREDEFINED_ACL, acl.entry()); } + /** + * Returns an option for specifying bucket's default ACL configuration for blobs. + */ public static BucketTargetOption predefinedDefaultObjectAcl(PredefinedAcl acl) { return new BucketTargetOption(StorageRpc.Option.PREDEFINED_DEFAULT_OBJECT_ACL, acl.entry()); } + /** + * Returns an option for bucket's metageneration match. If this option is used the request will + * fail if metageneration does not match. + */ public static BucketTargetOption metagenerationMatch() { return new BucketTargetOption(StorageRpc.Option.IF_METAGENERATION_MATCH); } + /** + * Returns an option for bucket's metageneration mismatch. If this option is used the request + * will fail if metageneration matches. + */ public static BucketTargetOption metagenerationNotMatch() { return new BucketTargetOption(StorageRpc.Option.IF_METAGENERATION_NOT_MATCH); } } + /** + * Class for specifying bucket source options. + */ class BucketSourceOption extends Option { private static final long serialVersionUID = 5185657617120212117L; @@ -106,15 +213,68 @@ private BucketSourceOption(StorageRpc.Option rpcOption, long metageneration) { super(rpcOption, metageneration); } + /** + * Returns an option for bucket's metageneration match. If this option is used the request will + * fail if bucket's metageneration does not match the provided value. + */ public static BucketSourceOption metagenerationMatch(long metageneration) { return new BucketSourceOption(StorageRpc.Option.IF_METAGENERATION_MATCH, metageneration); } + /** + * Returns an option for bucket's metageneration mismatch. If this option is used the request + * will fail if bucket's metageneration matches the provided value. + */ public static BucketSourceOption metagenerationNotMatch(long metageneration) { return new BucketSourceOption(StorageRpc.Option.IF_METAGENERATION_NOT_MATCH, metageneration); } } + /** + * Class for specifying bucket source options. + */ + class BucketGetOption extends Option { + + private static final long serialVersionUID = 1901844869484087395L; + + private BucketGetOption(StorageRpc.Option rpcOption, long metageneration) { + super(rpcOption, metageneration); + } + + private BucketGetOption(StorageRpc.Option rpcOption, String value) { + super(rpcOption, value); + } + + /** + * Returns an option for bucket's metageneration match. If this option is used the request will + * fail if bucket's metageneration does not match the provided value. + */ + public static BucketGetOption metagenerationMatch(long metageneration) { + return new BucketGetOption(StorageRpc.Option.IF_METAGENERATION_MATCH, metageneration); + } + + /** + * Returns an option for bucket's metageneration mismatch. If this option is used the request + * will fail if bucket's metageneration matches the provided value. + */ + public static BucketGetOption metagenerationNotMatch(long metageneration) { + return new BucketGetOption(StorageRpc.Option.IF_METAGENERATION_NOT_MATCH, metageneration); + } + + /** + * Returns an option to specify the bucket's fields to be returned by the RPC call. If this + * option is not provided all bucket's fields are returned. {@code BucketGetOption.fields}) can + * be used to specify only the fields of interest. Bucket name is always returned, even if not + * specified. + */ + public static BucketGetOption fields(BucketField... fields) { + return new BucketGetOption(StorageRpc.Option.FIELDS, BucketField.selector(fields)); + } + } + + /** + * Class for specifying blob target options. + */ class BlobTargetOption extends Option { private static final long serialVersionUID = 214616862061934846L; @@ -127,26 +287,48 @@ private BlobTargetOption(StorageRpc.Option rpcOption) { this(rpcOption, null); } + /** + * Returns an option for specifying blob's predefined ACL configuration. + */ public static BlobTargetOption predefinedAcl(PredefinedAcl acl) { return new BlobTargetOption(StorageRpc.Option.PREDEFINED_ACL, acl.entry()); } + /** + * Returns an option that causes an operation to succeed only if the target blob does not exist. + */ public static BlobTargetOption doesNotExist() { return new BlobTargetOption(StorageRpc.Option.IF_GENERATION_MATCH, 0L); } + /** + * Returns an option for blob's data generation match. If this option is used the request will + * fail if generation does not match. + */ public static BlobTargetOption generationMatch() { return new BlobTargetOption(StorageRpc.Option.IF_GENERATION_MATCH); } + /** + * Returns an option for blob's data generation mismatch. If this option is used the request + * will fail if generation matches. + */ public static BlobTargetOption generationNotMatch() { return new BlobTargetOption(StorageRpc.Option.IF_GENERATION_NOT_MATCH); } + /** + * Returns an option for blob's metageneration match. If this option is used the request will + * fail if metageneration does not match. + */ public static BlobTargetOption metagenerationMatch() { return new BlobTargetOption(StorageRpc.Option.IF_METAGENERATION_MATCH); } + /** + * Returns an option for blob's metageneration mismatch. If this option is used the request will + * fail if metageneration matches. + */ public static BlobTargetOption metagenerationNotMatch() { return new BlobTargetOption(StorageRpc.Option.IF_METAGENERATION_NOT_MATCH); } @@ -172,6 +354,9 @@ static Tuple convert(BlobInfo info, BlobWriteOptio } } + /** + * Class for specifying blob write options. + */ class BlobWriteOption implements Serializable { private static final long serialVersionUID = -3880421670966224580L; @@ -218,39 +403,72 @@ public boolean equals(Object obj) { return this.option == other.option && Objects.equals(this.value, other.value); } + /** + * Returns an option for specifying blob's predefined ACL configuration. + */ public static BlobWriteOption predefinedAcl(PredefinedAcl acl) { return new BlobWriteOption(Option.PREDEFINED_ACL, acl.entry()); } + /** + * Returns an option that causes an operation to succeed only if the target blob does not exist. + */ public static BlobWriteOption doesNotExist() { return new BlobWriteOption(Option.IF_GENERATION_MATCH, 0L); } + /** + * Returns an option for blob's data generation match. If this option is used the request will + * fail if generation does not match. + */ public static BlobWriteOption generationMatch() { return new BlobWriteOption(Option.IF_GENERATION_MATCH); } + /** + * Returns an option for blob's data generation mismatch. If this option is used the request + * will fail if generation matches. + */ public static BlobWriteOption generationNotMatch() { return new BlobWriteOption(Option.IF_GENERATION_NOT_MATCH); } + /** + * Returns an option for blob's metageneration match. If this option is used the request will + * fail if metageneration does not match. + */ public static BlobWriteOption metagenerationMatch() { return new BlobWriteOption(Option.IF_METAGENERATION_MATCH); } + /** + * Returns an option for blob's metageneration mismatch. If this option is used the request will + * fail if metageneration matches. + */ public static BlobWriteOption metagenerationNotMatch() { return new BlobWriteOption(Option.IF_METAGENERATION_NOT_MATCH); } + /** + * Returns an option for blob's data MD5 hash match. If this option is used the request will + * fail if blobs' data MD5 hash does not match. + */ public static BlobWriteOption md5Match() { return new BlobWriteOption(Option.IF_MD5_MATCH, true); } + /** + * Returns an option for blob's data CRC32C checksum match. If this option is used the request + * will fail if blobs' data CRC32C checksum does not match. + */ public static BlobWriteOption crc32cMatch() { return new BlobWriteOption(Option.IF_CRC32C_MATCH, true); } } + /** + * Class for specifying blob source options. + */ class BlobSourceOption extends Option { private static final long serialVersionUID = -3712768261070182991L; @@ -259,23 +477,100 @@ private BlobSourceOption(StorageRpc.Option rpcOption, long value) { super(rpcOption, value); } + /** + * Returns an option for blob's data generation match. If this option is used the request will + * fail if blob's generation does not match the provided value. + */ public static BlobSourceOption generationMatch(long generation) { return new BlobSourceOption(StorageRpc.Option.IF_GENERATION_MATCH, generation); } + /** + * Returns an option for blob's data generation mismatch. If this option is used the request + * will fail if blob's generation matches the provided value. + */ public static BlobSourceOption generationNotMatch(long generation) { return new BlobSourceOption(StorageRpc.Option.IF_GENERATION_NOT_MATCH, generation); } + /** + * Returns an option for blob's metageneration match. If this option is used the request will + * fail if blob's metageneration does not match the provided value. + */ public static BlobSourceOption metagenerationMatch(long metageneration) { return new BlobSourceOption(StorageRpc.Option.IF_METAGENERATION_MATCH, metageneration); } + /** + * Returns an option for blob's metageneration mismatch. If this option is used the request will + * fail if blob's metageneration matches the provided value. + */ public static BlobSourceOption metagenerationNotMatch(long metageneration) { return new BlobSourceOption(StorageRpc.Option.IF_METAGENERATION_NOT_MATCH, metageneration); } } + /** + * Class for specifying blob get options. + */ + class BlobGetOption extends Option { + + private static final long serialVersionUID = 803817709703661480L; + + private BlobGetOption(StorageRpc.Option rpcOption, long value) { + super(rpcOption, value); + } + + private BlobGetOption(StorageRpc.Option rpcOption, String value) { + super(rpcOption, value); + } + + /** + * Returns an option for blob's data generation match. If this option is used the request will + * fail if blob's generation does not match the provided value. + */ + public static BlobGetOption generationMatch(long generation) { + return new BlobGetOption(StorageRpc.Option.IF_GENERATION_MATCH, generation); + } + + /** + * Returns an option for blob's data generation mismatch. If this option is used the request + * will fail if blob's generation matches the provided value. + */ + public static BlobGetOption generationNotMatch(long generation) { + return new BlobGetOption(StorageRpc.Option.IF_GENERATION_NOT_MATCH, generation); + } + + /** + * Returns an option for blob's metageneration match. If this option is used the request will + * fail if blob's metageneration does not match the provided value. + */ + public static BlobGetOption metagenerationMatch(long metageneration) { + return new BlobGetOption(StorageRpc.Option.IF_METAGENERATION_MATCH, metageneration); + } + + /** + * Returns an option for blob's metageneration mismatch. If this option is used the request will + * fail if blob's metageneration matches the provided value. + */ + public static BlobGetOption metagenerationNotMatch(long metageneration) { + return new BlobGetOption(StorageRpc.Option.IF_METAGENERATION_NOT_MATCH, metageneration); + } + + /** + * Returns an option to specify the blob's fields to be returned by the RPC call. If this option + * is not provided all blob's fields are returned. {@code BlobGetOption.fields}) can be used to + * specify only the fields of interest. Blob name and bucket are always returned, even if not + * specified. + */ + public static BlobGetOption fields(BlobField... fields) { + return new BlobGetOption(StorageRpc.Option.FIELDS, BlobField.selector(fields)); + } + } + + /** + * Class for specifying bucket list options. + */ class BucketListOption extends Option { private static final long serialVersionUID = 8754017079673290353L; @@ -284,19 +579,44 @@ private BucketListOption(StorageRpc.Option option, Object value) { super(option, value); } + /** + * Returns an option to specify the maximum number of buckets to be returned. + */ public static BucketListOption maxResults(long maxResults) { return new BucketListOption(StorageRpc.Option.MAX_RESULTS, maxResults); } + /** + * Returns an option to specify the page token from which to start listing buckets. + */ public static BucketListOption startPageToken(String pageToken) { return new BucketListOption(StorageRpc.Option.PAGE_TOKEN, pageToken); } + /** + * Returns an option to set a prefix to filter results to buckets whose names begin with this + * prefix. + */ public static BucketListOption prefix(String prefix) { return new BucketListOption(StorageRpc.Option.PREFIX, prefix); } + + /** + * Returns an option to specify the bucket's fields to be returned by the RPC call. If this + * option is not provided all bucket's fields are returned. {@code BucketListOption.fields}) can + * be used to specify only the fields of interest. Bucket name is always returned, even if not + * specified. + */ + public static BucketListOption fields(BucketField... fields) { + StringBuilder builder = new StringBuilder(); + builder.append("items(").append(BucketField.selector(fields)).append(")"); + return new BucketListOption(StorageRpc.Option.FIELDS, builder.toString()); + } } + /** + * Class for specifying blob list options. + */ class BlobListOption extends Option { private static final long serialVersionUID = 9083383524788661294L; @@ -305,23 +625,51 @@ private BlobListOption(StorageRpc.Option option, Object value) { super(option, value); } + /** + * Returns an option to specify the maximum number of blobs to be returned. + */ public static BlobListOption maxResults(long maxResults) { return new BlobListOption(StorageRpc.Option.MAX_RESULTS, maxResults); } + /** + * Returns an option to specify the page token from which to start listing blobs. + */ public static BlobListOption startPageToken(String pageToken) { return new BlobListOption(StorageRpc.Option.PAGE_TOKEN, pageToken); } + /** + * Returns an option to set a prefix to filter results to blobs whose names begin with this + * prefix. + */ public static BlobListOption prefix(String prefix) { return new BlobListOption(StorageRpc.Option.PREFIX, prefix); } + /** + * Returns an option to specify whether blob listing should include subdirectories or not. + */ public static BlobListOption recursive(boolean recursive) { return new BlobListOption(StorageRpc.Option.DELIMITER, recursive); } + + /** + * Returns an option to specify the blob's fields to be returned by the RPC call. If this option + * is not provided all blob's fields are returned. {@code BlobListOption.fields}) can be used to + * specify only the fields of interest. Blob name and bucket are always returned, even if not + * specified. + */ + public static BlobListOption fields(BlobField... fields) { + StringBuilder builder = new StringBuilder(); + builder.append("items(").append(BlobField.selector(fields)).append(")"); + return new BlobListOption(StorageRpc.Option.FIELDS, builder.toString()); + } } + /** + * Class for specifying signed URL options. + */ class SignUrlOption implements Serializable { private static final long serialVersionUID = 7850569877451099267L; @@ -381,6 +729,12 @@ public static SignUrlOption serviceAccount(ServiceAccountAuthCredentials credent } } + /** + * A class to contain all information needed for a Google Cloud Storage Compose operation. + * + * @see + * Compose Operation + */ class ComposeRequest implements Serializable { private static final long serialVersionUID = -7385681353748590911L; @@ -389,6 +743,9 @@ class ComposeRequest implements Serializable { private final BlobInfo target; private final List targetOptions; + /** + * Class for Compose source blobs. + */ public static class SourceBlob implements Serializable { private static final long serialVersionUID = 4094962795951990439L; @@ -420,6 +777,9 @@ public static class Builder { private final Set targetOptions = new LinkedHashSet<>(); private BlobInfo target; + /** + * Add source blobs for compose operation. + */ public Builder addSource(Iterable blobs) { for (String blob : blobs) { sourceBlobs.add(new SourceBlob(blob)); @@ -427,6 +787,9 @@ public Builder addSource(Iterable blobs) { return this; } + /** + * Add source blobs for compose operation. + */ public Builder addSource(String... blobs) { return addSource(Arrays.asList(blobs)); } @@ -439,21 +802,33 @@ public Builder addSource(String blob, long generation) { return this; } + /** + * Sets compose operation's target blob. + */ public Builder target(BlobInfo target) { this.target = target; return this; } + /** + * Sets compose operation's target blob options. + */ public Builder targetOptions(BlobTargetOption... options) { Collections.addAll(targetOptions, options); return this; } + /** + * Sets compose operation's target blob options. + */ public Builder targetOptions(Iterable options) { Iterables.addAll(targetOptions, options); return this; } + /** + * Creates a {@code ComposeRequest} object. + */ public ComposeRequest build() { checkArgument(!sourceBlobs.isEmpty()); checkNotNull(target); @@ -467,31 +842,59 @@ private ComposeRequest(Builder builder) { targetOptions = ImmutableList.copyOf(builder.targetOptions); } + /** + * Returns compose operation's source blobs. + */ public List sourceBlobs() { return sourceBlobs; } + /** + * Returns compose operation's target blob. + */ public BlobInfo target() { return target; } + /** + * Returns compose operation's target blob's options. + */ public List targetOptions() { return targetOptions; } + /** + * Creates a {@code ComposeRequest} object. + * + * @param sources source blobs names + * @param target target blob + */ public static ComposeRequest of(Iterable sources, BlobInfo target) { return builder().target(target).addSource(sources).build(); } + /** + * Creates a {@code ComposeRequest} object. + * + * @param bucket name of the bucket where the compose operation takes place + * @param sources source blobs names + * @param target target blob name + */ public static ComposeRequest of(String bucket, Iterable sources, String target) { return of(sources, BlobInfo.builder(BlobId.of(bucket, target)).build()); } + /** + * Returns a {@code ComposeRequest} builder. + */ public static Builder builder() { return new Builder(); } } + /** + * A class to contain all information needed for a Google Cloud Storage Copy operation. + */ class CopyRequest implements Serializable { private static final long serialVersionUID = -4498650529476219937L; @@ -605,7 +1008,7 @@ public Builder megabytesCopiedPerChunk(Long megabytesCopiedPerChunk) { } /** - * Creates a {@code CopyRequest}. + * Creates a {@code CopyRequest} object. */ public CopyRequest build() { checkNotNull(source); @@ -623,7 +1026,7 @@ private CopyRequest(Builder builder) { } /** - * Returns the blob to rewrite, as a {@link BlobId}. + * Returns the blob to copy, as a {@link BlobId}. */ public BlobId source() { return source; @@ -637,7 +1040,7 @@ public List sourceOptions() { } /** - * Returns the rewrite target. + * Returns the {@link BlobInfo} for the target blob. */ public BlobInfo target() { return target; @@ -799,21 +1202,21 @@ private static void checkContentType(BlobInfo blobInfo) throws IllegalArgumentEx * * @throws StorageException upon failure */ - BucketInfo get(String bucket, BucketSourceOption... options); + BucketInfo get(String bucket, BucketGetOption... options); /** * Return the requested blob or {@code null} if not found. * * @throws StorageException upon failure */ - BlobInfo get(String bucket, String blob, BlobSourceOption... options); + BlobInfo get(String bucket, String blob, BlobGetOption... options); /** * Return the requested blob or {@code null} if not found. * * @throws StorageException upon failure */ - BlobInfo get(BlobId blob, BlobSourceOption... options); + BlobInfo get(BlobId blob, BlobGetOption... options); /** * Return the requested blob or {@code null} if not found. @@ -827,14 +1230,14 @@ private static void checkContentType(BlobInfo blobInfo) throws IllegalArgumentEx * * @throws StorageException upon failure */ - ListResult list(BucketListOption... options); + Page list(BucketListOption... options); /** * List the bucket's blobs. * * @throws StorageException upon failure */ - ListResult list(String bucket, BlobListOption... options); + Page list(String bucket, BlobListOption... options); /** * Update bucket information. diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageException.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageException.java index e354e3a6d427..c1075ae28c8b 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageException.java +++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageException.java @@ -16,8 +16,10 @@ package com.google.gcloud.storage; +import com.google.gcloud.BaseServiceException; import com.google.gcloud.RetryHelper; import com.google.gcloud.RetryHelper.RetryHelperException; +import com.google.gcloud.RetryHelper.RetryInterruptedException; /** * Storage service exception. @@ -25,29 +27,13 @@ * @see Google Cloud * Storage error codes */ -public class StorageException extends RuntimeException { +public class StorageException extends BaseServiceException { - private static final long serialVersionUID = -3748432005065428084L; + private static final long serialVersionUID = 8088235105953640145L; private static final int UNKNOWN_CODE = -1; - private final int code; - private final boolean retryable; - public StorageException(int code, String message, boolean retryable) { - super(message); - this.code = code; - this.retryable = retryable; - } - - /** - * Returns the code associated with this exception. - */ - public int code() { - return code; - } - - public boolean retryable() { - return retryable; + super(code, message, retryable); } /** diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageImpl.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageImpl.java index ab85dc8b4609..4c85113e940e 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageImpl.java +++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageImpl.java @@ -44,10 +44,12 @@ import com.google.common.io.BaseEncoding; import com.google.common.primitives.Ints; import com.google.gcloud.AuthCredentials.ServiceAccountAuthCredentials; +import com.google.gcloud.PageImpl; import com.google.gcloud.BaseService; import com.google.gcloud.ExceptionHandler; import com.google.gcloud.ExceptionHandler.Interceptor; import com.google.gcloud.RetryHelper.RetryHelperException; +import com.google.gcloud.Page; import com.google.gcloud.spi.StorageRpc; import com.google.gcloud.spi.StorageRpc.RewriteResponse; import com.google.gcloud.spi.StorageRpc.Tuple; @@ -165,7 +167,7 @@ public StorageObject call() { } @Override - public BucketInfo get(String bucket, BucketSourceOption... options) { + public BucketInfo get(String bucket, BucketGetOption... options) { final com.google.api.services.storage.model.Bucket bucketPb = BucketInfo.of(bucket).toPb(); final Map optionsMap = optionMap(options); try { @@ -190,12 +192,12 @@ public com.google.api.services.storage.model.Bucket call() { } @Override - public BlobInfo get(String bucket, String blob, BlobSourceOption... options) { + public BlobInfo get(String bucket, String blob, BlobGetOption... options) { return get(BlobId.of(bucket, blob), options); } @Override - public BlobInfo get(BlobId blob, BlobSourceOption... options) { + public BlobInfo get(BlobId blob, BlobGetOption... options) { final StorageObject storedObject = blob.toPb(); final Map optionsMap = optionMap(options); try { @@ -220,11 +222,11 @@ public StorageObject call() { @Override public BlobInfo get(BlobId blob) { - return get(blob, new BlobSourceOption[0]); + return get(blob, new BlobGetOption[0]); } private abstract static class BasePageFetcher - implements BaseListResult.NextPageFetcher { + implements PageImpl.NextPageFetcher { private static final long serialVersionUID = 8236329004030295223L; protected final Map requestOptions; @@ -256,7 +258,7 @@ private static class BucketPageFetcher extends BasePageFetcher { } @Override - public ListResult nextPage() { + public Page nextPage() { return listBuckets(serviceOptions, requestOptions); } } @@ -273,17 +275,17 @@ private static class BlobPageFetcher extends BasePageFetcher { } @Override - public ListResult nextPage() { + public Page nextPage() { return listBlobs(bucket, serviceOptions, requestOptions); } } @Override - public ListResult list(BucketListOption... options) { + public Page list(BucketListOption... options) { return listBuckets(options(), optionMap(options)); } - private static ListResult listBuckets(final StorageOptions serviceOptions, + private static Page listBuckets(final StorageOptions serviceOptions, final Map optionsMap) { try { Tuple> result = runWithRetries( @@ -302,7 +304,7 @@ public BucketInfo apply(com.google.api.services.storage.model.Bucket bucketPb) { return BucketInfo.fromPb(bucketPb); } }); - return new BaseListResult<>(new BucketPageFetcher(serviceOptions, cursor, optionsMap), cursor, + return new PageImpl<>(new BucketPageFetcher(serviceOptions, cursor, optionsMap), cursor, buckets); } catch (RetryHelperException e) { throw StorageException.translateAndThrow(e); @@ -310,11 +312,11 @@ public BucketInfo apply(com.google.api.services.storage.model.Bucket bucketPb) { } @Override - public ListResult list(final String bucket, BlobListOption... options) { + public Page list(final String bucket, BlobListOption... options) { return listBlobs(bucket, options(), optionMap(options)); } - private static ListResult listBlobs(final String bucket, + private static Page listBlobs(final String bucket, final StorageOptions serviceOptions, final Map optionsMap) { try { Tuple> result = runWithRetries( @@ -333,7 +335,7 @@ public BlobInfo apply(StorageObject storageObject) { return BlobInfo.fromPb(storageObject); } }); - return new BaseListResult<>(new BlobPageFetcher(bucket, serviceOptions, cursor, optionsMap), + return new PageImpl<>(new BlobPageFetcher(bucket, serviceOptions, cursor, optionsMap), cursor, blobs); } catch (RetryHelperException e) { @@ -508,7 +510,7 @@ public BatchResponse apply(BatchRequest batchRequest) { } List>> toGet = Lists.newArrayListWithCapacity(batchRequest.toGet().size()); - for (Map.Entry> entry : batchRequest.toGet().entrySet()) { + for (Map.Entry> entry : batchRequest.toGet().entrySet()) { BlobId blob = entry.getKey(); Map optionsMap = optionMap(null, null, entry.getValue()); toGet.add(Tuple.>of(blob.toPb(), optionsMap)); diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageOptions.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageOptions.java index 16c17c3e8d98..2ad0950aa6aa 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageOptions.java +++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/StorageOptions.java @@ -111,6 +111,13 @@ public String pathDelimiter() { return pathDelimiter; } + /** + * Returns a default {@code StorageOptions} instance. + */ + public static StorageOptions defaultInstance() { + return builder().build(); + } + @Override public Builder toBuilder() { return new Builder(this); diff --git a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/testing/RemoteGcsHelper.java b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/testing/RemoteGcsHelper.java index 6ad655db8670..b15768cffa98 100644 --- a/gcloud-java-storage/src/main/java/com/google/gcloud/storage/testing/RemoteGcsHelper.java +++ b/gcloud-java-storage/src/main/java/com/google/gcloud/storage/testing/RemoteGcsHelper.java @@ -195,7 +195,7 @@ public DeleteBucketTask(Storage storage, String bucket) { @Override public Boolean call() throws Exception { while (true) { - for (BlobInfo info : storage.list(bucket)) { + for (BlobInfo info : storage.list(bucket).values()) { storage.delete(bucket, info.name()); } try { diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BaseListResultTest.java b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BaseListResultTest.java deleted file mode 100644 index 4c22edbc35c8..000000000000 --- a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BaseListResultTest.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gcloud.storage; - -import static org.junit.Assert.assertEquals; - -import com.google.common.collect.ImmutableList; - -import org.junit.Test; - -import java.util.Collections; - -public class BaseListResultTest { - - @Test - public void testListResult() throws Exception { - ImmutableList values = ImmutableList.of("1", "2"); - final BaseListResult nextResult = - new BaseListResult<>(null, "c", Collections.emptyList()); - BaseListResult.NextPageFetcher fetcher = new BaseListResult.NextPageFetcher() { - - @Override - public BaseListResult nextPage() { - return nextResult; - } - }; - BaseListResult result = new BaseListResult<>(fetcher, "c", values); - assertEquals(nextResult, result.nextPage()); - assertEquals("c", result.nextPageCursor()); - assertEquals(values, ImmutableList.copyOf(result.iterator())); - - } -} diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BatchRequestTest.java b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BatchRequestTest.java index 06b1105d7b9b..600c8af0d554 100644 --- a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BatchRequestTest.java +++ b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BatchRequestTest.java @@ -23,6 +23,7 @@ import static org.junit.Assert.assertTrue; import com.google.common.collect.Iterables; +import com.google.gcloud.storage.Storage.BlobGetOption; import com.google.gcloud.storage.Storage.BlobSourceOption; import com.google.gcloud.storage.Storage.BlobTargetOption; @@ -42,7 +43,7 @@ public void testBatchRequest() { .update(BlobInfo.builder("b2", "o1").build(), BlobTargetOption.predefinedAcl(PUBLIC_READ)) .update(BlobInfo.builder("b2", "o2").build()) .get("b3", "o1") - .get("b3", "o2", BlobSourceOption.generationMatch(1)) + .get("b3", "o2", BlobGetOption.generationMatch(1)) .get("b3", "o3") .build(); @@ -68,16 +69,14 @@ public void testBatchRequest() { assertTrue(Iterables.isEmpty(update.getValue())); assertFalse(updates.hasNext()); - Iterator>> gets = request - .toGet().entrySet().iterator(); - Entry> get = gets.next(); + Iterator>> gets = request.toGet().entrySet().iterator(); + Entry> get = gets.next(); assertEquals(BlobId.of("b3", "o1"), get.getKey()); assertTrue(Iterables.isEmpty(get.getValue())); get = gets.next(); assertEquals(BlobId.of("b3", "o2"), get.getKey()); assertEquals(1, Iterables.size(get.getValue())); - assertEquals(BlobSourceOption.generationMatch(1), - Iterables.getFirst(get.getValue(), null)); + assertEquals(BlobGetOption.generationMatch(1), Iterables.getFirst(get.getValue(), null)); get = gets.next(); assertEquals(BlobId.of("b3", "o3"), get.getKey()); assertTrue(Iterables.isEmpty(get.getValue())); diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobListResultTest.java b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobListResultTest.java deleted file mode 100644 index 615213ab1516..000000000000 --- a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobListResultTest.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2015 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.gcloud.storage; - -import static org.easymock.EasyMock.createStrictMock; -import static org.easymock.EasyMock.expect; -import static org.easymock.EasyMock.replay; -import static org.easymock.EasyMock.verify; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; - -import com.google.common.collect.ImmutableList; - -import org.junit.Before; -import org.junit.Test; - -import java.util.Iterator; - -public class BlobListResultTest { - - private static final Iterable FIRST_PAGE_RESULTS = ImmutableList.of( - BlobInfo.builder("b1", "n1").build(), - BlobInfo.builder("b2", "n2").build()); - - private static final Iterable SECOND_PAGE_RESULTS = ImmutableList.of( - BlobInfo.builder("b1", "n1").build(), - BlobInfo.builder("b2", "n2").build()); - - private BaseListResult firstPage; - private BaseListResult secondPage; - private Storage storage; - private BlobListResult blobListResult; - - @Before - public void setUp() throws Exception { - firstPage = createStrictMock(BaseListResult.class); - secondPage = createStrictMock(BaseListResult.class); - storage = createStrictMock(Storage.class); - blobListResult = new BlobListResult(storage, firstPage); - } - - @Test - public void testListResult() throws Exception { - expect(firstPage.iterator()).andReturn(FIRST_PAGE_RESULTS.iterator()); - replay(firstPage); - Iterator firstPageIterator = FIRST_PAGE_RESULTS.iterator(); - Iterator blobListIterator = blobListResult.iterator(); - while (blobListIterator.hasNext() && firstPageIterator.hasNext()) { - assertEquals(firstPageIterator.next(), blobListIterator.next().info()); - } - assertFalse(blobListIterator.hasNext()); - assertFalse(firstPageIterator.hasNext()); - verify(firstPage); - } - - @Test - public void testCursor() throws Exception { - expect(firstPage.nextPageCursor()).andReturn("c"); - replay(firstPage); - assertEquals("c", blobListResult.nextPageCursor()); - verify(firstPage); - } - - @Test - public void testNextPage() throws Exception { - expect(firstPage.nextPage()).andReturn(secondPage); - expect(secondPage.iterator()).andReturn(SECOND_PAGE_RESULTS.iterator()); - replay(firstPage); - replay(secondPage); - ListResult nextPageResult = blobListResult.nextPage(); - Iterator secondPageIterator = SECOND_PAGE_RESULTS.iterator(); - Iterator blobListIterator = nextPageResult.iterator(); - while (blobListIterator.hasNext() && secondPageIterator.hasNext()) { - assertEquals(secondPageIterator.next(), blobListIterator.next().info()); - } - assertFalse(blobListIterator.hasNext()); - assertFalse(secondPageIterator.hasNext()); - verify(firstPage); - verify(secondPage); - } -} diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobTest.java b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobTest.java index defb1d35e3f4..02e325716c8b 100644 --- a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobTest.java +++ b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BlobTest.java @@ -72,14 +72,16 @@ public void testInfo() throws Exception { @Test public void testExists_True() throws Exception { - expect(storage.get(BLOB_INFO.blobId(), new Storage.BlobSourceOption[0])).andReturn(BLOB_INFO); + Storage.BlobGetOption[] expectedOptions = {Storage.BlobGetOption.fields()}; + expect(storage.get(BLOB_INFO.blobId(), expectedOptions)).andReturn(BLOB_INFO); replay(storage); assertTrue(blob.exists()); } @Test public void testExists_False() throws Exception { - expect(storage.get(BLOB_INFO.blobId(), new Storage.BlobSourceOption[0])).andReturn(null); + Storage.BlobGetOption[] expectedOptions = {Storage.BlobGetOption.fields()}; + expect(storage.get(BLOB_INFO.blobId(), expectedOptions)).andReturn(null); replay(storage); assertFalse(blob.exists()); } @@ -95,7 +97,7 @@ public void testContent() throws Exception { @Test public void testReload() throws Exception { BlobInfo updatedInfo = BLOB_INFO.toBuilder().cacheControl("c").build(); - expect(storage.get(BLOB_INFO.blobId(), new Storage.BlobSourceOption[0])).andReturn(updatedInfo); + expect(storage.get(BLOB_INFO.blobId(), new Storage.BlobGetOption[0])).andReturn(updatedInfo); replay(storage); Blob updatedBlob = blob.reload(); assertSame(storage, blob.storage()); diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BucketInfoTest.java b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BucketInfoTest.java index 09ba0e8cda8e..4fa420b4b6e1 100644 --- a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BucketInfoTest.java +++ b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BucketInfoTest.java @@ -31,10 +31,8 @@ import com.google.gcloud.storage.BucketInfo.DeleteRule; import com.google.gcloud.storage.BucketInfo.DeleteRule.Type; import com.google.gcloud.storage.BucketInfo.IsLiveDeleteRule; -import com.google.gcloud.storage.BucketInfo.Location; import com.google.gcloud.storage.BucketInfo.NumNewerVersionsDeleteRule; import com.google.gcloud.storage.BucketInfo.RawDeleteRule; -import com.google.gcloud.storage.BucketInfo.StorageClass; import org.junit.Test; @@ -59,8 +57,8 @@ public class BucketInfoTest { Collections.singletonList(new AgeDeleteRule(5)); private static final String INDEX_PAGE = "index.html"; private static final String NOT_FOUND_PAGE = "error.html"; - private static final Location LOCATION = Location.asia(); - private static final StorageClass STORAGE_CLASS = StorageClass.standard(); + private static final String LOCATION = "ASIA"; + private static final String STORAGE_CLASS = "STANDARD"; private static final Boolean VERSIONING_ENABLED = true; private static final BucketInfo BUCKET_INFO = BucketInfo.builder("b") .acl(ACL) @@ -149,16 +147,6 @@ private void compareBuckets(BucketInfo expected, BucketInfo value) { assertEquals(expected.versioningEnabled(), value.versioningEnabled()); } - @Test - public void testLocation() { - assertEquals("ASIA", Location.asia().value()); - assertEquals("EU", Location.eu().value()); - assertEquals("US", Location.us().value()); - assertSame(Location.asia(), Location.of("asia")); - assertSame(Location.eu(), Location.of("EU")); - assertSame(Location.us(), Location.of("uS")); - } - @Test public void testDeleteRules() { AgeDeleteRule ageRule = new AgeDeleteRule(10); diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BucketTest.java b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BucketTest.java index 370850a5b6d4..5d8fc5a9dffc 100644 --- a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BucketTest.java +++ b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/BucketTest.java @@ -27,6 +27,8 @@ import static org.junit.Assert.assertTrue; import com.google.common.collect.ImmutableList; +import com.google.gcloud.PageImpl; +import com.google.gcloud.Page; import com.google.gcloud.storage.BatchResponse.Result; import org.easymock.Capture; @@ -73,14 +75,16 @@ public void testInfo() throws Exception { @Test public void testExists_True() throws Exception { - expect(storage.get(BUCKET_INFO.name())).andReturn(BUCKET_INFO); + Storage.BucketGetOption[] expectedOptions = {Storage.BucketGetOption.fields()}; + expect(storage.get(BUCKET_INFO.name(), expectedOptions)).andReturn(BUCKET_INFO); replay(storage); assertTrue(bucket.exists()); } @Test public void testExists_False() throws Exception { - expect(storage.get(BUCKET_INFO.name())).andReturn(null); + Storage.BucketGetOption[] expectedOptions = {Storage.BucketGetOption.fields()}; + expect(storage.get(BUCKET_INFO.name(), expectedOptions)).andReturn(null); replay(storage); assertFalse(bucket.exists()); } @@ -114,24 +118,28 @@ public void testDelete() throws Exception { @Test public void testList() throws Exception { - BaseListResult blobInfoResult = new BaseListResult<>(null, "c", BLOB_INFO_RESULTS); - expect(storage.list(BUCKET_INFO.name())).andReturn(blobInfoResult); - replay(storage); - ListResult blobResult = bucket.list(); - Iterator blobInfoIterator = blobInfoResult.iterator(); - Iterator blobIterator = blobResult.iterator(); + StorageOptions storageOptions = createStrictMock(StorageOptions.class); + PageImpl blobInfoPage = new PageImpl<>(null, "c", BLOB_INFO_RESULTS); + expect(storage.list(BUCKET_INFO.name())).andReturn(blobInfoPage); + expect(storage.options()).andReturn(storageOptions); + expect(storageOptions.service()).andReturn(storage); + replay(storage, storageOptions); + Page blobPage = bucket.list(); + Iterator blobInfoIterator = blobInfoPage.values().iterator(); + Iterator blobIterator = blobPage.values().iterator(); while (blobInfoIterator.hasNext() && blobIterator.hasNext()) { assertEquals(blobInfoIterator.next(), blobIterator.next().info()); } assertFalse(blobInfoIterator.hasNext()); assertFalse(blobIterator.hasNext()); - assertEquals(blobInfoResult.nextPageCursor(), blobResult.nextPageCursor()); + assertEquals(blobInfoPage.nextPageCursor(), blobPage.nextPageCursor()); + verify(storageOptions); } @Test public void testGet() throws Exception { BlobInfo info = BlobInfo.builder("b", "n").build(); - expect(storage.get(BlobId.of(bucket.info().name(), "n"), new Storage.BlobSourceOption[0])) + expect(storage.get(BlobId.of(bucket.info().name(), "n"), new Storage.BlobGetOption[0])) .andReturn(info); replay(storage); Blob blob = bucket.get("n"); diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/ITStorageTest.java b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/ITStorageTest.java index 3aad7b712e48..423e972a8de6 100644 --- a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/ITStorageTest.java +++ b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/ITStorageTest.java @@ -26,7 +26,10 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import com.google.gcloud.Page; import com.google.gcloud.RestorableState; +import com.google.gcloud.storage.Storage.BlobField; +import com.google.gcloud.storage.Storage.BucketField; import com.google.gcloud.storage.testing.RemoteGcsHelper; import org.junit.AfterClass; @@ -80,17 +83,46 @@ public static void afterClass() @Test(timeout = 5000) public void testListBuckets() throws InterruptedException { - Iterator bucketIterator = - storage.list(Storage.BucketListOption.prefix(BUCKET)).iterator(); + Iterator bucketIterator = storage.list(Storage.BucketListOption.prefix(BUCKET), + Storage.BucketListOption.fields()).values().iterator(); while (!bucketIterator.hasNext()) { Thread.sleep(500); - bucketIterator = storage.list(Storage.BucketListOption.prefix(BUCKET)).iterator(); + bucketIterator = storage.list(Storage.BucketListOption.prefix(BUCKET), + Storage.BucketListOption.fields()).values().iterator(); } while (bucketIterator.hasNext()) { - assertTrue(bucketIterator.next().name().startsWith(BUCKET)); + BucketInfo remoteBucket = bucketIterator.next(); + assertTrue(remoteBucket.name().startsWith(BUCKET)); + assertNull(remoteBucket.createTime()); + assertNull(remoteBucket.selfLink()); } } + @Test + public void testGetBucketSelectedFields() { + BucketInfo remoteBucket = storage.get(BUCKET, Storage.BucketGetOption.fields(BucketField.ID)); + assertEquals(BUCKET, remoteBucket.name()); + assertNull(remoteBucket.createTime()); + assertNotNull(remoteBucket.id()); + } + + @Test + public void testGetBucketAllSelectedFields() { + BucketInfo remoteBucket = storage.get(BUCKET, + Storage.BucketGetOption.fields(BucketField.values())); + assertEquals(BUCKET, remoteBucket.name()); + assertNotNull(remoteBucket.createTime()); + assertNotNull(remoteBucket.selfLink()); + } + + @Test + public void testGetBucketEmptyFields() { + BucketInfo remoteBucket = storage.get(BUCKET, Storage.BucketGetOption.fields()); + assertEquals(BUCKET, remoteBucket.name()); + assertNull(remoteBucket.createTime()); + assertNull(remoteBucket.selfLink()); + } + @Test public void testCreateBlob() { String blobName = "test-create-blob"; @@ -160,6 +192,104 @@ public void testCreateBlobMd5Fail() throws UnsupportedEncodingException { } } + @Test + public void testGetBlobEmptySelectedFields() { + String blobName = "test-get-empty-selected-fields-blob"; + BlobInfo blob = BlobInfo.builder(BUCKET, blobName).contentType(CONTENT_TYPE).build(); + assertNotNull(storage.create(blob)); + BlobInfo remoteBlob = storage.get(blob.blobId(), Storage.BlobGetOption.fields()); + assertEquals(blob.blobId(), remoteBlob.blobId()); + assertNull(remoteBlob.contentType()); + assertTrue(storage.delete(BUCKET, blobName)); + } + + @Test + public void testGetBlobSelectedFields() { + String blobName = "test-get-selected-fields-blob"; + BlobInfo blob = BlobInfo.builder(BUCKET, blobName) + .contentType(CONTENT_TYPE) + .metadata(ImmutableMap.of("k", "v")) + .build(); + assertNotNull(storage.create(blob)); + BlobInfo remoteBlob = storage.get(blob.blobId(), Storage.BlobGetOption.fields( + BlobField.METADATA)); + assertEquals(blob.blobId(), remoteBlob.blobId()); + assertEquals(ImmutableMap.of("k", "v"), remoteBlob.metadata()); + assertNull(remoteBlob.contentType()); + assertTrue(storage.delete(BUCKET, blobName)); + } + + @Test + public void testGetBlobAllSelectedFields() { + String blobName = "test-get-all-selected-fields-blob"; + BlobInfo blob = BlobInfo.builder(BUCKET, blobName) + .contentType(CONTENT_TYPE) + .metadata(ImmutableMap.of("k", "v")) + .build(); + assertNotNull(storage.create(blob)); + BlobInfo remoteBlob = storage.get(blob.blobId(), + Storage.BlobGetOption.fields(BlobField.values())); + assertEquals(blob.blobId(), remoteBlob.blobId()); + assertEquals(ImmutableMap.of("k", "v"), remoteBlob.metadata()); + assertNotNull(remoteBlob.id()); + assertNotNull(remoteBlob.selfLink()); + assertTrue(storage.delete(BUCKET, blobName)); + } + + @Test + public void testListBlobsSelectedFields() { + String[] blobNames = {"test-list-blobs-selected-fields-blob1", + "test-list-blobs-selected-fields-blob2"}; + ImmutableMap metadata = ImmutableMap.of("k", "v"); + BlobInfo blob1 = BlobInfo.builder(BUCKET, blobNames[0]) + .contentType(CONTENT_TYPE) + .metadata(metadata) + .build(); + BlobInfo blob2 = BlobInfo.builder(BUCKET, blobNames[1]) + .contentType(CONTENT_TYPE) + .metadata(metadata) + .build(); + assertNotNull(storage.create(blob1)); + assertNotNull(storage.create(blob2)); + Page page = storage.list(BUCKET, + Storage.BlobListOption.prefix("test-list-blobs-selected-fields-blob"), + Storage.BlobListOption.fields(BlobField.METADATA)); + int index = 0; + for (BlobInfo remoteBlob : page.values()) { + assertEquals(BUCKET, remoteBlob.bucket()); + assertEquals(blobNames[index++], remoteBlob.name()); + assertEquals(metadata, remoteBlob.metadata()); + assertNull(remoteBlob.contentType()); + } + assertTrue(storage.delete(BUCKET, blobNames[0])); + assertTrue(storage.delete(BUCKET, blobNames[1])); + } + + @Test + public void testListBlobsEmptySelectedFields() { + String[] blobNames = {"test-list-blobs-empty-selected-fields-blob1", + "test-list-blobs-empty-selected-fields-blob2"}; + BlobInfo blob1 = BlobInfo.builder(BUCKET, blobNames[0]) + .contentType(CONTENT_TYPE) + .build(); + BlobInfo blob2 = BlobInfo.builder(BUCKET, blobNames[1]) + .contentType(CONTENT_TYPE) + .build(); + assertNotNull(storage.create(blob1)); + assertNotNull(storage.create(blob2)); + Page page = storage.list(BUCKET, + Storage.BlobListOption.prefix("test-list-blobs-empty-selected-fields-blob"), + Storage.BlobListOption.fields()); + int index = 0; + for (BlobInfo remoteBlob : page.values()) { + assertEquals(BUCKET, remoteBlob.bucket()); + assertEquals(blobNames[index++], remoteBlob.name()); + assertNull(remoteBlob.contentType()); + } + assertTrue(storage.delete(BUCKET, blobNames[0])); + assertTrue(storage.delete(BUCKET, blobNames[1])); + } + @Test public void testUpdateBlob() { String blobName = "test-update-blob"; @@ -442,7 +572,7 @@ public void testBatchRequestFail() { BatchRequest batchRequest = BatchRequest.builder() .update(updatedBlob, Storage.BlobTargetOption.generationMatch()) .delete(BUCKET, blobName, Storage.BlobSourceOption.generationMatch(-1L)) - .get(BUCKET, blobName, Storage.BlobSourceOption.generationMatch(-1L)) + .get(BUCKET, blobName, Storage.BlobGetOption.generationMatch(-1L)) .build(); BatchResponse updateResponse = storage.apply(batchRequest); assertEquals(1, updateResponse.updates().size()); diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/RemoteGcsHelperTest.java b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/RemoteGcsHelperTest.java index 329767e85d4a..3c3d1aebb3df 100644 --- a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/RemoteGcsHelperTest.java +++ b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/RemoteGcsHelperTest.java @@ -20,6 +20,7 @@ import static org.junit.Assert.assertTrue; import com.google.common.collect.ImmutableList; +import com.google.gcloud.Page; import com.google.gcloud.storage.testing.RemoteGcsHelper; import org.easymock.EasyMock; @@ -74,20 +75,25 @@ public class RemoteGcsHelperTest { BlobInfo.builder(BUCKET_NAME, "n2").build()); private static final StorageException RETRYABLE_EXCEPTION = new StorageException(409, "", true); private static final StorageException FATAL_EXCEPTION = new StorageException(500, "", false); - private static final ListResult BLOB_LIST_RESULT = new ListResult() { + private static final Page BLOB_PAGE = new Page() { @Override public String nextPageCursor() { - return "listResult"; + return "nextPageCursor"; } @Override - public ListResult nextPage() { + public Page nextPage() { return null; } @Override - public Iterator iterator() { + public Iterable values() { + return BLOB_LIST; + } + + @Override + public Iterator iterateAll() { return BLOB_LIST.iterator(); } }; @@ -106,7 +112,7 @@ public static void beforeClass() { @Test public void testForceDelete() throws InterruptedException, ExecutionException { Storage storageMock = EasyMock.createMock(Storage.class); - EasyMock.expect(storageMock.list(BUCKET_NAME)).andReturn(BLOB_LIST_RESULT); + EasyMock.expect(storageMock.list(BUCKET_NAME)).andReturn(BLOB_PAGE); for (BlobInfo info : BLOB_LIST) { EasyMock.expect(storageMock.delete(BUCKET_NAME, info.name())).andReturn(true); } @@ -119,7 +125,7 @@ public void testForceDelete() throws InterruptedException, ExecutionException { @Test public void testForceDeleteTimeout() throws InterruptedException, ExecutionException { Storage storageMock = EasyMock.createMock(Storage.class); - EasyMock.expect(storageMock.list(BUCKET_NAME)).andReturn(BLOB_LIST_RESULT).anyTimes(); + EasyMock.expect(storageMock.list(BUCKET_NAME)).andReturn(BLOB_PAGE).anyTimes(); for (BlobInfo info : BLOB_LIST) { EasyMock.expect(storageMock.delete(BUCKET_NAME, info.name())).andReturn(true).anyTimes(); } @@ -132,7 +138,7 @@ public void testForceDeleteTimeout() throws InterruptedException, ExecutionExcep @Test public void testForceDeleteFail() throws InterruptedException, ExecutionException { Storage storageMock = EasyMock.createMock(Storage.class); - EasyMock.expect(storageMock.list(BUCKET_NAME)).andReturn(BLOB_LIST_RESULT); + EasyMock.expect(storageMock.list(BUCKET_NAME)).andReturn(BLOB_PAGE); for (BlobInfo info : BLOB_LIST) { EasyMock.expect(storageMock.delete(BUCKET_NAME, info.name())).andReturn(true); } @@ -153,11 +159,11 @@ public void testCreateFromStream() { assertEquals(PROJECT_ID, options.projectId()); assertEquals(60000, options.connectTimeout()); assertEquals(60000, options.readTimeout()); - assertEquals(10, options.retryParams().getRetryMaxAttempts()); - assertEquals(6, options.retryParams().getRetryMinAttempts()); - assertEquals(30000, options.retryParams().getMaxRetryDelayMillis()); - assertEquals(120000, options.retryParams().getTotalRetryPeriodMillis()); - assertEquals(250, options.retryParams().getInitialRetryDelayMillis()); + assertEquals(10, options.retryParams().retryMaxAttempts()); + assertEquals(6, options.retryParams().retryMinAttempts()); + assertEquals(30000, options.retryParams().maxRetryDelayMillis()); + assertEquals(120000, options.retryParams().totalRetryPeriodMillis()); + assertEquals(250, options.retryParams().initialRetryDelayMillis()); } @Test diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/SerializationTest.java b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/SerializationTest.java index 4c22170bba80..2d80191aeb2d 100644 --- a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/SerializationTest.java +++ b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/SerializationTest.java @@ -21,6 +21,7 @@ import com.google.common.collect.ImmutableMap; import com.google.gcloud.AuthCredentials; +import com.google.gcloud.PageImpl; import com.google.gcloud.RestorableState; import com.google.gcloud.RetryParams; import com.google.gcloud.spi.StorageRpc; @@ -54,7 +55,7 @@ public class SerializationTest { Collections.singletonList(BatchResponse.Result.of(true)), Collections.>emptyList(), Collections.>emptyList()); - private static final BaseListResult LIST_RESULT = new BaseListResult<>( + private static final PageImpl PAGE_RESULT = new PageImpl<>( null, "c", Collections.singletonList(BlobInfo.builder("b", "n").build())); private static final Storage.BlobListOption BLOB_LIST_OPTIONS = Storage.BlobListOption.maxResults(100); @@ -81,7 +82,7 @@ public void testServiceOptions() throws Exception { options = options.toBuilder() .projectId("p2") - .retryParams(RetryParams.getDefaultInstance()) + .retryParams(RetryParams.defaultInstance()) .authCredentials(AuthCredentials.noCredentials()) .pathDelimiter(":") .build(); @@ -93,7 +94,7 @@ public void testServiceOptions() throws Exception { public void testModelAndRequests() throws Exception { Serializable[] objects = {ACL_DOMAIN, ACL_GROUP, ACL_PROJECT_, ACL_USER, ACL_RAW, BLOB_INFO, BUCKET_INFO, - ORIGIN, CORS, BATCH_REQUEST, BATCH_RESPONSE, LIST_RESULT, BLOB_LIST_OPTIONS, + ORIGIN, CORS, BATCH_REQUEST, BATCH_RESPONSE, PAGE_RESULT, BLOB_LIST_OPTIONS, BLOB_SOURCE_OPTIONS, BLOB_TARGET_OPTIONS, BUCKET_LIST_OPTIONS, BUCKET_SOURCE_OPTIONS, BUCKET_TARGET_OPTIONS}; for (Serializable obj : objects) { @@ -109,7 +110,7 @@ public void testModelAndRequests() throws Exception { public void testReadChannelState() throws IOException, ClassNotFoundException { StorageOptions options = StorageOptions.builder() .projectId("p2") - .retryParams(RetryParams.getDefaultInstance()) + .retryParams(RetryParams.defaultInstance()) .authCredentials(AuthCredentials.noCredentials()) .build(); BlobReadChannel reader = @@ -125,7 +126,7 @@ public void testReadChannelState() throws IOException, ClassNotFoundException { public void testWriteChannelState() throws IOException, ClassNotFoundException { StorageOptions options = StorageOptions.builder() .projectId("p2") - .retryParams(RetryParams.getDefaultInstance()) + .retryParams(RetryParams.defaultInstance()) .authCredentials(AuthCredentials.noCredentials()) .build(); BlobWriteChannelImpl writer = new BlobWriteChannelImpl( diff --git a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/StorageImplTest.java b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/StorageImplTest.java index bdac54bcef2d..f07c7000813e 100644 --- a/gcloud-java-storage/src/test/java/com/google/gcloud/storage/StorageImplTest.java +++ b/gcloud-java-storage/src/test/java/com/google/gcloud/storage/StorageImplTest.java @@ -35,6 +35,7 @@ import com.google.gcloud.AuthCredentials.ServiceAccountAuthCredentials; import com.google.gcloud.RetryParams; import com.google.gcloud.ServiceOptions; +import com.google.gcloud.Page; import com.google.gcloud.spi.StorageRpc; import com.google.gcloud.spi.StorageRpc.Tuple; import com.google.gcloud.spi.StorageRpcFactory; @@ -137,13 +138,32 @@ public class StorageImplTest { private static final Storage.BlobWriteOption BLOB_WRITE_CRC2C = Storage.BlobWriteOption.crc32cMatch(); - // Bucket source options + // Bucket get/source options private static final Storage.BucketSourceOption BUCKET_SOURCE_METAGENERATION = Storage.BucketSourceOption.metagenerationMatch(BUCKET_INFO1.metageneration()); private static final Map BUCKET_SOURCE_OPTIONS = ImmutableMap.of( StorageRpc.Option.IF_METAGENERATION_MATCH, BUCKET_SOURCE_METAGENERATION.value()); + private static final Storage.BucketGetOption BUCKET_GET_METAGENERATION = + Storage.BucketGetOption.metagenerationMatch(BUCKET_INFO1.metageneration()); + private static final Storage.BucketGetOption BUCKET_GET_FIELDS = + Storage.BucketGetOption.fields(Storage.BucketField.LOCATION, Storage.BucketField.ACL); + private static final Storage.BucketGetOption BUCKET_GET_EMPTY_FIELDS = + Storage.BucketGetOption.fields(); + private static final Map BUCKET_GET_OPTIONS = ImmutableMap.of( + StorageRpc.Option.IF_METAGENERATION_MATCH, BUCKET_SOURCE_METAGENERATION.value()); - // Blob source options + // Blob get/source options + private static final Storage.BlobGetOption BLOB_GET_METAGENERATION = + Storage.BlobGetOption.metagenerationMatch(BLOB_INFO1.metageneration()); + private static final Storage.BlobGetOption BLOB_GET_GENERATION = + Storage.BlobGetOption.generationMatch(BLOB_INFO1.generation()); + private static final Storage.BlobGetOption BLOB_GET_FIELDS = + Storage.BlobGetOption.fields(Storage.BlobField.CONTENT_TYPE, Storage.BlobField.CRC32C); + private static final Storage.BlobGetOption BLOB_GET_EMPTY_FIELDS = + Storage.BlobGetOption.fields(); + private static final Map BLOB_GET_OPTIONS = ImmutableMap.of( + StorageRpc.Option.IF_METAGENERATION_MATCH, BLOB_GET_METAGENERATION.value(), + StorageRpc.Option.IF_GENERATION_MATCH, BLOB_GET_GENERATION.value()); private static final Storage.BlobSourceOption BLOB_SOURCE_METAGENERATION = Storage.BlobSourceOption.metagenerationMatch(BLOB_INFO1.metageneration()); private static final Storage.BlobSourceOption BLOB_SOURCE_GENERATION = @@ -160,6 +180,10 @@ public class StorageImplTest { Storage.BucketListOption.maxResults(42L); private static final Storage.BucketListOption BUCKET_LIST_PREFIX = Storage.BucketListOption.prefix("prefix"); + private static final Storage.BucketListOption BUCKET_LIST_FIELDS = + Storage.BucketListOption.fields(Storage.BucketField.LOCATION, Storage.BucketField.ACL); + private static final Storage.BucketListOption BUCKET_LIST_EMPTY_FIELDS = + Storage.BucketListOption.fields(); private static final Map BUCKET_LIST_OPTIONS = ImmutableMap.of( StorageRpc.Option.MAX_RESULTS, BUCKET_LIST_MAX_RESULT.value(), StorageRpc.Option.PREFIX, BUCKET_LIST_PREFIX.value()); @@ -169,6 +193,10 @@ public class StorageImplTest { Storage.BlobListOption.maxResults(42L); private static final Storage.BlobListOption BLOB_LIST_PREFIX = Storage.BlobListOption.prefix("prefix"); + private static final Storage.BlobListOption BLOB_LIST_FIELDS = + Storage.BlobListOption.fields(Storage.BlobField.CONTENT_TYPE, Storage.BlobField.MD5HASH); + private static final Storage.BlobListOption BLOB_LIST_EMPTY_FIELDS = + Storage.BlobListOption.fields(); private static final Map BLOB_LIST_OPTIONS = ImmutableMap.of( StorageRpc.Option.MAX_RESULTS, BLOB_LIST_MAX_RESULT.value(), StorageRpc.Option.PREFIX, BLOB_LIST_PREFIX.value()); @@ -358,16 +386,51 @@ public void testGetBucket() { @Test public void testGetBucketWithOptions() { - EasyMock.expect(storageRpcMock.get(BucketInfo.of(BUCKET_NAME1).toPb(), BUCKET_SOURCE_OPTIONS)) + EasyMock.expect(storageRpcMock.get(BucketInfo.of(BUCKET_NAME1).toPb(), BUCKET_GET_OPTIONS)) .andReturn(BUCKET_INFO1.toPb()); EasyMock.replay(storageRpcMock); storage = options.service(); - BucketInfo bucket = - storage.get(BUCKET_NAME1, - Storage.BucketSourceOption.metagenerationMatch(BUCKET_INFO1.metageneration())); + BucketInfo bucket = storage.get(BUCKET_NAME1, BUCKET_GET_METAGENERATION); assertEquals(BUCKET_INFO1, bucket); } + @Test + public void testGetBucketWithSelectedFields() { + Capture> capturedOptions = + Capture.>newInstance(); + EasyMock.expect(storageRpcMock.get(EasyMock.eq(BucketInfo.of(BUCKET_NAME1).toPb()), + EasyMock.capture(capturedOptions))).andReturn(BUCKET_INFO1.toPb()); + EasyMock.replay(storageRpcMock); + storage = options.service(); + BucketInfo bucket = storage.get(BUCKET_NAME1, BUCKET_GET_METAGENERATION, BUCKET_GET_FIELDS); + assertEquals(BUCKET_GET_METAGENERATION.value(), + capturedOptions.getValue().get(BUCKET_GET_METAGENERATION.rpcOption())); + String selector = (String) capturedOptions.getValue().get(BLOB_GET_FIELDS.rpcOption()); + assertTrue(selector.contains("name")); + assertTrue(selector.contains("location")); + assertTrue(selector.contains("acl")); + assertEquals(17, selector.length()); + assertEquals(BUCKET_INFO1.name(), bucket.name()); + } + + @Test + public void testGetBucketWithEmptyFields() { + Capture> capturedOptions = + Capture.>newInstance(); + EasyMock.expect(storageRpcMock.get(EasyMock.eq(BucketInfo.of(BUCKET_NAME1).toPb()), + EasyMock.capture(capturedOptions))).andReturn(BUCKET_INFO1.toPb()); + EasyMock.replay(storageRpcMock); + storage = options.service(); + BucketInfo bucket = storage.get(BUCKET_NAME1, BUCKET_GET_METAGENERATION, + BUCKET_GET_EMPTY_FIELDS); + assertEquals(BUCKET_GET_METAGENERATION.value(), + capturedOptions.getValue().get(BUCKET_GET_METAGENERATION.rpcOption())); + String selector = (String) capturedOptions.getValue().get(BLOB_GET_FIELDS.rpcOption()); + assertTrue(selector.contains("name")); + assertEquals(4, selector.length()); + assertEquals(BUCKET_INFO1.name(), bucket.name()); + } + @Test public void testGetBlob() { EasyMock.expect( @@ -382,12 +445,56 @@ public void testGetBlob() { @Test public void testGetBlobWithOptions() { EasyMock.expect( - storageRpcMock.get(BlobId.of(BUCKET_NAME1, BLOB_NAME1).toPb(), BLOB_SOURCE_OPTIONS)) + storageRpcMock.get(BlobId.of(BUCKET_NAME1, BLOB_NAME1).toPb(), BLOB_GET_OPTIONS)) .andReturn(BLOB_INFO1.toPb()); EasyMock.replay(storageRpcMock); storage = options.service(); BlobInfo blob = - storage.get(BUCKET_NAME1, BLOB_NAME1, BLOB_SOURCE_METAGENERATION, BLOB_SOURCE_GENERATION); + storage.get(BUCKET_NAME1, BLOB_NAME1, BLOB_GET_METAGENERATION, BLOB_GET_GENERATION); + assertEquals(BLOB_INFO1, blob); + } + + @Test + public void testGetBlobWithSelectedFields() { + Capture> capturedOptions = + Capture.>newInstance(); + EasyMock.expect(storageRpcMock.get(EasyMock.eq(BlobId.of(BUCKET_NAME1, BLOB_NAME1).toPb()), + EasyMock.capture(capturedOptions))).andReturn(BLOB_INFO1.toPb()); + EasyMock.replay(storageRpcMock); + storage = options.service(); + BlobInfo blob = storage.get(BUCKET_NAME1, BLOB_NAME1, BLOB_GET_METAGENERATION, + BLOB_GET_GENERATION, BLOB_GET_FIELDS); + assertEquals(BLOB_GET_METAGENERATION.value(), + capturedOptions.getValue().get(BLOB_GET_METAGENERATION.rpcOption())); + assertEquals(BLOB_GET_GENERATION.value(), + capturedOptions.getValue().get(BLOB_GET_GENERATION.rpcOption())); + String selector = (String) capturedOptions.getValue().get(BLOB_GET_FIELDS.rpcOption()); + assertTrue(selector.contains("bucket")); + assertTrue(selector.contains("name")); + assertTrue(selector.contains("contentType")); + assertTrue(selector.contains("crc32c")); + assertEquals(30, selector.length()); + assertEquals(BLOB_INFO1, blob); + } + + @Test + public void testGetBlobWithEmptyFields() { + Capture> capturedOptions = + Capture.>newInstance(); + EasyMock.expect(storageRpcMock.get(EasyMock.eq(BlobId.of(BUCKET_NAME1, BLOB_NAME1).toPb()), + EasyMock.capture(capturedOptions))).andReturn(BLOB_INFO1.toPb()); + EasyMock.replay(storageRpcMock); + storage = options.service(); + BlobInfo blob = storage.get(BUCKET_NAME1, BLOB_NAME1, BLOB_GET_METAGENERATION, + BLOB_GET_GENERATION, BLOB_GET_EMPTY_FIELDS); + assertEquals(BLOB_GET_METAGENERATION.value(), + capturedOptions.getValue().get(BLOB_GET_METAGENERATION.rpcOption())); + assertEquals(BLOB_GET_GENERATION.value(), + capturedOptions.getValue().get(BLOB_GET_GENERATION.rpcOption())); + String selector = (String) capturedOptions.getValue().get(BLOB_GET_FIELDS.rpcOption()); + assertTrue(selector.contains("bucket")); + assertTrue(selector.contains("name")); + assertEquals(11, selector.length()); assertEquals(BLOB_INFO1, blob); } @@ -400,9 +507,9 @@ public void testListBuckets() { EasyMock.expect(storageRpcMock.list(EMPTY_RPC_OPTIONS)).andReturn(result); EasyMock.replay(storageRpcMock); storage = options.service(); - ListResult listResult = storage.list(); - assertEquals(cursor, listResult.nextPageCursor()); - assertArrayEquals(bucketList.toArray(), Iterables.toArray(listResult, BucketInfo.class)); + Page page = storage.list(); + assertEquals(cursor, page.nextPageCursor()); + assertArrayEquals(bucketList.toArray(), Iterables.toArray(page.values(), BucketInfo.class)); } @Test @@ -411,10 +518,10 @@ public void testListBucketsEmpty() { Tuple.>of(null, null)); EasyMock.replay(storageRpcMock); storage = options.service(); - ListResult listResult = storage.list(); - assertNull(listResult.nextPageCursor()); + Page page = storage.list(); + assertNull(page.nextPageCursor()); assertArrayEquals(ImmutableList.of().toArray(), - Iterables.toArray(listResult, BucketInfo.class)); + Iterables.toArray(page.values(), BucketInfo.class)); } @Test @@ -426,9 +533,51 @@ public void testListBucketsWithOptions() { EasyMock.expect(storageRpcMock.list(BUCKET_LIST_OPTIONS)).andReturn(result); EasyMock.replay(storageRpcMock); storage = options.service(); - ListResult listResult = storage.list(BUCKET_LIST_MAX_RESULT, BUCKET_LIST_PREFIX); - assertEquals(cursor, listResult.nextPageCursor()); - assertArrayEquals(bucketList.toArray(), Iterables.toArray(listResult, BucketInfo.class)); + Page page = storage.list(BUCKET_LIST_MAX_RESULT, BUCKET_LIST_PREFIX); + assertEquals(cursor, page.nextPageCursor()); + assertArrayEquals(bucketList.toArray(), Iterables.toArray(page.values(), BucketInfo.class)); + } + + @Test + public void testListBucketsWithSelectedFields() { + String cursor = "cursor"; + Capture> capturedOptions = + Capture.>newInstance(); + ImmutableList bucketList = ImmutableList.of(BUCKET_INFO1, BUCKET_INFO2); + Tuple> result = + Tuple.of(cursor, Iterables.transform(bucketList, BucketInfo.TO_PB_FUNCTION)); + EasyMock.expect(storageRpcMock.list(EasyMock.capture(capturedOptions))).andReturn(result); + EasyMock.replay(storageRpcMock); + storage = options.service(); + Page page = storage.list(BUCKET_LIST_FIELDS); + String selector = (String) capturedOptions.getValue().get(BLOB_LIST_FIELDS.rpcOption()); + assertTrue(selector.contains("items")); + assertTrue(selector.contains("name")); + assertTrue(selector.contains("acl")); + assertTrue(selector.contains("location")); + assertEquals(24, selector.length()); + assertEquals(cursor, page.nextPageCursor()); + assertArrayEquals(bucketList.toArray(), Iterables.toArray(page.values(), BucketInfo.class)); + } + + @Test + public void testListBucketsWithEmptyFields() { + String cursor = "cursor"; + Capture> capturedOptions = + Capture.>newInstance(); + ImmutableList bucketList = ImmutableList.of(BUCKET_INFO1, BUCKET_INFO2); + Tuple> result = + Tuple.of(cursor, Iterables.transform(bucketList, BucketInfo.TO_PB_FUNCTION)); + EasyMock.expect(storageRpcMock.list(EasyMock.capture(capturedOptions))).andReturn(result); + EasyMock.replay(storageRpcMock); + storage = options.service(); + Page page = storage.list(BUCKET_LIST_EMPTY_FIELDS); + String selector = (String) capturedOptions.getValue().get(BLOB_LIST_FIELDS.rpcOption()); + assertTrue(selector.contains("items")); + assertTrue(selector.contains("name")); + assertEquals(11, selector.length()); + assertEquals(cursor, page.nextPageCursor()); + assertArrayEquals(bucketList.toArray(), Iterables.toArray(page.values(), BucketInfo.class)); } @Test @@ -440,22 +589,22 @@ public void testListBlobs() { EasyMock.expect(storageRpcMock.list(BUCKET_NAME1, EMPTY_RPC_OPTIONS)).andReturn(result); EasyMock.replay(storageRpcMock); storage = options.service(); - ListResult listResult = storage.list(BUCKET_NAME1); - assertEquals(cursor, listResult.nextPageCursor()); - assertArrayEquals(blobList.toArray(), Iterables.toArray(listResult, BlobInfo.class)); + Page page = storage.list(BUCKET_NAME1); + assertEquals(cursor, page.nextPageCursor()); + assertArrayEquals(blobList.toArray(), Iterables.toArray(page.values(), BlobInfo.class)); } @Test public void testListBlobsEmpty() { EasyMock.expect(storageRpcMock.list(BUCKET_NAME1, EMPTY_RPC_OPTIONS)) - .andReturn( - Tuple.>of(null, - null)); + .andReturn(Tuple.>of( + null, null)); EasyMock.replay(storageRpcMock); storage = options.service(); - ListResult listResult = storage.list(BUCKET_NAME1); - assertNull(listResult.nextPageCursor()); - assertArrayEquals(ImmutableList.of().toArray(), Iterables.toArray(listResult, BlobInfo.class)); + Page page = storage.list(BUCKET_NAME1); + assertNull(page.nextPageCursor()); + assertArrayEquals(ImmutableList.of().toArray(), + Iterables.toArray(page.values(), BlobInfo.class)); } @Test @@ -467,10 +616,67 @@ public void testListBlobsWithOptions() { EasyMock.expect(storageRpcMock.list(BUCKET_NAME1, BLOB_LIST_OPTIONS)).andReturn(result); EasyMock.replay(storageRpcMock); storage = options.service(); - ListResult listResult = - storage.list(BUCKET_NAME1, BLOB_LIST_MAX_RESULT, BLOB_LIST_PREFIX); - assertEquals(cursor, listResult.nextPageCursor()); - assertArrayEquals(blobList.toArray(), Iterables.toArray(listResult, BlobInfo.class)); + Page page = storage.list(BUCKET_NAME1, BLOB_LIST_MAX_RESULT, BLOB_LIST_PREFIX); + assertEquals(cursor, page.nextPageCursor()); + assertArrayEquals(blobList.toArray(), Iterables.toArray(page.values(), BlobInfo.class)); + } + + @Test + public void testListBlobsWithSelectedFields() { + String cursor = "cursor"; + Capture> capturedOptions = + Capture.>newInstance(); + ImmutableList blobList = ImmutableList.of(BLOB_INFO1, BLOB_INFO2); + Tuple> result = + Tuple.of(cursor, Iterables.transform(blobList, BlobInfo.TO_PB_FUNCTION)); + EasyMock.expect( + storageRpcMock.list(EasyMock.eq(BUCKET_NAME1), EasyMock.capture(capturedOptions))) + .andReturn(result); + EasyMock.replay(storageRpcMock); + storage = options.service(); + Page page = + storage.list(BUCKET_NAME1, BLOB_LIST_MAX_RESULT, BLOB_LIST_PREFIX, BLOB_LIST_FIELDS); + assertEquals(BLOB_LIST_MAX_RESULT.value(), + capturedOptions.getValue().get(BLOB_LIST_MAX_RESULT.rpcOption())); + assertEquals(BLOB_LIST_PREFIX.value(), + capturedOptions.getValue().get(BLOB_LIST_PREFIX.rpcOption())); + String selector = (String) capturedOptions.getValue().get(BLOB_LIST_FIELDS.rpcOption()); + assertTrue(selector.contains("items")); + assertTrue(selector.contains("bucket")); + assertTrue(selector.contains("name")); + assertTrue(selector.contains("contentType")); + assertTrue(selector.contains("md5Hash")); + assertEquals(38, selector.length()); + assertEquals(cursor, page.nextPageCursor()); + assertArrayEquals(blobList.toArray(), Iterables.toArray(page.values(), BlobInfo.class)); + } + + @Test + public void testListBlobsWithEmptyFields() { + String cursor = "cursor"; + Capture> capturedOptions = + Capture.>newInstance(); + ImmutableList blobList = ImmutableList.of(BLOB_INFO1, BLOB_INFO2); + Tuple> result = + Tuple.of(cursor, Iterables.transform(blobList, BlobInfo.TO_PB_FUNCTION)); + EasyMock.expect( + storageRpcMock.list(EasyMock.eq(BUCKET_NAME1), EasyMock.capture(capturedOptions))) + .andReturn(result); + EasyMock.replay(storageRpcMock); + storage = options.service(); + Page page = + storage.list(BUCKET_NAME1, BLOB_LIST_MAX_RESULT, BLOB_LIST_PREFIX, BLOB_LIST_EMPTY_FIELDS); + assertEquals(BLOB_LIST_MAX_RESULT.value(), + capturedOptions.getValue().get(BLOB_LIST_MAX_RESULT.rpcOption())); + assertEquals(BLOB_LIST_PREFIX.value(), + capturedOptions.getValue().get(BLOB_LIST_PREFIX.rpcOption())); + String selector = (String) capturedOptions.getValue().get(BLOB_LIST_EMPTY_FIELDS.rpcOption()); + assertTrue(selector.contains("items")); + assertTrue(selector.contains("bucket")); + assertTrue(selector.contains("name")); + assertEquals(18, selector.length()); + assertEquals(cursor, page.nextPageCursor()); + assertArrayEquals(blobList.toArray(), Iterables.toArray(page.values(), BlobInfo.class)); } @Test @@ -996,7 +1202,7 @@ public void testRetryableException() { .andThrow(new StorageException(500, "InternalError", true)) .andReturn(BLOB_INFO1.toPb()); EasyMock.replay(storageRpcMock); - storage = options.toBuilder().retryParams(RetryParams.getDefaultInstance()).build().service(); + storage = options.toBuilder().retryParams(RetryParams.defaultInstance()).build().service(); BlobInfo readBlob = storage.get(blob); assertEquals(BLOB_INFO1, readBlob); } @@ -1008,7 +1214,7 @@ public void testNonRetryableException() { EasyMock.expect(storageRpcMock.get(blob.toPb(), EMPTY_RPC_OPTIONS)) .andThrow(new StorageException(501, exceptionMessage, false)); EasyMock.replay(storageRpcMock); - storage = options.toBuilder().retryParams(RetryParams.getDefaultInstance()).build().service(); + storage = options.toBuilder().retryParams(RetryParams.defaultInstance()).build().service(); thrown.expect(StorageException.class); thrown.expectMessage(exceptionMessage); storage.get(blob); @@ -1021,7 +1227,7 @@ public void testRuntimeException() { EasyMock.expect(storageRpcMock.get(blob.toPb(), EMPTY_RPC_OPTIONS)) .andThrow(new RuntimeException(exceptionMessage)); EasyMock.replay(storageRpcMock); - storage = options.toBuilder().retryParams(RetryParams.getDefaultInstance()).build().service(); + storage = options.toBuilder().retryParams(RetryParams.defaultInstance()).build().service(); thrown.expect(StorageException.class); thrown.expectMessage(exceptionMessage); storage.get(blob); diff --git a/gcloud-java/README.md b/gcloud-java/README.md index 7e2eee84a8c4..eaaed21af5fe 100644 --- a/gcloud-java/README.md +++ b/gcloud-java/README.md @@ -20,14 +20,22 @@ This client supports the following Google Cloud Platform services: Quickstart ---------- -Add this to your pom.xml file +If you are using Maven, add this to your pom.xml file ```xml com.google.gcloud gcloud-java - 0.0.10 + 0.0.11 ``` +If you are using Gradle, add this to your dependencies +```Groovy +compile 'com.google.gcloud:gcloud-java:jar:0.0.11' +``` +If you are using SBT, add this to your dependencies +```Scala +libraryDependencies += "com.google.gcloud" % "gcloud-java" % "0.0.11" +``` Java Versions ------------- diff --git a/gcloud-java/pom.xml b/gcloud-java/pom.xml index ea6bb21ceb29..f0ee3bf758f7 100644 --- a/gcloud-java/pom.xml +++ b/gcloud-java/pom.xml @@ -11,7 +11,7 @@ com.google.gcloud gcloud-java-pom - 0.0.11-SNAPSHOT + 0.0.12-SNAPSHOT diff --git a/pom.xml b/pom.xml index 10326aa579f7..ce0c8d32cb49 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.google.gcloud gcloud-java-pom pom - 0.0.11-SNAPSHOT + 0.0.12-SNAPSHOT GCloud Java https://github.com/GoogleCloudPlatform/gcloud-java diff --git a/src/site/resources/index.html b/src/site/resources/index.html index 25c769db2fbf..3ed496b5bbb3 100644 --- a/src/site/resources/index.html +++ b/src/site/resources/index.html @@ -143,7 +143,7 @@

Example: Retrieve Datastore Entries

// Authentication is automatic inside Google Compute Engine // and Google App Engine. -DatastoreOptions options = DatastoreOptions.builder().build(); +DatastoreOptions options = DatastoreOptions.defaultInstance(); Datastore datastore = options.service(); KeyFactory keyFactory = datastore.newKeyFactory().kind(KIND); Key key = keyFactory.newKey(keyName); diff --git a/utilities/after_success.sh b/utilities/after_success.sh index 26405bcd9db3..05ab5fb373d6 100755 --- a/utilities/after_success.sh +++ b/utilities/after_success.sh @@ -7,35 +7,37 @@ source ./utilities/integration_test_env.sh echo "Travis branch: " ${TRAVIS_BRANCH} echo "Travis pull request: " ${TRAVIS_PULL_REQUEST} echo "Travis JDK version: " ${TRAVIS_JDK_VERSION} -if [ "${TRAVIS_JDK_VERSION}" == "oraclejdk7" -a "${TRAVIS_BRANCH}" == "master" -a "${TRAVIS_PULL_REQUEST}" == "false" ]; then - mvn cobertura:cobertura coveralls:report - SITE_VERSION=$(mvn org.apache.maven.plugins:maven-help-plugin:2.1.1:evaluate -Dexpression=project.version | grep -Ev '(^\[|\w+:)') - if [ "${SITE_VERSION##*-}" != "SNAPSHOT" ]; then - # Deploy site if not a SNAPSHOT - git config --global user.name "travis-ci" - git config --global user.email "travis@travis-ci.org" - git clone --branch gh-pages --single-branch https://github.com/GoogleCloudPlatform/gcloud-java/ tmp_gh-pages - mkdir -p tmp_gh-pages/$SITE_VERSION - mvn site -DskipTests=true - mvn site:stage -DtopSiteURL=http://googlecloudplatform.github.io/gcloud-java/site/${SITE_VERSION}/ - cd tmp_gh-pages - cp -r ../target/staging/$SITE_VERSION/* $SITE_VERSION/ - sed -i "s/{{SITE_VERSION}}/$SITE_VERSION/g" ${SITE_VERSION}/index.html # Update "Quickstart with Maven" to reflect version change - git add $SITE_VERSION - echo "" > index.html - git add index.html - echo "" > apidocs/index.html - git add apidocs/index.html - git commit -m "Added a new site for version $SITE_VERSION and updated the root directory's redirect." - git config --global push.default simple - git push --quiet "https://${CI_DEPLOY_USERNAME}:${CI_DEPLOY_PASSWORD}@github.com/GoogleCloudPlatform/gcloud-java.git" > /dev/null 2>&1 +if [ "${TRAVIS_JDK_VERSION}" == "oraclejdk7" -a "${TRAVIS_BRANCH}" == "master" ]; then + mvn clean cobertura:cobertura coveralls:report + if [ "${TRAVIS_PULL_REQUEST}" == "false" ]; then + SITE_VERSION=$(mvn org.apache.maven.plugins:maven-help-plugin:2.1.1:evaluate -Dexpression=project.version | grep -Ev '(^\[|\w+:)') + if [ "${SITE_VERSION##*-}" != "SNAPSHOT" ]; then + # Deploy site if not a SNAPSHOT + git config --global user.name "travis-ci" + git config --global user.email "travis@travis-ci.org" + git clone --branch gh-pages --single-branch https://github.com/GoogleCloudPlatform/gcloud-java/ tmp_gh-pages + mkdir -p tmp_gh-pages/$SITE_VERSION + mvn site -DskipTests=true + mvn site:stage -DtopSiteURL=http://googlecloudplatform.github.io/gcloud-java/site/${SITE_VERSION}/ + cd tmp_gh-pages + cp -r ../target/staging/$SITE_VERSION/* $SITE_VERSION/ + sed -i "s/{{SITE_VERSION}}/$SITE_VERSION/g" ${SITE_VERSION}/index.html # Update "Quickstart with Maven" to reflect version change + git add $SITE_VERSION + echo "" > index.html + git add index.html + echo "" > apidocs/index.html + git add apidocs/index.html + git commit -m "Added a new site for version $SITE_VERSION and updated the root directory's redirect." + git config --global push.default simple + git push --quiet "https://${CI_DEPLOY_USERNAME}:${CI_DEPLOY_PASSWORD}@github.com/GoogleCloudPlatform/gcloud-java.git" > /dev/null 2>&1 - cd .. - utilities/update_docs_version.sh # Update version in READMEs - mvn clean deploy --settings ~/.m2/settings.xml -P sign-deploy - else - mvn clean deploy -DskipTests=true -Dgpg.skip=true --settings ~/.m2/settings.xml + cd .. + utilities/update_docs_version.sh # Update version in READMEs + mvn clean deploy --settings ~/.m2/settings.xml -P sign-deploy + else + mvn clean deploy -DskipTests=true -Dgpg.skip=true --settings ~/.m2/settings.xml + fi fi else echo "Not deploying artifacts. This is only done with non-pull-request commits to master branch with Oracle Java 7 builds." diff --git a/utilities/update_docs_version.sh b/utilities/update_docs_version.sh index d7e7bdbfb830..4b1641a0bd81 100755 --- a/utilities/update_docs_version.sh +++ b/utilities/update_docs_version.sh @@ -14,6 +14,8 @@ if [ "${RELEASED_VERSION##*-}" != "SNAPSHOT" ]; then for item in ${module_folders[*]} do sed -ri "s/[0-9]+\.[0-9]+\.[0-9]+<\/version>/${RELEASED_VERSION}<\/version>/g" ${item}/README.md + sed -ri "s/:[0-9]+\.[0-9]+\.[0-9]+'/:${RELEASED_VERSION}'/g" ${item}/README.md + sed -ri "s/\"[0-9]+\.[0-9]+\.[0-9]+\"/\"${RELEASED_VERSION}\"/g" ${item}/README.md done git add README.md */README.md