• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
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  * A copy of the License is located at
7  *
8  *  http://aws.amazon.com/apache2.0
9  *
10  * or in the "license" file accompanying this file. This file is distributed
11  * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12  * express or implied. See the License for the specific language governing
13  * permissions and limitations under the License.
14  */
15 
16 package software.amazon.awssdk.awscore.exception;
17 
18 import java.time.Duration;
19 import java.time.Instant;
20 import java.util.Optional;
21 import java.util.StringJoiner;
22 import software.amazon.awssdk.annotations.SdkPublicApi;
23 import software.amazon.awssdk.awscore.internal.AwsErrorCode;
24 import software.amazon.awssdk.awscore.internal.AwsStatusCode;
25 import software.amazon.awssdk.core.exception.SdkServiceException;
26 import software.amazon.awssdk.core.retry.ClockSkew;
27 import software.amazon.awssdk.http.SdkHttpResponse;
28 
29 /**
30  * Extension of {@link SdkServiceException} that represents an error response returned
31  * by an Amazon web service.
32  * <p>
33  * <p>
34  * AmazonServiceException provides callers several pieces of
35  * information that can be used to obtain more information about the error and
36  * why it occurred. In particular, the errorType field can be used to determine
37  * if the caller's request was invalid, or the service encountered an error on
38  * the server side while processing it.
39  *
40  * @see SdkServiceException
41  */
42 @SdkPublicApi
43 public class AwsServiceException extends SdkServiceException {
44 
45     private AwsErrorDetails awsErrorDetails;
46     private Duration clockSkew;
47 
AwsServiceException(Builder b)48     protected AwsServiceException(Builder b) {
49         super(b);
50         this.awsErrorDetails = b.awsErrorDetails();
51         this.clockSkew = b.clockSkew();
52     }
53 
54     /**
55      * Additional details pertaining to an exception thrown by an AWS service.
56      *
57      * @return {@link AwsErrorDetails}.
58      */
awsErrorDetails()59     public AwsErrorDetails awsErrorDetails() {
60         return awsErrorDetails;
61     }
62 
63     @Override
getMessage()64     public String getMessage() {
65         if (awsErrorDetails != null) {
66             StringJoiner details = new StringJoiner(", ", "(", ")");
67             details.add("Service: " + awsErrorDetails().serviceName());
68             details.add("Status Code: " + statusCode());
69             details.add("Request ID: " + requestId());
70             if (extendedRequestId() != null) {
71                 details.add("Extended Request ID: " + extendedRequestId());
72             }
73             String message = super.getMessage();
74             if (message == null) {
75                 message = awsErrorDetails().errorMessage();
76             }
77             return message + " " + details;
78         }
79 
80         return super.getMessage();
81     }
82 
83     @Override
isClockSkewException()84     public boolean isClockSkewException() {
85         if (super.isClockSkewException()) {
86             return true;
87         }
88 
89         if (awsErrorDetails == null) {
90             return false;
91         }
92 
93         if (AwsErrorCode.isDefiniteClockSkewErrorCode(awsErrorDetails.errorCode())) {
94             return true;
95         }
96 
97         SdkHttpResponse sdkHttpResponse = awsErrorDetails.sdkHttpResponse();
98 
99         if (clockSkew == null || sdkHttpResponse == null) {
100             return false;
101         }
102 
103         boolean isPossibleClockSkewError = AwsErrorCode.isPossibleClockSkewErrorCode(awsErrorDetails.errorCode()) ||
104                                            AwsStatusCode.isPossibleClockSkewStatusCode(statusCode());
105 
106         return isPossibleClockSkewError && ClockSkew.isClockSkewed(Instant.now().minus(clockSkew),
107                                                                    ClockSkew.getServerTime(sdkHttpResponse).orElse(null));
108     }
109 
110     @Override
isThrottlingException()111     public boolean isThrottlingException() {
112         return super.isThrottlingException() ||
113                 Optional.ofNullable(awsErrorDetails)
114                         .map(a -> AwsErrorCode.isThrottlingErrorCode(a.errorCode()))
115                         .orElse(false);
116     }
117 
118     /**
119      * @return {@link Builder} instance to construct a new {@link AwsServiceException}.
120      */
builder()121     public static Builder builder() {
122         return new BuilderImpl();
123     }
124 
125     /**
126      * Create a {@link AwsServiceException.Builder} initialized with the properties of this {@code AwsServiceException}.
127      *
128      * @return A new builder initialized with this config's properties.
129      */
130     @Override
toBuilder()131     public Builder toBuilder() {
132         return new BuilderImpl(this);
133     }
134 
serializableBuilderClass()135     public static Class<? extends Builder> serializableBuilderClass() {
136         return BuilderImpl.class;
137     }
138 
139     public interface Builder extends SdkServiceException.Builder {
140 
141         /**
142          * Specifies the additional awsErrorDetails from the service response.
143          * @param awsErrorDetails Object containing additional details from the response.
144          * @return This object for method chaining.
145          */
awsErrorDetails(AwsErrorDetails awsErrorDetails)146         Builder awsErrorDetails(AwsErrorDetails awsErrorDetails);
147 
148         /**
149          * The {@link AwsErrorDetails} from the service response.
150          *
151          * @return {@link AwsErrorDetails}.
152          */
awsErrorDetails()153         AwsErrorDetails awsErrorDetails();
154 
155         /**
156          * The request-level time skew between the client and server date for the request that generated this exception. Positive
157          * values imply the client clock is "fast" and negative values imply the client clock is "slow".
158          */
clockSkew(Duration timeOffSet)159         Builder clockSkew(Duration timeOffSet);
160 
161         /**
162          * The request-level time skew between the client and server date for the request that generated this exception. Positive
163          * values imply the client clock is "fast" and negative values imply the client clock is "slow".
164          */
clockSkew()165         Duration clockSkew();
166 
167         @Override
message(String message)168         Builder message(String message);
169 
170         @Override
cause(Throwable t)171         Builder cause(Throwable t);
172 
173         @Override
requestId(String requestId)174         Builder requestId(String requestId);
175 
176         @Override
extendedRequestId(String extendedRequestId)177         Builder extendedRequestId(String extendedRequestId);
178 
179         @Override
statusCode(int statusCode)180         Builder statusCode(int statusCode);
181 
182         @Override
build()183         AwsServiceException build();
184     }
185 
186     protected static class BuilderImpl extends SdkServiceException.BuilderImpl implements Builder {
187 
188         protected AwsErrorDetails awsErrorDetails;
189         private Duration clockSkew;
190 
BuilderImpl()191         protected BuilderImpl() {
192         }
193 
BuilderImpl(AwsServiceException ex)194         protected BuilderImpl(AwsServiceException ex) {
195             super(ex);
196             this.awsErrorDetails = ex.awsErrorDetails();
197         }
198 
199         @Override
awsErrorDetails(AwsErrorDetails awsErrorDetails)200         public Builder awsErrorDetails(AwsErrorDetails awsErrorDetails) {
201             this.awsErrorDetails = awsErrorDetails;
202             return this;
203         }
204 
205         @Override
awsErrorDetails()206         public AwsErrorDetails awsErrorDetails() {
207             return awsErrorDetails;
208         }
209 
getAwsErrorDetails()210         public AwsErrorDetails getAwsErrorDetails() {
211             return awsErrorDetails;
212         }
213 
setAwsErrorDetails(AwsErrorDetails awsErrorDetails)214         public void setAwsErrorDetails(AwsErrorDetails awsErrorDetails) {
215             this.awsErrorDetails = awsErrorDetails;
216         }
217 
218         @Override
clockSkew(Duration clockSkew)219         public Builder clockSkew(Duration clockSkew) {
220             this.clockSkew = clockSkew;
221             return this;
222         }
223 
224         @Override
clockSkew()225         public Duration clockSkew() {
226             return clockSkew;
227         }
228 
229         @Override
message(String message)230         public Builder message(String message) {
231             this.message = message;
232             return this;
233         }
234 
235         @Override
cause(Throwable cause)236         public Builder cause(Throwable cause) {
237             this.cause = cause;
238             return this;
239         }
240 
241         @Override
writableStackTrace(Boolean writableStackTrace)242         public Builder writableStackTrace(Boolean writableStackTrace) {
243             this.writableStackTrace = writableStackTrace;
244             return this;
245         }
246 
247         @Override
requestId(String requestId)248         public Builder requestId(String requestId) {
249             this.requestId = requestId;
250             return this;
251         }
252 
253         @Override
extendedRequestId(String extendedRequestId)254         public Builder extendedRequestId(String extendedRequestId) {
255             this.extendedRequestId = extendedRequestId;
256             return this;
257         }
258 
259         @Override
statusCode(int statusCode)260         public Builder statusCode(int statusCode) {
261             this.statusCode = statusCode;
262             return this;
263         }
264 
265         @Override
build()266         public AwsServiceException build() {
267             return new AwsServiceException(this);
268         }
269     }
270 }
271