@@ -48,10 +48,16 @@ protected static final class Error implements Serializable {
4848
4949 private final Integer code ;
5050 private final String reason ;
51+ private final boolean rejected ;
5152
5253 public Error (Integer code , String reason ) {
54+ this (code , reason , false );
55+ }
56+
57+ public Error (Integer code , String reason , boolean rejected ) {
5358 this .code = code ;
5459 this .reason = reason ;
60+ this .rejected = rejected ;
5561 }
5662
5763 /**
@@ -61,18 +67,27 @@ public Integer code() {
6167 return code ;
6268 }
6369
70+ /**
71+ * Returns true if the error indicates that the API call was certainly not accepted by the
72+ * server. For instance, if the server returns a rate limit exceeded error, it certainly did not
73+ * process the request and this method will return {@code true}.
74+ */
75+ public boolean rejected () {
76+ return rejected ;
77+ }
78+
6479 /**
6580 * Returns the reason that caused the exception.
6681 */
6782 public String reason () {
6883 return reason ;
6984 }
7085
71- boolean isRetryable (Set <Error > retryableErrors ) {
86+ boolean isRetryable (boolean idempotent , Set <Error > retryableErrors ) {
7287 for (Error retryableError : retryableErrors ) {
7388 if ((retryableError .code () == null || retryableError .code ().equals (this .code ()))
7489 && (retryableError .reason () == null || retryableError .reason ().equals (this .reason ()))) {
75- return true ;
90+ return idempotent || retryableError . rejected () ;
7691 }
7792 }
7893 return false ;
@@ -95,12 +110,14 @@ public BaseServiceException(IOException exception, boolean idempotent) {
95110 String reason = null ;
96111 String location = null ;
97112 String debugInfo = null ;
113+ Boolean retryable = null ;
98114 if (exception instanceof GoogleJsonResponseException ) {
99115 GoogleJsonError jsonError = ((GoogleJsonResponseException ) exception ).getDetails ();
100116 if (jsonError != null ) {
101- Error error = error (jsonError );
117+ Error error = new Error (jsonError . getCode (), reason ( jsonError ) );
102118 code = error .code ;
103119 reason = error .reason ;
120+ retryable = isRetryable (idempotent , error );
104121 if (reason != null ) {
105122 GoogleJsonError .ErrorInfo errorInfo = jsonError .getErrors ().get (0 );
106123 location = errorInfo .getLocation ();
@@ -110,22 +127,16 @@ public BaseServiceException(IOException exception, boolean idempotent) {
110127 code = ((GoogleJsonResponseException ) exception ).getStatusCode ();
111128 }
112129 }
130+ this .retryable = MoreObjects .firstNonNull (retryable , isRetryable (idempotent , exception ));
113131 this .code = code ;
114- this .retryable = idempotent && isRetryable (exception );
115132 this .reason = reason ;
116133 this .idempotent = idempotent ;
117134 this .location = location ;
118135 this .debugInfo = debugInfo ;
119136 }
120137
121138 public BaseServiceException (GoogleJsonError error , boolean idempotent ) {
122- super (error .getMessage ());
123- this .code = error .getCode ();
124- this .reason = reason (error );
125- this .idempotent = idempotent ;
126- this .retryable = idempotent && isRetryable (error );
127- this .location = null ;
128- this .debugInfo = null ;
139+ this (error .getCode (), error .getMessage (), reason (error ), idempotent );
129140 }
130141
131142 public BaseServiceException (int code , String message , String reason , boolean idempotent ) {
@@ -138,7 +149,7 @@ public BaseServiceException(int code, String message, String reason, boolean ide
138149 this .code = code ;
139150 this .reason = reason ;
140151 this .idempotent = idempotent ;
141- this .retryable = idempotent && new Error (code , reason ). isRetryable ( retryableErrors ( ));
152+ this .retryable = isRetryable ( idempotent , new Error (code , reason ));
142153 this .location = null ;
143154 this .debugInfo = null ;
144155 }
@@ -147,15 +158,12 @@ protected Set<Error> retryableErrors() {
147158 return Collections .emptySet ();
148159 }
149160
150- protected boolean isRetryable (GoogleJsonError error ) {
151- return error != null && error ( error ) .isRetryable (retryableErrors ());
161+ protected boolean isRetryable (boolean idempotent , Error error ) {
162+ return error .isRetryable (idempotent , retryableErrors ());
152163 }
153164
154- protected boolean isRetryable (IOException exception ) {
155- if (exception instanceof GoogleJsonResponseException ) {
156- return isRetryable (((GoogleJsonResponseException ) exception ).getDetails ());
157- }
158- return exception instanceof SocketTimeoutException ;
165+ protected boolean isRetryable (boolean idempotent , IOException exception ) {
166+ return idempotent && exception instanceof SocketTimeoutException ;
159167 }
160168
161169 /**
@@ -187,8 +195,8 @@ public boolean idempotent() {
187195 }
188196
189197 /**
190- * Returns the service location where the error causing the exception occurred. Returns
191- * {@code null} if not set .
198+ * Returns the service location where the error causing the exception occurred. Returns {@code
199+ * null} if not available .
192200 */
193201 public String location () {
194202 return location ;
@@ -223,18 +231,14 @@ public int hashCode() {
223231 debugInfo );
224232 }
225233
226- protected static String reason (GoogleJsonError error ) {
227- if (error .getErrors () != null && !error .getErrors ().isEmpty ()) {
234+ private static String reason (GoogleJsonError error ) {
235+ if (error .getErrors () != null && !error .getErrors ().isEmpty ()) {
228236 return error .getErrors ().get (0 ).getReason ();
229237 }
230238 return null ;
231239 }
232240
233- protected static Error error (GoogleJsonError error ) {
234- return new Error (error .getCode (), reason (error ));
235- }
236-
237- protected static String message (IOException exception ) {
241+ private static String message (IOException exception ) {
238242 if (exception instanceof GoogleJsonResponseException ) {
239243 GoogleJsonError details = ((GoogleJsonResponseException ) exception ).getDetails ();
240244 if (details != null ) {
0 commit comments