1 /* 2 * Copyright 2017 Google LLC 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.cloud.http; 18 19 import com.google.api.client.googleapis.json.GoogleJsonError; 20 import com.google.api.client.googleapis.json.GoogleJsonResponseException; 21 import com.google.api.client.http.HttpResponseException; 22 import com.google.api.core.InternalApi; 23 import com.google.cloud.BaseServiceException; 24 import com.google.common.base.MoreObjects; 25 import java.io.IOException; 26 import java.util.Set; 27 28 /** Base class for all exceptions from http-based services. */ 29 public class BaseHttpServiceException extends BaseServiceException { 30 31 private static final long serialVersionUID = -5793034110344127954L; 32 public static final int UNKNOWN_CODE = 0; 33 34 @InternalApi("This class should only be extended within google-cloud-java") BaseHttpServiceException( IOException exception, boolean idempotent, Set<BaseServiceException.Error> retryableErrors)35 protected BaseHttpServiceException( 36 IOException exception, boolean idempotent, Set<BaseServiceException.Error> retryableErrors) { 37 super(makeExceptionData(exception, idempotent, retryableErrors)); 38 } 39 makeExceptionData( IOException exception, boolean idempotent, Set<BaseServiceException.Error> retryableErrors)40 private static ExceptionData makeExceptionData( 41 IOException exception, boolean idempotent, Set<BaseServiceException.Error> retryableErrors) { 42 int code = UNKNOWN_CODE; 43 String reason = null; 44 String location = null; 45 String debugInfo = null; 46 Boolean retryable = null; 47 if (exception instanceof HttpResponseException) { 48 if (exception instanceof GoogleJsonResponseException) { 49 GoogleJsonError jsonError = ((GoogleJsonResponseException) exception).getDetails(); 50 if (jsonError != null) { 51 BaseServiceException.Error error = 52 new BaseServiceException.Error(jsonError.getCode(), reason(jsonError)); 53 code = error.getCode(); 54 reason = error.getReason(); 55 retryable = error.isRetryable(idempotent, retryableErrors); 56 if (reason != null) { 57 GoogleJsonError.ErrorInfo errorInfo = jsonError.getErrors().get(0); 58 location = errorInfo.getLocation(); 59 debugInfo = (String) errorInfo.get("debugInfo"); 60 } 61 } else { 62 code = ((GoogleJsonResponseException) exception).getStatusCode(); 63 retryable = BaseServiceException.isRetryable(code, null, idempotent, retryableErrors); 64 } 65 } else { 66 // In cases where an exception is an instance of HttpResponseException but not 67 // an instance of GoogleJsonResponseException, check the status code to determine whether 68 // it's retryable 69 code = ((HttpResponseException) exception).getStatusCode(); 70 retryable = BaseServiceException.isRetryable(code, null, idempotent, retryableErrors); 71 } 72 } 73 return ExceptionData.newBuilder() 74 .setMessage(message(exception)) 75 .setCause(exception) 76 .setRetryable( 77 MoreObjects.firstNonNull( 78 retryable, BaseServiceException.isRetryable(idempotent, exception))) 79 .setCode(code) 80 .setReason(reason) 81 .setLocation(location) 82 .setDebugInfo(debugInfo) 83 .build(); 84 } 85 86 @InternalApi("This class should only be extended within google-cloud-java") BaseHttpServiceException( GoogleJsonError googleJsonError, boolean idempotent, Set<BaseServiceException.Error> retryableErrors)87 protected BaseHttpServiceException( 88 GoogleJsonError googleJsonError, 89 boolean idempotent, 90 Set<BaseServiceException.Error> retryableErrors) { 91 super(makeExceptionData(googleJsonError, idempotent, retryableErrors)); 92 } 93 makeExceptionData( GoogleJsonError googleJsonError, boolean idempotent, Set<BaseServiceException.Error> retryableErrors)94 private static ExceptionData makeExceptionData( 95 GoogleJsonError googleJsonError, 96 boolean idempotent, 97 Set<BaseServiceException.Error> retryableErrors) { 98 int code = googleJsonError.getCode(); 99 String reason = reason(googleJsonError); 100 101 ExceptionData.Builder exceptionData = ExceptionData.newBuilder(); 102 exceptionData 103 .setMessage(googleJsonError.getMessage()) 104 .setCause(null) 105 .setRetryable(BaseServiceException.isRetryable(code, reason, idempotent, retryableErrors)) 106 .setCode(code) 107 .setReason(reason); 108 if (reason != null) { 109 GoogleJsonError.ErrorInfo errorInfo = googleJsonError.getErrors().get(0); 110 exceptionData.setLocation(errorInfo.getLocation()); 111 exceptionData.setDebugInfo((String) errorInfo.get("debugInfo")); 112 } else { 113 exceptionData.setLocation(null); 114 exceptionData.setDebugInfo(null); 115 } 116 return exceptionData.build(); 117 } 118 119 @InternalApi("This class should only be extended within google-cloud-java") BaseHttpServiceException( int code, String message, String reason, boolean idempotent, Set<BaseServiceException.Error> retryableErrors)120 protected BaseHttpServiceException( 121 int code, 122 String message, 123 String reason, 124 boolean idempotent, 125 Set<BaseServiceException.Error> retryableErrors) { 126 this(code, message, reason, idempotent, retryableErrors, null); 127 } 128 129 @InternalApi("This class should only be extended within google-cloud-java") BaseHttpServiceException( int code, String message, String reason, boolean idempotent, Set<BaseServiceException.Error> retryableErrors, Throwable cause)130 protected BaseHttpServiceException( 131 int code, 132 String message, 133 String reason, 134 boolean idempotent, 135 Set<BaseServiceException.Error> retryableErrors, 136 Throwable cause) { 137 super( 138 ExceptionData.newBuilder() 139 .setMessage(message) 140 .setCause(cause) 141 .setRetryable( 142 BaseServiceException.isRetryable(code, reason, idempotent, retryableErrors)) 143 .setCode(code) 144 .setReason(reason) 145 .setLocation(null) 146 .setDebugInfo(null) 147 .build()); 148 } 149 reason(GoogleJsonError error)150 private static String reason(GoogleJsonError error) { 151 if (error.getErrors() != null && !error.getErrors().isEmpty()) { 152 return error.getErrors().get(0).getReason(); 153 } 154 return null; 155 } 156 message(IOException exception)157 private static String message(IOException exception) { 158 if (exception instanceof GoogleJsonResponseException) { 159 GoogleJsonError details = ((GoogleJsonResponseException) exception).getDetails(); 160 if (details != null) { 161 return details.getMessage(); 162 } 163 } 164 return exception.getMessage(); 165 } 166 } 167