Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create BaseServiceException in gcloud-java-core #324

Merged
merged 2 commits into from
Nov 5, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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() {

This comment was marked as spam.

return retryable;
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<String, Code> REASON_TO_CODE;
private static final ImmutableMap<Integer, Code> HTTP_TO_CODE;
private static final long serialVersionUID = -2336749234060754893L;
private static final ImmutableMap<String, DatastoreError> REASON_TO_ERROR;
private static final ImmutableMap<Integer, DatastoreError> HTTP_TO_ERROR;

private final Code code;
private final DatastoreError error;

/**
* An error code to represent the failure.
* Represents Datastore errors.
*
* @see <a href="https://cloud.google.com/datastore/docs/concepts/errors#Error_Codes">Google Cloud
* Datastore error codes</a>
*/
public enum Code {
public enum DatastoreError {

ABORTED(Reason.ABORTED),
DEADLINE_EXCEEDED(Reason.DEADLINE_EXCEEDED),
Expand All @@ -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;
}

Expand All @@ -89,30 +86,31 @@ DatastoreException translate(DatastoreRpcException exception, String message) {
}

static {
ImmutableMap.Builder<String, Code> builder = ImmutableMap.builder();
Map<Integer, Code> httpCodes = new HashMap<>();
for (Code code : Code.values()) {
builder.put(code.name(), code);
httpCodes.put(code.httpStatus(), code);
ImmutableMap.Builder<String, DatastoreError> builder = ImmutableMap.builder();
Map<Integer, DatastoreError> 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) {
Expand All @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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
Expand All @@ -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());
}
}
}
Expand All @@ -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());
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
}

Expand Down Expand Up @@ -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());
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,38 +16,24 @@

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.
*
* @see <a href="https://cloud.google.com/storage/docs/json_api/v1/status-codes">Google Cloud
* Storage error codes</a>
*/
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);
}

/**
Expand Down