diff --git a/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerExceptionFactory.java b/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerExceptionFactory.java index 39978102e263..f6f6210d85e7 100644 --- a/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerExceptionFactory.java +++ b/google-cloud-clients/google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerExceptionFactory.java @@ -18,6 +18,8 @@ import static com.google.cloud.spanner.SpannerException.DoNotConstructDirectly; +import com.google.api.gax.grpc.GrpcStatusCode; +import com.google.api.gax.rpc.ApiException; import com.google.common.base.MoreObjects; import com.google.common.base.Predicate; import io.grpc.Context; @@ -48,6 +50,28 @@ public static SpannerException propagateInterrupt(InterruptedException e) { return SpannerExceptionFactory.newSpannerException(ErrorCode.CANCELLED, "Interrupted", e); } + /** + * Transforms a {@code TimeoutException} to a {@code SpannerException}. + * + *
+   * 
+   * try {
+   *   Spanner spanner = SpannerOptions.getDefaultInstance();
+   *   spanner
+   *       .getDatabaseAdminClient()
+   *       .createDatabase("[INSTANCE_ID]", "[DATABASE_ID]", [STATEMENTS])
+   *       .get();
+   * } catch (TimeoutException e) {
+   *   propagateTimeout(e);
+   * }
+   * 
+   * 
+ */ + public static SpannerException propagateTimeout(TimeoutException e) { + return SpannerExceptionFactory.newSpannerException( + ErrorCode.DEADLINE_EXCEEDED, "Operation did not complete in the given time", e); + } + /** * Creates a new exception based on {@code cause}. * @@ -71,6 +95,8 @@ public static SpannerException newSpannerException(@Nullable Context context, Th return newSpannerExceptionPreformatted(e.getErrorCode(), e.getMessage(), e); } else if (cause instanceof CancellationException) { return newSpannerExceptionForCancellation(context, cause); + } else if (cause instanceof ApiException) { + return fromApiException((ApiException) cause); } // Extract gRPC status. This will produce "UNKNOWN" for non-gRPC exceptions. Status status = Status.fromThrowable(cause); @@ -120,6 +146,17 @@ private static SpannerException newSpannerExceptionPreformatted( } } + private static SpannerException fromApiException(ApiException exception) { + Status.Code code = ((GrpcStatusCode) exception.getStatusCode()).getTransportCode(); + ErrorCode errorCode = ErrorCode.fromGrpcStatus(Status.fromCode(code)); + if (exception.getCause() != null) { + return SpannerExceptionFactory.newSpannerException( + errorCode, exception.getMessage(), exception.getCause()); + } else { + return SpannerExceptionFactory.newSpannerException(errorCode, exception.getMessage()); + } + } + private static boolean isRetryable(ErrorCode code, @Nullable Throwable cause) { switch (code) { case INTERNAL: