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 }