• 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.core.client.config;
17 
18 import static java.util.Collections.emptyList;
19 import static java.util.Collections.emptyMap;
20 import static software.amazon.awssdk.core.client.config.SdkClientOption.ADDITIONAL_HTTP_HEADERS;
21 import static software.amazon.awssdk.core.client.config.SdkClientOption.API_CALL_ATTEMPT_TIMEOUT;
22 import static software.amazon.awssdk.core.client.config.SdkClientOption.API_CALL_TIMEOUT;
23 import static software.amazon.awssdk.core.client.config.SdkClientOption.COMPRESSION_CONFIGURATION;
24 import static software.amazon.awssdk.core.client.config.SdkClientOption.CONFIGURED_COMPRESSION_CONFIGURATION;
25 import static software.amazon.awssdk.core.client.config.SdkClientOption.CONFIGURED_SCHEDULED_EXECUTOR_SERVICE;
26 import static software.amazon.awssdk.core.client.config.SdkClientOption.EXECUTION_ATTRIBUTES;
27 import static software.amazon.awssdk.core.client.config.SdkClientOption.EXECUTION_INTERCEPTORS;
28 import static software.amazon.awssdk.core.client.config.SdkClientOption.METRIC_PUBLISHERS;
29 import static software.amazon.awssdk.core.client.config.SdkClientOption.PROFILE_FILE_SUPPLIER;
30 import static software.amazon.awssdk.core.client.config.SdkClientOption.PROFILE_NAME;
31 import static software.amazon.awssdk.core.client.config.SdkClientOption.RETRY_POLICY;
32 import static software.amazon.awssdk.core.client.config.SdkClientOption.SCHEDULED_EXECUTOR_SERVICE;
33 import static software.amazon.awssdk.utils.ScheduledExecutorUtils.unmanagedScheduledExecutor;
34 import static software.amazon.awssdk.utils.ScheduledExecutorUtils.unwrapUnmanagedScheduledExecutor;
35 
36 import java.time.Duration;
37 import java.util.ArrayList;
38 import java.util.Collections;
39 import java.util.HashSet;
40 import java.util.List;
41 import java.util.Map;
42 import java.util.Optional;
43 import java.util.Set;
44 import java.util.TreeMap;
45 import java.util.concurrent.ScheduledExecutorService;
46 import java.util.function.Consumer;
47 import java.util.function.Supplier;
48 import software.amazon.awssdk.annotations.SdkInternalApi;
49 import software.amazon.awssdk.annotations.SdkPublicApi;
50 import software.amazon.awssdk.annotations.ToBuilderIgnoreField;
51 import software.amazon.awssdk.core.CompressionConfiguration;
52 import software.amazon.awssdk.core.RequestOverrideConfiguration;
53 import software.amazon.awssdk.core.SdkPlugin;
54 import software.amazon.awssdk.core.interceptor.ExecutionAttribute;
55 import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
56 import software.amazon.awssdk.core.interceptor.ExecutionInterceptor;
57 import software.amazon.awssdk.core.retry.RetryMode;
58 import software.amazon.awssdk.core.retry.RetryPolicy;
59 import software.amazon.awssdk.core.sync.ResponseTransformer;
60 import software.amazon.awssdk.metrics.MetricPublisher;
61 import software.amazon.awssdk.profiles.ProfileFile;
62 import software.amazon.awssdk.profiles.ProfileFileSupplier;
63 import software.amazon.awssdk.profiles.ProfileFileSystemSetting;
64 import software.amazon.awssdk.utils.AttributeMap;
65 import software.amazon.awssdk.utils.CollectionUtils;
66 import software.amazon.awssdk.utils.ToString;
67 import software.amazon.awssdk.utils.Validate;
68 import software.amazon.awssdk.utils.builder.CopyableBuilder;
69 import software.amazon.awssdk.utils.builder.ToCopyableBuilder;
70 
71 /**
72  * Configuration values for which the client already provides sensible defaults. All values are optional, and not specifying them
73  * will use optimal values defined by the service itself.
74  *
75  * <p>Use {@link #builder()} to create a set of options.</p>
76  */
77 @SdkPublicApi
78 public final class ClientOverrideConfiguration
79         implements ToCopyableBuilder<ClientOverrideConfiguration.Builder, ClientOverrideConfiguration> {
80     /**
81      * The set of options modified by this ClientOverrideConfiguration. This is used when the ClientOverrideConfiguration
82      * is created from a {@link SdkClientConfiguration} to filter out properties that this object doesn't use.
83      *
84      * This is important so that unrelated configuration values don't "pass through" from when this object is created
85      * from a SdkClientConfiguration and then converted back.
86      */
87     private static final Set<ClientOption<?>> CLIENT_OVERRIDE_OPTIONS;
88 
89     /**
90      * The set of options that can be visible from this ClientOverrideConfiguration, but can't be modified directly. For
91      * example, when this ClientOverrideConfiguration is created from an SdkClientConfiguration, we want the
92      * {@link SdkClientOption#COMPRESSION_CONFIGURATION} to be visible to {@link #compressionConfiguration()} even though
93      * the setting that this object manipulates is {@link SdkClientOption#CONFIGURED_COMPRESSION_CONFIGURATION}.
94      *
95      * In practice, this means that when we create a ClientOverrideConfiguration from a SdkClientConfiguration, these
96      * values can be read by users of the ClientOverrideConfiguration, but these values won't be included in the result
97      * of {@link #asSdkClientConfiguration()}.
98      */
99     private static final Set<ClientOption<?>> RESOLVED_OPTIONS;
100 
101     static {
102         Set<ClientOption<?>> options = new HashSet<>();
103         options.add(ADDITIONAL_HTTP_HEADERS);
104         options.add(EXECUTION_INTERCEPTORS);
105         options.add(METRIC_PUBLISHERS);
106         options.add(EXECUTION_ATTRIBUTES);
107         options.add(CONFIGURED_COMPRESSION_CONFIGURATION);
108         options.add(CONFIGURED_SCHEDULED_EXECUTOR_SERVICE);
109         options.add(RETRY_POLICY);
110         options.add(API_CALL_TIMEOUT);
111         options.add(API_CALL_ATTEMPT_TIMEOUT);
112         options.add(PROFILE_FILE_SUPPLIER);
113         options.add(PROFILE_NAME);
114         CLIENT_OVERRIDE_OPTIONS = Collections.unmodifiableSet(options);
115 
116         Set<ClientOption<?>> resolvedOptions = new HashSet<>();
117         resolvedOptions.add(COMPRESSION_CONFIGURATION);
118         resolvedOptions.add(SCHEDULED_EXECUTOR_SERVICE);
119         RESOLVED_OPTIONS = Collections.unmodifiableSet(resolvedOptions);
120     }
121 
122     private final SdkClientConfiguration config;
123     private final SdkClientConfiguration resolvedConfig;
124 
125     private final Map<String, List<String>> headers;
126     private final List<ExecutionInterceptor> executionInterceptors;
127     private final List<MetricPublisher> metricPublishers;
128     private final ExecutionAttributes executionAttributes;
129 
130     /**
131      * Initialize this configuration. Private to require use of {@link #builder()}.
132      */
133     @SdkInternalApi
ClientOverrideConfiguration(SdkClientConfiguration config, SdkClientConfiguration resolvedConfig)134     ClientOverrideConfiguration(SdkClientConfiguration config, SdkClientConfiguration resolvedConfig) {
135         this.config = config;
136         this.resolvedConfig = resolvedConfig;
137 
138         // Store separately any mutable types, so that modifications to the underlying option (e.g. from the builder) would not
139         // be visible to users of this configuration
140         Map<String, List<String>> headers = config.option(ADDITIONAL_HTTP_HEADERS);
141         this.headers = headers == null
142                        ? emptyMap()
143                        : CollectionUtils.deepUnmodifiableMap(headers, () -> new TreeMap<>(String.CASE_INSENSITIVE_ORDER));
144 
145         List<ExecutionInterceptor> interceptors = config.option(EXECUTION_INTERCEPTORS);
146         this.executionInterceptors = interceptors == null
147                                      ? emptyList()
148                                      : Collections.unmodifiableList(new ArrayList<>(interceptors));
149 
150 
151         List<MetricPublisher> metricPublishers = config.option(METRIC_PUBLISHERS);
152         this.metricPublishers = metricPublishers == null
153                                 ? emptyList()
154                                 : Collections.unmodifiableList(new ArrayList<>(metricPublishers));
155 
156         ExecutionAttributes executionAttributes = config.option(EXECUTION_ATTRIBUTES);
157         this.executionAttributes = executionAttributes == null
158                                    ? new ExecutionAttributes()
159                                    : ExecutionAttributes.unmodifiableExecutionAttributes(executionAttributes);
160 
161         Validate.isPositiveOrNull(apiCallTimeout().orElse(null), "apiCallTimeout");
162         Validate.isPositiveOrNull(apiCallAttemptTimeout().orElse(null), "apiCallAttemptTimeout");
163     }
164 
165     @Override
166     @ToBuilderIgnoreField({"config", "resolvedConfig"})
toBuilder()167     public Builder toBuilder() {
168         return new DefaultBuilder(this.config.toBuilder(), this.resolvedConfig.toBuilder())
169             .headers(headers)
170             .executionInterceptors(executionInterceptors)
171             .executionAttributes(executionAttributes)
172             .metricPublishers(metricPublishers);
173     }
174 
175     /**
176      * Create a {@link Builder}, used to create a {@link ClientOverrideConfiguration}.
177      */
builder()178     public static Builder builder() {
179         return new DefaultBuilder();
180     }
181 
182     @SdkInternalApi
asSdkClientConfiguration()183     SdkClientConfiguration asSdkClientConfiguration() {
184         return config;
185     }
186 
187     /**
188      * An unmodifiable representation of the set of HTTP headers that should be sent with every request.
189      *
190      * <p>
191      * If not set, this will return an empty map.
192      *
193      * @see Builder#headers(Map)
194      */
headers()195     public Map<String, List<String>> headers() {
196         return headers;
197     }
198 
199     /**
200      * The optional retry policy that should be used when handling failure cases.
201      *
202      * @see Builder#retryPolicy(RetryPolicy)
203      */
retryPolicy()204     public Optional<RetryPolicy> retryPolicy() {
205         return Optional.ofNullable(config.option(RETRY_POLICY));
206     }
207 
208     /**
209      * Load the optional requested advanced option that was configured on the client builder.
210      *
211      * @see Builder#putAdvancedOption(SdkAdvancedClientOption, Object)
212      */
advancedOption(SdkAdvancedClientOption<T> option)213     public <T> Optional<T> advancedOption(SdkAdvancedClientOption<T> option) {
214         return Optional.ofNullable(config.option(option));
215     }
216 
217     /**
218      * An immutable collection of {@link ExecutionInterceptor}s that should be hooked into the execution of each request, in the
219      * order that they should be applied.
220      *
221      */
executionInterceptors()222     public List<ExecutionInterceptor> executionInterceptors() {
223         return executionInterceptors;
224     }
225 
226     /**
227      * The optional scheduled executor service that should be used for scheduling tasks such as async retry attempts
228      * and timeout task.
229      * <p>
230      * <b>The SDK will not automatically close the executor when the client is closed. It is the responsibility of the
231      * user to manually close the executor once all clients utilizing it have been closed.</b>
232      */
scheduledExecutorService()233     public Optional<ScheduledExecutorService> scheduledExecutorService() {
234         // If the client override configuration is accessed from a plugin or a client, we want the actual executor service we're
235         // using to be available. For that reason, we should check the SCHEDULED_EXECUTOR_SERVICE.
236         ScheduledExecutorService scheduledExecutorService = resolvedConfig.option(SCHEDULED_EXECUTOR_SERVICE);
237         if (scheduledExecutorService == null) {
238             // Unwrap the executor to ensure that read-after-write returns the same values.
239             scheduledExecutorService = unwrapUnmanagedScheduledExecutor(config.option(CONFIGURED_SCHEDULED_EXECUTOR_SERVICE));
240         }
241         return Optional.ofNullable(scheduledExecutorService);
242     }
243 
244     /**
245      * The amount of time to allow the client to complete the execution of an API call. This timeout covers the entire client
246      * execution except for marshalling. This includes request handler execution, all HTTP requests including retries,
247      * unmarshalling, etc. This value should always be positive, if present.
248      *
249      * <p>The api call timeout feature doesn't have strict guarantees on how quickly a request is aborted when the
250      * timeout is breached. The typical case aborts the request within a few milliseconds but there may occasionally be
251      * requests that don't get aborted until several seconds after the timer has been breached. Because of this, the client
252      * execution timeout feature should not be used when absolute precision is needed.
253      *
254      * <p>This may be used together with {@link #apiCallAttemptTimeout()} to enforce both a timeout on each individual HTTP
255      * request (i.e. each retry) and the total time spent on all requests across retries (i.e. the 'api call' time).
256      *
257      * @see Builder#apiCallTimeout(Duration)
258      */
apiCallTimeout()259     public Optional<Duration> apiCallTimeout() {
260         return Optional.ofNullable(config.option(API_CALL_TIMEOUT));
261     }
262 
263     /**
264      * The amount of time to wait for the http request to complete before giving up and timing out. This value should always be
265      * positive, if present.
266      *
267      * <p>The request timeout feature doesn't have strict guarantees on how quickly a request is aborted when the timeout is
268      * breached. The typical case aborts the request within a few milliseconds but there may occasionally be requests that
269      * don't get aborted until several seconds after the timer has been breached. Because of this, the request timeout
270      * feature should not be used when absolute precision is needed.
271      *
272      * <p>This may be used together with {@link #apiCallTimeout()} to enforce both a timeout on each individual HTTP
273      * request
274      * (i.e. each retry) and the total time spent on all requests across retries (i.e. the 'api call' time).
275      *
276      * @see Builder#apiCallAttemptTimeout(Duration)
277      */
apiCallAttemptTimeout()278     public Optional<Duration> apiCallAttemptTimeout() {
279         return Optional.ofNullable(config.option(API_CALL_ATTEMPT_TIMEOUT));
280     }
281 
282     /**
283      * The profile file supplier that should be used by default for all profile-based configuration in the SDK client.
284      *
285      * @see Builder#defaultProfileFileSupplier(Supplier)
286      */
defaultProfileFileSupplier()287     public Optional<Supplier<ProfileFile>> defaultProfileFileSupplier() {
288         return Optional.ofNullable(config.option(PROFILE_FILE_SUPPLIER));
289     }
290 
291     /**
292      * The profile file that should be used by default for all profile-based configuration in the SDK client.
293      *
294      * @see Builder#defaultProfileFile(ProfileFile)
295      */
defaultProfileFile()296     public Optional<ProfileFile> defaultProfileFile() {
297         return Optional.ofNullable(config.option(PROFILE_FILE_SUPPLIER)).map(Supplier::get);
298     }
299 
300     /**
301      * The profile name that should be used by default for all profile-based configuration in the SDK client.
302      *
303      * @see Builder#defaultProfileName(String)
304      */
defaultProfileName()305     public Optional<String> defaultProfileName() {
306         return Optional.ofNullable(config.option(PROFILE_NAME));
307     }
308 
309     /**
310      * The metric publishers to use to publisher metrics collected for this client.
311      *
312      * @return The metric publisher.
313      */
metricPublishers()314     public List<MetricPublisher> metricPublishers() {
315         return metricPublishers;
316     }
317 
318     /**
319      *  Returns the additional execution attributes to be added for this client.
320      *
321      * @Return Map of execution attributes.
322      */
executionAttributes()323     public ExecutionAttributes executionAttributes() {
324         return executionAttributes;
325     }
326 
327     /**
328      * The compression configuration object, which includes options to enable/disable compression and set the minimum
329      * compression threshold.
330      *
331      * @see Builder#compressionConfiguration(CompressionConfiguration)
332      */
compressionConfiguration()333     public Optional<CompressionConfiguration> compressionConfiguration() {
334 
335         // If the client override configuration is accessed from a plugin or a client, we want the compression configuration
336         // we're using to be available. For that reason, we should check the COMPRESSION_CONFIGURATION.
337         CompressionConfiguration compressionConfig = resolvedConfig.option(COMPRESSION_CONFIGURATION);
338         if (compressionConfig == null) {
339             compressionConfig = config.option(CONFIGURED_COMPRESSION_CONFIGURATION);
340         }
341         return Optional.ofNullable(compressionConfig);
342     }
343 
344     @Override
toString()345     public String toString() {
346         return ToString.builder("ClientOverrideConfiguration")
347                        .add("headers", headers())
348                        .add("retryPolicy", retryPolicy().orElse(null))
349                        .add("apiCallTimeout", apiCallTimeout().orElse(null))
350                        .add("apiCallAttemptTimeout", apiCallAttemptTimeout().orElse(null))
351                        .add("executionInterceptors", executionInterceptors())
352                        .add("profileFileSupplier", defaultProfileFileSupplier().orElse(null))
353                        .add("profileFile", defaultProfileFile().orElse(null))
354                        .add("profileName", defaultProfileName().orElse(null))
355                        .add("scheduledExecutorService", scheduledExecutorService().orElse(null))
356                        .add("compressionConfiguration", compressionConfiguration().orElse(null))
357                        .build();
358     }
359 
360     /**
361      * A builder for {@link ClientOverrideConfiguration}.
362      *
363      * <p>All implementations of this interface are mutable and not thread safe.</p>
364      */
365     public interface Builder extends CopyableBuilder<Builder, ClientOverrideConfiguration> {
366 
367         /**
368          * Add a single header to be set on the HTTP request.
369          * <p>
370          * This overrides any values for the given header set on the request by default by the SDK.
371          *
372          * <p>
373          * This overrides any values already configured with this header name in the builder.
374          *
375          * @param name The name of the header.
376          * @param value The value of the header.
377          * @return This object for method chaining.
378          */
putHeader(String name, String value)379         default Builder putHeader(String name, String value) {
380             putHeader(name, Collections.singletonList(value));
381             return this;
382         }
383 
384         /**
385          * Add a single header with multiple values to be set on the HTTP request.
386          * <p>
387          * This overrides any values for the given header set on the request by default by the SDK.
388          *
389          * <p>
390          * This overrides any values already configured with this header name in the builder.
391          *
392          * @param name The name of the header.
393          * @param values The values of the header.
394          * @return This object for method chaining.
395          */
putHeader(String name, List<String> values)396         Builder putHeader(String name, List<String> values);
397 
398         /**
399          * Configure headers to be set on the HTTP request.
400          * <p>
401          * This overrides any values for the given headers set on the request by default by the SDK.
402          *
403          * <p>
404          * This overrides any values currently configured in the builder.
405          *
406          * @param headers The set of additional headers.
407          * @return This object for method chaining.
408          */
headers(Map<String, List<String>> headers)409         Builder headers(Map<String, List<String>> headers);
410 
headers()411         Map<String, List<String>> headers();
412 
413         /**
414          * Configure the retry policy that should be used when handling failure cases.
415          *
416          * @see ClientOverrideConfiguration#retryPolicy()
417          */
retryPolicy(RetryPolicy retryPolicy)418         Builder retryPolicy(RetryPolicy retryPolicy);
419 
420         /**
421          * Configure the retry policy the should be used when handling failure cases.
422          */
retryPolicy(Consumer<RetryPolicy.Builder> retryPolicy)423         default Builder retryPolicy(Consumer<RetryPolicy.Builder> retryPolicy) {
424             return retryPolicy(RetryPolicy.builder().applyMutation(retryPolicy).build());
425         }
426 
427         /**
428          * Configure the retry mode used to determine the retry policy that is used when handling failure cases. This is
429          * shorthand for {@code retryPolicy(RetryPolicy.forRetryMode(retryMode))}, and overrides any configured retry policy on
430          * this builder.
431          */
retryPolicy(RetryMode retryMode)432         default Builder retryPolicy(RetryMode retryMode) {
433             return retryPolicy(RetryPolicy.forRetryMode(retryMode));
434         }
435 
retryPolicy()436         RetryPolicy retryPolicy();
437 
438         /**
439          * Configure a list of execution interceptors that will have access to read and modify the request and response objcets as
440          * they are processed by the SDK. These will replace any interceptors configured previously with this method or
441          * {@link #addExecutionInterceptor(ExecutionInterceptor)}.
442          *
443          * <p>
444          * The provided interceptors are executed in the order they are configured and are always later in the order than the ones
445          * automatically added by the SDK. See {@link ExecutionInterceptor} for a more detailed explanation of interceptor order.
446          *
447          * <p>
448          * This overrides any values currently configured in the builder.
449          *
450          * @see ClientOverrideConfiguration#executionInterceptors()
451          */
executionInterceptors(List<ExecutionInterceptor> executionInterceptors)452         Builder executionInterceptors(List<ExecutionInterceptor> executionInterceptors);
453 
454         /**
455          * Add an execution interceptor that will have access to read and modify the request and response objects as they are
456          * processed by the SDK.
457          *
458          * <p>
459          * Interceptors added using this method are executed in the order they are configured and are always later in the order
460          * than the ones automatically added by the SDK. See {@link ExecutionInterceptor} for a more detailed explanation of
461          * interceptor order.
462          *
463          * @see ClientOverrideConfiguration#executionInterceptors()
464          */
addExecutionInterceptor(ExecutionInterceptor executionInterceptor)465         Builder addExecutionInterceptor(ExecutionInterceptor executionInterceptor);
466 
executionInterceptors()467         List<ExecutionInterceptor> executionInterceptors();
468 
469         /**
470          * Configure the scheduled executor service that should be used for scheduling tasks such as async retry attempts
471          * and timeout task.
472          *
473          * <p>
474          * <b>The SDK will not automatically close the executor when the client is closed. It is the responsibility of the
475          * user to manually close the executor once all clients utilizing it have been closed.</b>
476          *
477          * <p>
478          * When modifying this option from an {@link SdkPlugin}, it is strongly recommended to decorate the
479          * {@link #scheduledExecutorService()}. If you will be replacing it entirely, you MUST shut it down to prevent the
480          * resources being leaked.
481          *
482          * @see ClientOverrideConfiguration#scheduledExecutorService()
483          */
scheduledExecutorService(ScheduledExecutorService scheduledExecutorService)484         Builder scheduledExecutorService(ScheduledExecutorService scheduledExecutorService);
485 
scheduledExecutorService()486         ScheduledExecutorService scheduledExecutorService();
487 
488         /**
489          * Configure an advanced override option. These values are used very rarely, and the majority of SDK customers can ignore
490          * them.
491          *
492          * @param option The option to configure.
493          * @param value The value of the option.
494          * @param <T> The type of the option.
495          */
putAdvancedOption(SdkAdvancedClientOption<T> option, T value)496         <T> Builder putAdvancedOption(SdkAdvancedClientOption<T> option, T value);
497 
498         /**
499          * Configure the map of advanced override options. This will override all values currently configured. The values in the
500          * map must match the key type of the map, or a runtime exception will be raised.
501          */
advancedOptions(Map<SdkAdvancedClientOption<?>, ?> advancedOptions)502         Builder advancedOptions(Map<SdkAdvancedClientOption<?>, ?> advancedOptions);
503 
advancedOptions()504         AttributeMap advancedOptions();
505 
506         /**
507          * Configure the amount of time to allow the client to complete the execution of an API call. This timeout covers the
508          * entire client execution except for marshalling. This includes request handler execution, all HTTP requests including
509          * retries, unmarshalling, etc. This value should always be positive, if present.
510          *
511          * <p>The api call timeout feature doesn't have strict guarantees on how quickly a request is aborted when the
512          * timeout is breached. The typical case aborts the request within a few milliseconds but there may occasionally be
513          * requests that don't get aborted until several seconds after the timer has been breached. Because of this, the client
514          * execution timeout feature should not be used when absolute precision is needed.
515          *
516          * <p>
517          * For synchronous streaming operations, implementations of {@link ResponseTransformer} must handle interrupt
518          * properly to allow the the SDK to timeout the request in a timely manner.
519          *
520          * <p>This may be used together with {@link #apiCallAttemptTimeout()} to enforce both a timeout on each individual HTTP
521          * request (i.e. each retry) and the total time spent on all requests across retries (i.e. the 'api call' time).
522          *
523          * <p>
524          * You can also configure it on a per-request basis via
525          * {@link RequestOverrideConfiguration.Builder#apiCallTimeout(Duration)}.
526          * Note that request-level timeout takes precedence.
527          *
528          * @see ClientOverrideConfiguration#apiCallTimeout()
529          */
apiCallTimeout(Duration apiCallTimeout)530         Builder apiCallTimeout(Duration apiCallTimeout);
531 
apiCallTimeout()532         Duration apiCallTimeout();
533 
534         /**
535          * Configure the amount of time to wait for the http request to complete before giving up and timing out. This value
536          * should always be positive, if present.
537          *
538          * <p>The request timeout feature doesn't have strict guarantees on how quickly a request is aborted when the timeout is
539          * breached. The typical case aborts the request within a few milliseconds but there may occasionally be requests that
540          * don't get aborted until several seconds after the timer has been breached. Because of this, the api call attempt
541          * timeout feature should not be used when absolute precision is needed.
542          *
543          * <p>For synchronous streaming operations, the process in {@link ResponseTransformer} is not timed and will not
544          * be aborted.
545          *
546          * <p>This may be used together with {@link #apiCallTimeout()} to enforce both a timeout on each individual HTTP
547          * request (i.e. each retry) and the total time spent on all requests across retries (i.e. the 'api call' time).
548          *
549          * <p>
550          * You can also configure it on a per-request basis via
551          * {@link RequestOverrideConfiguration.Builder#apiCallAttemptTimeout(Duration)}.
552          * Note that request-level timeout takes precedence.
553          *
554          * @see ClientOverrideConfiguration#apiCallAttemptTimeout()
555          */
apiCallAttemptTimeout(Duration apiCallAttemptTimeout)556         Builder apiCallAttemptTimeout(Duration apiCallAttemptTimeout);
557 
apiCallAttemptTimeout()558         Duration apiCallAttemptTimeout();
559 
560         /**
561          * Configure a {@link ProfileFileSupplier} that should be used by default for all profile-based configuration in the SDK
562          * client.
563          *
564          * <p>This is equivalent to setting {@link #defaultProfileFile(ProfileFile)}, except the supplier is read every time
565          * the configuration is requested. It's recommended to use {@link ProfileFileSupplier} that provides configurable
566          * caching for the reading of the profile file.
567          *
568          * <p>If this is not set, the {@link ProfileFile#defaultProfileFile()} is used.
569          *
570          * @see #defaultProfileFile(ProfileFile)
571          * @see #defaultProfileName(String)
572          */
defaultProfileFileSupplier(Supplier<ProfileFile> defaultProfileFile)573         Builder defaultProfileFileSupplier(Supplier<ProfileFile> defaultProfileFile);
574 
defaultProfileFileSupplier()575         Supplier<ProfileFile> defaultProfileFileSupplier();
576 
577         /**
578          * Configure the profile file that should be used by default for all profile-based configuration in the SDK client.
579          *
580          * <p>This is equivalent to setting the {@link ProfileFileSystemSetting#AWS_CONFIG_FILE} and
581          * {@link ProfileFileSystemSetting#AWS_SHARED_CREDENTIALS_FILE} environment variables or system properties.
582          *
583          * <p>Like the system settings, this value is only used when determining default values. For example, directly configuring
584          * the retry policy, credentials provider or region will mean that the configured values will be used instead of those
585          * from the profile file.
586          *
587          * <p>Like the {@code --profile} setting in the CLI, profile-based configuration loaded from this profile file has lower
588          * priority than more specific environment variables, like the {@code AWS_REGION} environment variable.
589          *
590          * <p>If this is not set, the {@link ProfileFile#defaultProfileFile()} is used.
591          *
592          * @see #defaultProfileFileSupplier(Supplier)
593          * @see #defaultProfileName(String)
594          */
defaultProfileFile(ProfileFile defaultProfileFile)595         Builder defaultProfileFile(ProfileFile defaultProfileFile);
596 
defaultProfileFile()597         ProfileFile defaultProfileFile();
598 
599         /**
600          * Configure the profile name that should be used by default for all profile-based configuration in the SDK client.
601          *
602          * <p>This is equivalent to setting the {@link ProfileFileSystemSetting#AWS_PROFILE} environment variable or system
603          * property.
604          *
605          * <p>Like the system setting, this value is only used when determining default values. For example, directly configuring
606          * the retry policy, credentials provider or region will mean that the configured values will be used instead of those
607          * from this profile.
608          *
609          * <p>If this is not set, the {@link ProfileFileSystemSetting#AWS_PROFILE} (or {@code "default"}) is used.
610          *
611          * @see #defaultProfileFile(ProfileFile)
612          */
defaultProfileName(String defaultProfileName)613         Builder defaultProfileName(String defaultProfileName);
614 
defaultProfileName()615         String defaultProfileName();
616 
617         /**
618          * Set the Metric publishers to be use to publish metrics for this client. This overwrites the current list of
619          * metric publishers set on the builder.
620          *
621          * @param metricPublishers The metric publishers.
622          */
metricPublishers(List<MetricPublisher> metricPublishers)623         Builder metricPublishers(List<MetricPublisher> metricPublishers);
624 
625 
626         /**
627          * Add a metric publisher to the existing list of previously set publishers to be used for publishing metrics
628          * for this client.
629          *
630          * @param metricPublisher The metric publisher to add.
631          */
addMetricPublisher(MetricPublisher metricPublisher)632         Builder addMetricPublisher(MetricPublisher metricPublisher);
633 
metricPublishers()634         List<MetricPublisher> metricPublishers();
635 
636         /**
637          * Sets the additional execution attributes collection for this client.
638          * @param executionAttributes Execution attributes map for this client.
639          * @return This object for method chaining.
640          */
executionAttributes(ExecutionAttributes executionAttributes)641         Builder executionAttributes(ExecutionAttributes executionAttributes);
642 
643         /**
644          * Put an execution attribute into to the existing collection of execution attributes.
645          * @param attribute The execution attribute object
646          * @param value The value of the execution attribute.
647          */
putExecutionAttribute(ExecutionAttribute<T> attribute, T value)648         <T> Builder putExecutionAttribute(ExecutionAttribute<T> attribute, T value);
649 
executionAttributes()650         ExecutionAttributes executionAttributes();
651 
652         /**
653          * Sets the {@link CompressionConfiguration} for this client.
654          */
compressionConfiguration(CompressionConfiguration compressionConfiguration)655         Builder compressionConfiguration(CompressionConfiguration compressionConfiguration);
656 
657         /**
658          * Sets the {@link CompressionConfiguration} for this client.
659          */
compressionConfiguration(Consumer<CompressionConfiguration.Builder> compressionConfiguration)660         default Builder compressionConfiguration(Consumer<CompressionConfiguration.Builder> compressionConfiguration) {
661             return compressionConfiguration(CompressionConfiguration.builder()
662                                                                     .applyMutation(compressionConfiguration)
663                                                                     .build());
664         }
665 
compressionConfiguration()666         CompressionConfiguration compressionConfiguration();
667     }
668 
669     /**
670      * An SDK-internal implementation of {@link ClientOverrideConfiguration.Builder}.
671      */
672     @SdkInternalApi
673     static final class DefaultBuilder implements Builder {
674         private final SdkClientConfiguration.Builder config;
675         private final SdkClientConfiguration.Builder resolvedConfig;
676 
677         @SdkInternalApi
DefaultBuilder(SdkClientConfiguration.Builder config)678         DefaultBuilder(SdkClientConfiguration.Builder config) {
679             this();
680             RESOLVED_OPTIONS.forEach(o -> copyValue(o, config, this.resolvedConfig));
681             CLIENT_OVERRIDE_OPTIONS.forEach(o -> copyValue(o, config, this.config));
682             SdkAdvancedClientOption.options().forEach(o -> copyValue(o, config, this.config));
683         }
684 
DefaultBuilder()685         private DefaultBuilder() {
686             this(SdkClientConfiguration.builder(), SdkClientConfiguration.builder());
687         }
688 
DefaultBuilder(SdkClientConfiguration.Builder config, SdkClientConfiguration.Builder resolvedConfig)689         private DefaultBuilder(SdkClientConfiguration.Builder config,
690                                SdkClientConfiguration.Builder resolvedConfig) {
691             this.config = config;
692             this.resolvedConfig = resolvedConfig;
693         }
694 
695         @Override
headers(Map<String, List<String>> headers)696         public Builder headers(Map<String, List<String>> headers) {
697             Validate.paramNotNull(headers, "headers");
698             this.config.option(ADDITIONAL_HTTP_HEADERS, CollectionUtils.deepCopyMap(headers, this::newHeaderMap));
699             return this;
700         }
701 
setHeaders(Map<String, List<String>> additionalHttpHeaders)702         public void setHeaders(Map<String, List<String>> additionalHttpHeaders) {
703             headers(additionalHttpHeaders);
704         }
705 
706         @Override
headers()707         public Map<String, List<String>> headers() {
708             Map<String, List<String>> option = Validate
709                 .getOrDefault(config.option(ADDITIONAL_HTTP_HEADERS), Collections::emptyMap);
710             return CollectionUtils.unmodifiableMapOfLists(option);
711         }
712 
713         @Override
putHeader(String header, List<String> values)714         public Builder putHeader(String header, List<String> values) {
715             Validate.paramNotNull(header, "header");
716             Validate.paramNotNull(values, "values");
717             config.computeOptionIfAbsent(ADDITIONAL_HTTP_HEADERS, this::newHeaderMap)
718                   .put(header, new ArrayList<>(values));
719             return this;
720         }
721 
722         @Override
retryPolicy(RetryPolicy retryPolicy)723         public Builder retryPolicy(RetryPolicy retryPolicy) {
724             config.option(RETRY_POLICY, retryPolicy);
725             return this;
726         }
727 
setRetryPolicy(RetryPolicy retryPolicy)728         public void setRetryPolicy(RetryPolicy retryPolicy) {
729             retryPolicy(retryPolicy);
730         }
731 
732         @Override
retryPolicy()733         public RetryPolicy retryPolicy() {
734             return config.option(RETRY_POLICY);
735         }
736 
737         @Override
executionInterceptors(List<ExecutionInterceptor> executionInterceptors)738         public Builder executionInterceptors(List<ExecutionInterceptor> executionInterceptors) {
739             Validate.paramNotNull(executionInterceptors, "executionInterceptors");
740             config.option(EXECUTION_INTERCEPTORS, new ArrayList<>(executionInterceptors));
741             return this;
742         }
743 
744         @Override
addExecutionInterceptor(ExecutionInterceptor executionInterceptor)745         public Builder addExecutionInterceptor(ExecutionInterceptor executionInterceptor) {
746             config.computeOptionIfAbsent(EXECUTION_INTERCEPTORS, ArrayList::new).add(executionInterceptor);
747             return this;
748         }
749 
setExecutionInterceptors(List<ExecutionInterceptor> executionInterceptors)750         public void setExecutionInterceptors(List<ExecutionInterceptor> executionInterceptors) {
751             executionInterceptors(executionInterceptors);
752         }
753 
754         @Override
executionInterceptors()755         public List<ExecutionInterceptor> executionInterceptors() {
756             List<ExecutionInterceptor> interceptors = config.option(EXECUTION_INTERCEPTORS);
757             return Collections.unmodifiableList(interceptors == null ? emptyList() : interceptors);
758         }
759 
760         @Override
scheduledExecutorService()761         public ScheduledExecutorService scheduledExecutorService() {
762             // If the client override configuration is accessed from a plugin or a client, we want the actual executor service
763             // we're using to be available. For that reason, we should check the SCHEDULED_EXECUTOR_SERVICE.
764             ScheduledExecutorService resolvedExecutor = resolvedConfig.option(SCHEDULED_EXECUTOR_SERVICE);
765             if (resolvedExecutor != null) {
766                 return resolvedExecutor;
767             }
768 
769             // Unwrap the unmanaged executor to preserve read-after-write consistency.
770             return unwrapUnmanagedScheduledExecutor(config.option(CONFIGURED_SCHEDULED_EXECUTOR_SERVICE));
771         }
772 
773         @Override
scheduledExecutorService(ScheduledExecutorService scheduledExecutorService)774         public Builder scheduledExecutorService(ScheduledExecutorService scheduledExecutorService) {
775             // For read-after-write consistency, just remove the SCHEDULED_EXECUTOR_SERVICE when this is set.
776             resolvedConfig.option(SCHEDULED_EXECUTOR_SERVICE, null);
777             config.option(CONFIGURED_SCHEDULED_EXECUTOR_SERVICE,
778                           unmanagedScheduledExecutor(scheduledExecutorService));
779             return this;
780         }
781 
782         @Override
putAdvancedOption(SdkAdvancedClientOption<T> option, T value)783         public <T> Builder putAdvancedOption(SdkAdvancedClientOption<T> option, T value) {
784             config.option(option, value);
785             return this;
786         }
787 
788         @Override
advancedOptions(Map<SdkAdvancedClientOption<?>, ?> advancedOptions)789         public Builder advancedOptions(Map<SdkAdvancedClientOption<?>, ?> advancedOptions) {
790             SdkAdvancedClientOption.options().forEach(o -> this.config.option(o, null));
791             this.config.putAll(advancedOptions);
792             return this;
793         }
794 
setAdvancedOptions(Map<SdkAdvancedClientOption<?>, Object> advancedOptions)795         public void setAdvancedOptions(Map<SdkAdvancedClientOption<?>, Object> advancedOptions) {
796             advancedOptions(advancedOptions);
797         }
798 
799         @Override
advancedOptions()800         public AttributeMap advancedOptions() {
801             AttributeMap.Builder resultBuilder = AttributeMap.builder();
802             SdkAdvancedClientOption.options().forEach(o -> setValue(o, resultBuilder));
803             return resultBuilder.build();
804         }
805 
806         @Override
apiCallTimeout(Duration apiCallTimeout)807         public Builder apiCallTimeout(Duration apiCallTimeout) {
808             config.option(API_CALL_TIMEOUT, apiCallTimeout);
809             return this;
810         }
811 
setApiCallTimeout(Duration apiCallTimeout)812         public void setApiCallTimeout(Duration apiCallTimeout) {
813             apiCallTimeout(apiCallTimeout);
814         }
815 
816         @Override
apiCallTimeout()817         public Duration apiCallTimeout() {
818             return config.option(API_CALL_TIMEOUT);
819         }
820 
821         @Override
apiCallAttemptTimeout(Duration apiCallAttemptTimeout)822         public Builder apiCallAttemptTimeout(Duration apiCallAttemptTimeout) {
823             config.option(API_CALL_ATTEMPT_TIMEOUT, apiCallAttemptTimeout);
824             return this;
825         }
826 
setApiCallAttemptTimeout(Duration apiCallAttemptTimeout)827         public void setApiCallAttemptTimeout(Duration apiCallAttemptTimeout) {
828             apiCallAttemptTimeout(apiCallAttemptTimeout);
829         }
830 
831         @Override
apiCallAttemptTimeout()832         public Duration apiCallAttemptTimeout() {
833             return config.option(API_CALL_ATTEMPT_TIMEOUT);
834         }
835 
836         @Override
defaultProfileFileSupplier(Supplier<ProfileFile> defaultProfileFileSupplier)837         public Builder defaultProfileFileSupplier(Supplier<ProfileFile> defaultProfileFileSupplier) {
838             config.option(PROFILE_FILE_SUPPLIER, defaultProfileFileSupplier);
839             return this;
840         }
841 
842         @Override
defaultProfileFileSupplier()843         public Supplier<ProfileFile> defaultProfileFileSupplier() {
844             return config.option(PROFILE_FILE_SUPPLIER);
845         }
846 
847         @Override
defaultProfileFile()848         public ProfileFile defaultProfileFile() {
849             Supplier<ProfileFile> supplier = defaultProfileFileSupplier();
850             return supplier == null ? null : supplier.get();
851         }
852 
853         @Override
defaultProfileFile(ProfileFile defaultProfileFile)854         public Builder defaultProfileFile(ProfileFile defaultProfileFile) {
855             defaultProfileFileSupplier(ProfileFileSupplier.fixedProfileFile(defaultProfileFile));
856             return this;
857         }
858 
859         @Override
defaultProfileName()860         public String defaultProfileName() {
861             return config.option(PROFILE_NAME);
862         }
863 
864         @Override
defaultProfileName(String defaultProfileName)865         public Builder defaultProfileName(String defaultProfileName) {
866             config.option(PROFILE_NAME, defaultProfileName);
867             return this;
868         }
869 
870         @Override
metricPublishers(List<MetricPublisher> metricPublishers)871         public Builder metricPublishers(List<MetricPublisher> metricPublishers) {
872             Validate.paramNotNull(metricPublishers, "metricPublishers");
873             config.option(METRIC_PUBLISHERS, new ArrayList<>(metricPublishers));
874             return this;
875         }
876 
setMetricPublishers(List<MetricPublisher> metricPublishers)877         public void setMetricPublishers(List<MetricPublisher> metricPublishers) {
878             metricPublishers(metricPublishers);
879         }
880 
881         @Override
addMetricPublisher(MetricPublisher metricPublisher)882         public Builder addMetricPublisher(MetricPublisher metricPublisher) {
883             Validate.paramNotNull(metricPublisher, "metricPublisher");
884             config.computeOptionIfAbsent(METRIC_PUBLISHERS, ArrayList::new).add(metricPublisher);
885             return this;
886         }
887 
888         @Override
metricPublishers()889         public List<MetricPublisher> metricPublishers() {
890             List<MetricPublisher> metricPublishers = config.option(METRIC_PUBLISHERS);
891             return Collections.unmodifiableList(metricPublishers == null ? emptyList() : metricPublishers);
892         }
893 
894         @Override
executionAttributes(ExecutionAttributes executionAttributes)895         public Builder executionAttributes(ExecutionAttributes executionAttributes) {
896             Validate.paramNotNull(executionAttributes, "executionAttributes");
897             config.option(EXECUTION_ATTRIBUTES, executionAttributes);
898             return this;
899         }
900 
901         @Override
putExecutionAttribute(ExecutionAttribute<T> executionAttribute, T value)902         public <T> Builder putExecutionAttribute(ExecutionAttribute<T> executionAttribute, T value) {
903             config.computeOptionIfAbsent(EXECUTION_ATTRIBUTES, ExecutionAttributes::new)
904                   .putAttribute(executionAttribute, value);
905             return this;
906         }
907 
908         @Override
executionAttributes()909         public ExecutionAttributes executionAttributes() {
910             ExecutionAttributes attributes = config.option(EXECUTION_ATTRIBUTES);
911             return attributes == null ? new ExecutionAttributes() : attributes;
912         }
913 
914         @Override
compressionConfiguration(CompressionConfiguration compressionConfiguration)915         public Builder compressionConfiguration(CompressionConfiguration compressionConfiguration) {
916             // For read-after-write consistency, just remove the COMPRESSION_CONFIGURATION when this is set.
917             resolvedConfig.option(COMPRESSION_CONFIGURATION, null);
918             config.option(CONFIGURED_COMPRESSION_CONFIGURATION, compressionConfiguration);
919             return this;
920         }
921 
setRequestCompressionEnabled(CompressionConfiguration compressionConfiguration)922         public void setRequestCompressionEnabled(CompressionConfiguration compressionConfiguration) {
923             compressionConfiguration(compressionConfiguration);
924         }
925 
926         @Override
compressionConfiguration()927         public CompressionConfiguration compressionConfiguration() {
928             // If the client override configuration is accessed from a plugin or a client, we want the actual configuration
929             // we're using to be available. For that reason, we should check the COMPRESSION_CONFIGURATION.
930             CompressionConfiguration resolvedCompressionConfig = resolvedConfig.option(COMPRESSION_CONFIGURATION);
931             if (resolvedCompressionConfig != null) {
932                 return resolvedCompressionConfig;
933             }
934             return config.option(CONFIGURED_COMPRESSION_CONFIGURATION);
935         }
936 
937         @Override
build()938         public ClientOverrideConfiguration build() {
939             return new ClientOverrideConfiguration(config.build(), resolvedConfig.build());
940         }
941 
newHeaderMap()942         private Map<String, List<String>> newHeaderMap() {
943             return new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
944         }
945 
copyValue(ClientOption<T> option, SdkClientConfiguration.Builder src, SdkClientConfiguration.Builder dst)946         private <T> void copyValue(ClientOption<T> option,
947                                    SdkClientConfiguration.Builder src,
948                                    SdkClientConfiguration.Builder dst) {
949             T value = src.option(option);
950             if (value != null) {
951                 dst.option(option, value);
952             }
953         }
954 
955 
setValue(ClientOption<T> option, AttributeMap.Builder dst)956         private <T> void setValue(ClientOption<T> option,
957                                   AttributeMap.Builder dst) {
958 
959             T value = config.option(option);
960             if (value != null) {
961                 dst.put(option, value);
962             }
963         }
964     }
965 }