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/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-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/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..6afd40927264 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()); } } 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); } /**