1 /*
2 * Copyright 2010-2020 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 package com.amazonaws;
16
17 import static com.amazonaws.SDKGlobalConfiguration.PROFILING_SYSTEM_PROPERTY;
18
19 import com.amazonaws.annotation.SdkInternalApi;
20 import com.amazonaws.annotation.SdkProtectedApi;
21 import com.amazonaws.auth.AWSCredentialsProvider;
22 import com.amazonaws.auth.EndpointPrefixAwareSigner;
23 import com.amazonaws.auth.RegionAwareSigner;
24 import com.amazonaws.auth.RegionFromEndpointResolverAwareSigner;
25 import com.amazonaws.auth.Signer;
26 import com.amazonaws.auth.SignerFactory;
27 import com.amazonaws.client.AwsSyncClientParams;
28 import com.amazonaws.client.builder.AwsClientBuilder;
29 import com.amazonaws.handlers.RequestHandler;
30 import com.amazonaws.handlers.RequestHandler2;
31 import com.amazonaws.http.AmazonHttpClient;
32 import com.amazonaws.http.ExecutionContext;
33 import com.amazonaws.internal.DefaultServiceEndpointBuilder;
34 import com.amazonaws.internal.auth.DefaultSignerProvider;
35 import com.amazonaws.internal.auth.SignerProvider;
36 import com.amazonaws.internal.auth.SignerProviderContext;
37 import com.amazonaws.log.CommonsLogFactory;
38 import com.amazonaws.metrics.AwsSdkMetrics;
39 import com.amazonaws.metrics.RequestMetricCollector;
40 import com.amazonaws.monitoring.CsmConfiguration;
41 import com.amazonaws.monitoring.CsmConfigurationProvider;
42 import com.amazonaws.monitoring.DefaultCsmConfigurationProviderChain;
43 import com.amazonaws.monitoring.MonitoringListener;
44 import com.amazonaws.monitoring.internal.AgentMonitoringListener;
45 import com.amazonaws.monitoring.internal.ClientSideMonitoringRequestHandler;
46 import com.amazonaws.regions.EndpointToRegion;
47 import com.amazonaws.regions.MetadataSupportedRegionFromEndpointProvider;
48 import com.amazonaws.regions.Region;
49 import com.amazonaws.regions.Regions;
50 import com.amazonaws.util.AWSRequestMetrics;
51 import com.amazonaws.util.AWSRequestMetrics.Field;
52 import com.amazonaws.util.Classes;
53 import com.amazonaws.util.RuntimeHttpUtils;
54 import com.amazonaws.util.StringUtils;
55 import java.net.URI;
56 import java.util.Collection;
57 import java.util.Collections;
58 import java.util.List;
59 import java.util.concurrent.CopyOnWriteArrayList;
60 import org.apache.commons.logging.Log;
61 import org.apache.commons.logging.LogFactory;
62
63 /**
64 * Abstract base class for Amazon Web Service Java clients.
65 * <p>
66 * Responsible for basic client capabilities that are the same across all AWS
67 * SDK Java clients (ex: setting the client endpoint).
68 */
69 public abstract class AmazonWebServiceClient {
70
71 /**
72 * @deprecated No longer used.
73 */
74 @Deprecated
75 public static final boolean LOGGING_AWS_REQUEST_METRIC = true;
76
77 private static final String AMAZON = "Amazon";
78 private static final String AWS = "AWS";
79 private static final String DEFAULT_CLIENT_ID = "";
80
81 private static final Log log =
82 LogFactory.getLog(AmazonWebServiceClient.class);
83
84 static {
85 // Configures the internal logging of the signers and core
86 // classes to use Jakarta Commons Logging to stay consistent with the
87 // rest of the library.
88 boolean success = com.amazonaws.log.InternalLogFactory.configureFactory(
89 new CommonsLogFactory());
90 if (log.isDebugEnabled())
91 log.debug("Internal logging successfully configured to commons logger: "
92 + success);
93 }
94
95 /**
96 * Flag indicating whether a client is mutable or not. Legacy clients built via the constructors
97 * are mutable. Clients built with the fluent builders are immutable.
98 */
99 private volatile boolean isImmutable = false;
100
101 /**
102 * The service endpoint to which this client will send requests.
103 * <p>
104 * Subclass should only read but not assign to this field, at least not
105 * without synchronization on the enclosing object for thread-safety
106 * reason. If this value is changed to effectively override the endpoint, then the 'isEndpointOverridden' property
107 * should also be set to 'true' within the same synchronized block of code.
108 */
109 protected volatile URI endpoint;
110
111 /**
112 * A boolean flag that indicates whether the endpoint has been overridden either on construction or later mutated
113 * due to a call to setEndpoint(). If the endpoint property is updated directly then the method doing that update
114 * also has the responsibility to update this flag as part of an atomic threadsafe operation.
115 */
116 protected volatile boolean isEndpointOverridden = false;
117
118 /**
119 * Used to explicitly override the internal signer region computed by the
120 * default implementation. This field is typically null.
121 */
122 private volatile String signerRegionOverride;
123
124 /** The client configuration */
125 protected ClientConfiguration clientConfiguration;
126
127 /** Low level client for sending requests to AWS services. */
128 protected AmazonHttpClient client;
129
130 /** Optional request handlers for additional request processing. */
131 protected final List<RequestHandler2> requestHandler2s;
132
133 /** Optional offset (in seconds) to use when signing requests */
134 protected int timeOffset;
135
136 private volatile SignerProvider signerProvider;
137
138 private final CsmConfiguration csmConfiguration;
139
140 /**
141 * The cached service abbreviation for this service, used for identifying
142 * service endpoints by region, identifying the necessary signer, etc.
143 * Thread safe so it's backward compatible.
144 */
145 private volatile String serviceName;
146
147 /**
148 * The service name in region metadata, i.e. the prefix of endpoint.
149 */
150 private volatile String endpointPrefix;
151
152 /**
153 * Region used to sign requests.
154 */
155 private volatile String signingRegion;
156
157 private Collection<MonitoringListener> monitoringListeners;
158
159 private AgentMonitoringListener agentMonitoringListener;
160
161 /**
162 * Constructs a new AmazonWebServiceClient object using the specified
163 * configuration.
164 *
165 * @param clientConfiguration
166 * The client configuration for this client.
167 */
168 public AmazonWebServiceClient(ClientConfiguration clientConfiguration) {
169 this(clientConfiguration, null);
170 }
171
172 /**
173 * Constructs a new AmazonWebServiceClient object using the specified
174 * configuration and request metric collector.
175 *
176 * @param clientConfiguration
177 * The client configuration for this client.
178 * @param requestMetricCollector
179 * optional request metric collector to be used at the http
180 * client level; can be null.
181 */
182 public AmazonWebServiceClient(ClientConfiguration clientConfiguration,
183 RequestMetricCollector requestMetricCollector) {
184 this(clientConfiguration, requestMetricCollector, false);
185 }
186
187 @SdkProtectedApi
188 protected AmazonWebServiceClient(final ClientConfiguration clientConfiguration,
189 final RequestMetricCollector requestMetricCollector,
190 boolean disableStrictHostNameVerification) {
191 this(new AwsSyncClientParams() {
192 @Override
193 public AWSCredentialsProvider getCredentialsProvider() {
194 return null;
195 }
196
197 @Override
198 public ClientConfiguration getClientConfiguration() {
199 return clientConfiguration;
200 }
201
202 @Override
203 public RequestMetricCollector getRequestMetricCollector() {
204 return requestMetricCollector;
205 }
206
207 @Override
208 public List<RequestHandler2> getRequestHandlers() {
209 return new CopyOnWriteArrayList<RequestHandler2>();
210 }
211
212 @Override
213 public CsmConfigurationProvider getClientSideMonitoringConfigurationProvider() {
214 return DefaultCsmConfigurationProviderChain.getInstance();
215 }
216
217 @Override
218 public MonitoringListener getMonitoringListener() {
219 return null;
220 }
221 }, !disableStrictHostNameVerification);
222 }
223
224 protected AmazonWebServiceClient(AwsSyncClientParams clientParams) {
225 this(clientParams, null);
226 }
227
228 private AmazonWebServiceClient(AwsSyncClientParams clientParams, Boolean useStrictHostNameVerification) {
229 this.clientConfiguration = clientParams.getClientConfiguration();
230 this.requestHandler2s = clientParams.getRequestHandlers();
231 this.monitoringListeners = new CopyOnWriteArrayList<MonitoringListener>();
232
233 useStrictHostNameVerification = useStrictHostNameVerification != null ? useStrictHostNameVerification
234 : useStrictHostNameVerification();
235
236 this.client = new AmazonHttpClient(clientConfiguration,
237 clientParams.getRequestMetricCollector(),
238 !useStrictHostNameVerification,
239 calculateCRC32FromCompressedData());
240 this.csmConfiguration = getCsmConfiguration(clientParams.getClientSideMonitoringConfigurationProvider());
241
242 if (isCsmEnabled()) {
243 agentMonitoringListener = new AgentMonitoringListener(csmConfiguration.getHost(), csmConfiguration.getPort());
244 monitoringListeners.add(agentMonitoringListener);
245 }
246
247 if (clientParams.getMonitoringListener() != null) {
248 monitoringListeners.add(clientParams.getMonitoringListener());
249 }
250
251 if (shouldGenerateClientSideMonitoringEvents()) {
252 requestHandler2s.add(new ClientSideMonitoringRequestHandler(getClientId(), monitoringListeners));
253 }
254 }
255
256 /**
257 * Returns the signer.
258 * <p>
259 * Note, however, the signer configured for S3 is incomplete at this stage
260 * as the information on the S3 bucket and key is not yet known.
261 */
262 @Deprecated
263 protected Signer getSigner() {
264 return signerProvider.getSigner(SignerProviderContext.builder().build());
265 }
266
267 /**
268 * Returns a flag that indicates whether the endpoint for this client has been overridden or not.
269 * @return true if the configured endpoint is an override; false if not.
270 */
271 @SdkProtectedApi
272 protected boolean isEndpointOverridden() {
273 return this.isEndpointOverridden;
274 }
275
276 /**
277 * @return Current SignerProvider instance.
278 */
279 @SdkProtectedApi
280 protected SignerProvider getSignerProvider() {
281 return signerProvider;
282 }
283
284 /**
285 * Overrides the default endpoint for this client. Callers can use this
286 * method to control which AWS region they want to work with.
287 * <p>
288 * <b>This method is not threadsafe. Endpoints should be configured when the
289 * client is created and before any service requests are made. Changing it
290 * afterwards creates inevitable race conditions for any service requests in
291 * transit.</b>
292 * <p>
293 * Callers can pass in just the endpoint (ex: "ec2.amazonaws.com") or a full
294 * URL, including the protocol (ex: "https://ec2.amazonaws.com"). If the
295 * protocol is not specified here, the default protocol from this client's
296 * {@link ClientConfiguration} will be used, which by default is HTTPS.
297 * <p>
298 * For more information on using AWS regions with the AWS SDK for Java, and
299 * a complete list of all available endpoints for all AWS services, see:
300 * <a href="https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/java-dg-region-selection.html#region-selection-choose-endpoint">
301 * https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/java-dg-region-selection.html#region-selection-choose-endpoint</a>
302 *
303 * @param endpoint
304 * The endpoint (ex: "ec2.amazonaws.com") or a full URL,
305 * including the protocol (ex: "https://ec2.amazonaws.com") of
306 * the region specific AWS endpoint this client will communicate
307 * with.
308 * @throws IllegalArgumentException
309 * If any problems are detected with the specified endpoint.
310 *
311 * @deprecated use {@link AwsClientBuilder#setEndpointConfiguration(AwsClientBuilder.EndpointConfiguration)} for example:
312 * {@code builder.setEndpointConfiguration(new EndpointConfiguration(endpoint, signingRegion));}
313 */
314 @Deprecated
315 public void setEndpoint(String endpoint) throws IllegalArgumentException {
316 checkMutability();
317 URI uri = toURI(endpoint);
318 Signer signer = computeSignerByURI(uri, signerRegionOverride, false);
319 synchronized (this) {
320 this.isEndpointOverridden = true;
321 this.endpoint = uri;
322 this.signerProvider = createSignerProvider(signer);
323 this.signingRegion = EndpointToRegion.guessRegionNameForEndpoint(endpoint, getEndpointPrefix());
324 }
325 }
326
327 /** Returns the endpoint as a URI. */
328 private URI toURI(String endpoint) throws IllegalArgumentException {
329 return RuntimeHttpUtils.toUri(endpoint, clientConfiguration);
330 }
331
332 /**
333 * Allows specifying the endpoint along with signing information (service name and signing region). This method will
334 * overwrite any information set previously by any set/with/configure Region/Endpoint methods.
335 * <p>
336 * Overrides the default endpoint for this client
337 * ("http://dynamodb.us-east-1.amazonaws.com/") and explicitly provides an
338 * AWS region ID and AWS service name to use when the client calculates a
339 * signature for requests. In almost all cases, this region ID and service
340 * name are automatically determined from the endpoint, and callers should
341 * use the simpler one-argument form of setEndpoint instead of this method.
342 * <p>
343 * Callers can pass in just the endpoint (ex:
344 * "dynamodb.us-east-1.amazonaws.com/") or a full URL, including the
345 * protocol (ex: "http://dynamodb.us-east-1.amazonaws.com/"). If the
346 * protocol is not specified here, the default protocol from this client's
347 * {@link ClientConfiguration} will be used, which by default is HTTPS.
348 * <p>
349 * For more information on using AWS regions with the AWS SDK for Java, and
350 * a complete list of all available endpoints for all AWS services, see: <a
351 * href=
352 * "http://developer.amazonwebservices.com/connect/entry.jspa?externalID=3912"
353 * > http://developer.amazonwebservices.com/connect/entry.jspa?externalID=
354 * 3912</a>
355 *
356 * @param endpoint
357 * The endpoint (ex: "dynamodb.us-east-1.amazonaws.com/") or a
358 * full URL, including the protocol (ex:
359 * "http://dynamodb.us-east-1.amazonaws.com/") of the region
360 * specific AWS endpoint this client will communicate with.
361 * @param serviceName
362 * This parameter is ignored.
363 * @param regionId
364 * The ID of the region in which this service resides AND the
365 * overriding region for signing purposes.
366 *
367 * @throws IllegalArgumentException
368 * If any problems are detected with the specified endpoint.
369 * @deprecated Please use the client builders instead. The
370 * {@link AwsClientBuilder#withEndpointConfiguration(AwsClientBuilder.EndpointConfiguration)} method on the builder allows
371 * setting both endpoint and signing region. See
372 * <a href="http://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/creating-clients.html">Creating Service Clients</a>
373 * for more information.
374 */
375 @Deprecated
376 public void setEndpoint(String endpoint, String serviceName, String regionId) {
377 URI uri = toURI(endpoint);
378 Signer signer = computeSignerByServiceRegion(serviceName, regionId,
379 regionId, true);
380 synchronized (this) {
381 setServiceNameIntern(serviceName);
382 this.signerProvider = createSignerProvider(signer);
383 this.isEndpointOverridden = true;
384 this.endpoint = uri;
385 this.signerRegionOverride = regionId;
386 this.signingRegion = regionId;
387 }
388 }
389
390 /**
391 * Returns the signer based on the given URI and the current AWS client
392 * configuration. Currently only the SQS client can have different region on
393 * a per request basis. For other AWS clients, the region remains the same
394 * on a per AWS client level.
395 * <p>
396 * Note, however, the signer returned for S3 is incomplete at this stage as
397 * the information on the S3 bucket and key is not yet known.
398 */
399 public Signer getSignerByURI(URI uri) {
400 return computeSignerByURI(uri, signerRegionOverride, true);
401 }
402
403 /**
404 * Returns the signer for the given uri and the current client
405 * configuration.
406 * <p>
407 * Note, however, the signer returned for S3 is incomplete at this stage as
408 * the information on the S3 bucket and key is not yet known.
409 *
410 * @param signerRegionOverride
411 * the overriding signer region; or null if there is none.
412 * @param isRegionIdAsSignerParam
413 * true if the "regionId" is used to configure the signer if
414 * applicable; false if this method is called for the purpose of
415 * purely setting the communication end point of this AWS client,
416 * and therefore the "regionId" parameter will not be used
417 * directly for configuring the signer.
418 */
419 private Signer computeSignerByURI(URI uri, String signerRegionOverride,
420 boolean isRegionIdAsSignerParam) {
421 if (uri == null) {
422 throw new IllegalArgumentException(
423 "Endpoint is not set. Use setEndpoint to set an endpoint before performing any request.");
424 }
425 if (uri.getHost() == null) {
426 throw new IllegalArgumentException("Endpoint does not contain a valid host name: " + uri);
427 }
428 String service = getServiceNameIntern();
429 String region = EndpointToRegion.guessRegionNameForEndpointWithDefault(uri.getHost(), getEndpointPrefix(), "us-east-1");
430 return computeSignerByServiceRegion(
431 service, region, signerRegionOverride, isRegionIdAsSignerParam);
432 }
433
434 /**
435 * Returns the signer for the given service name, region id, and the current
436 * client configuration.
437 * <p>
438 * Note, however, the signer returned for S3 is incomplete at this stage as
439 * the information on the S3 bucket and key is not yet known.
440 *
441 * @param regionId
442 * the region for sending AWS requests
443 * @param signerRegionOverride
444 * the overriding signer region; or null if there is none.
445 * @param isRegionIdAsSignerParam
446 * true if the "regionId" is used to configure the signer if
447 * applicable; false if this method is called for the purpose of
448 * purely setting the communication end point of this AWS client,
449 * and therefore the "regionId" parameter will not be used
450 * directly for configuring the signer.
451 */
452 private Signer computeSignerByServiceRegion(
453 String serviceName, String regionId,
454 String signerRegionOverride,
455 boolean isRegionIdAsSignerParam) {
456 String signerType = clientConfiguration.getSignerOverride();
457 Signer signer = signerType == null
458 ? SignerFactory.getSigner(serviceName, regionId)
459 : SignerFactory.getSignerByTypeAndService(signerType, serviceName)
460 ;
461
462 if (signer instanceof RegionAwareSigner) {
463 // Overrides the default region computed
464 RegionAwareSigner regionAwareSigner = (RegionAwareSigner)signer;
465 // (signerRegionOverride != null) means that it is likely to be AWS
466 // internal dev work, as "signerRegionOverride" is typically null
467 // when used in the external release
468 if (signerRegionOverride != null)
469 regionAwareSigner.setRegionName(signerRegionOverride);
470 else if (regionId != null && isRegionIdAsSignerParam)
471 regionAwareSigner.setRegionName(regionId);
472 }
473
474 if (signer instanceof EndpointPrefixAwareSigner) {
475 EndpointPrefixAwareSigner endpointPrefixAwareSigner = (EndpointPrefixAwareSigner) signer;
476 /*
477 * This will be used to compute the region name required for signing
478 * if signerRegionOverride is not provided
479 */
480 endpointPrefixAwareSigner.setEndpointPrefix(endpointPrefix);
481 }
482
483 if (signer instanceof RegionFromEndpointResolverAwareSigner) {
484 // Allow the signer to assess the endpoints.json file for regions
485 RegionFromEndpointResolverAwareSigner awareSigner = (RegionFromEndpointResolverAwareSigner) signer;
486 awareSigner.setRegionFromEndpointResolver(new MetadataSupportedRegionFromEndpointProvider());
487 }
488
489 return signer;
490 }
491
492 /**
493 * An alternative to {@link AmazonWebServiceClient#setEndpoint(String)}, sets the regional
494 * endpoint for this client's service calls. Callers can use this method to control which AWS
495 * region they want to work with.
496 * <p>
497 * <b>This method is not threadsafe. A region should be configured when the client is created
498 * and before any service requests are made. Changing it afterwards creates inevitable race
499 * conditions for any service requests in transit or retrying.</b>
500 * <p>
501 * By default, all service endpoints in all regions use the https protocol. To use http instead,
502 * specify it in the {@link ClientConfiguration} supplied at construction.
503 *
504 * @param region
505 * The region this client will communicate with. See
506 * {@link Region#getRegion(com.amazonaws.regions.Regions)} for accessing a given
507 * region.
508 * @throws java.lang.IllegalArgumentException
509 * If the given region is null, or if this service isn't available in the given
510 * region. See {@link Region#isServiceSupported(String)}
511 * @see Region#getRegion(com.amazonaws.regions.Regions)
512 * @see Region#createClient(Class, com.amazonaws.auth.AWSCredentialsProvider,
513 * ClientConfiguration)
514 * @deprecated use {@link AwsClientBuilder#setRegion(String)}
515 */
516 @Deprecated
517 public void setRegion(Region region) throws IllegalArgumentException {
518 checkMutability();
519 if (region == null) {
520 throw new IllegalArgumentException("No region provided");
521 }
522 final String serviceNameForEndpoint = getEndpointPrefix();
523 final String serviceNameForSigner = getServiceNameIntern();
524 URI uri = new DefaultServiceEndpointBuilder(serviceNameForEndpoint, clientConfiguration.getProtocol()
525 .toString()).withRegion(region).getServiceEndpoint();
526 Signer signer = computeSignerByServiceRegion(serviceNameForSigner, region.getName(), signerRegionOverride, false);
527 synchronized (this) {
528 this.isEndpointOverridden = false;
529 this.endpoint = uri;
530 this.signerProvider = createSignerProvider(signer);
531 this.signingRegion = EndpointToRegion.guessRegionNameForEndpoint(endpoint.toString(), getEndpointPrefix());
532 }
533 }
534
535 /**
536 * Convenient method for setting region.
537 *
538 * @param region region to set to; must not be null.
539 *
540 * @see #setRegion(Region)
541 * @deprecated use {@link AwsClientBuilder#setRegion(String)}
542 */
543 @Deprecated
544 public final void configureRegion(Regions region) {
545 checkMutability();
546 if (region == null)
547 throw new IllegalArgumentException("No region provided");
548 this.setRegion(Region.getRegion(region));
549 }
550
551 /**
552 * Shuts down this client object, releasing any resources that might be held
553 * open. If this method is not invoked, resources may be leaked. Once a client
554 * has been shutdown, it should not be used to make any more requests.
555 */
556 public void shutdown() {
557 if (agentMonitoringListener != null) {
558 agentMonitoringListener.shutdown();
559 }
560 client.shutdown();
561 }
562
563 /**
564 * @deprecated by {@link #addRequestHandler(RequestHandler2)}.
565 *
566 * Appends a request handler to the list of registered handlers that are run
567 * as part of a request's lifecycle.
568 *
569 * @param requestHandler
570 * The new handler to add to the current list of request
571 * handlers.
572 */
573 @Deprecated
574 public void addRequestHandler(RequestHandler requestHandler) {
575 checkMutability();
576 requestHandler2s.add(RequestHandler2.adapt(requestHandler));
577 }
578
579 /**
580 * Appends a request handler to the list of registered handlers that are run
581 * as part of a request's lifecycle.
582 *
583 * @param requestHandler2
584 * The new handler to add to the current list of request
585 * handlers.
586 * @deprecated use {@link AwsClientBuilder#withRequestHandlers(RequestHandler2...)}
587 */
588 @Deprecated
589 public void addRequestHandler(RequestHandler2 requestHandler2) {
590 checkMutability();
591 requestHandler2s.add(requestHandler2);
592 }
593
594 /**
595 * Removes a request handler from the list of registered handlers that are run
596 * as part of a request's lifecycle.
597 *
598 * @param requestHandler
599 * The handler to remove from the current list of request
600 * handlers.
601 * @deprecated use {@link AwsClientBuilder#withRequestHandlers(RequestHandler2...)}
602 */
603 @Deprecated
604 public void removeRequestHandler(RequestHandler requestHandler) {
605 checkMutability();
606 requestHandler2s.remove(RequestHandler2.adapt(requestHandler));
607 }
608
609 /**
610 * @deprecated use {@link AwsClientBuilder#withRequestHandlers(RequestHandler2...)}
611 */
612 @Deprecated
613 public void removeRequestHandler(RequestHandler2 requestHandler2) {
614 checkMutability();
615 requestHandler2s.remove(requestHandler2);
616 }
617
618 /**
619 * Runs the {@code beforeMarshalling} method of any
620 * {@code RequestHandler2}s associated with this client.
621 *
622 * @param request the request passed in from the user
623 * @return the (possibly different) request to marshal
624 */
625 @SuppressWarnings("unchecked")
626 protected final <T extends AmazonWebServiceRequest> T beforeMarshalling(
627 T request) {
628
629 T local = request;
630 for (RequestHandler2 handler : requestHandler2s) {
631 local = (T) handler.beforeMarshalling(local);
632 }
633 return local;
634 }
635
636 protected ExecutionContext createExecutionContext(AmazonWebServiceRequest req) {
637 return createExecutionContext(req, signerProvider);
638 }
639
640 protected ExecutionContext createExecutionContext(AmazonWebServiceRequest req,
641 SignerProvider signerProvider) {
642 boolean isMetricsEnabled = isRequestMetricsEnabled(req) || isProfilingEnabled() ||
643 shouldGenerateClientSideMonitoringEvents();
644 return ExecutionContext.builder()
645 .withRequestHandler2s(requestHandler2s)
646 .withUseRequestMetrics(isMetricsEnabled)
647 .withAwsClient(this)
648 .withSignerProvider(signerProvider).build();
649 }
650
651 protected final ExecutionContext createExecutionContext(Request<?> req) {
652 return createExecutionContext(req.getOriginalRequest());
653 }
654
655 protected SignerProvider createSignerProvider(Signer signer) {
656 return new DefaultSignerProvider(this, signer);
657 }
658
659 /* Check the profiling system property and return true if set */
660 protected static boolean isProfilingEnabled() {
661 return System.getProperty(PROFILING_SYSTEM_PROPERTY) != null;
662 }
663
664 /*
665 * Whether to generate client side monitoring events. Only generating
666 * client side monitoring events when there are monitoring listeners attached.
667 *
668 * @see ClientSideMonitoringRequestMetricCollector
669 */
670 protected boolean shouldGenerateClientSideMonitoringEvents() {
671 return !monitoringListeners.isEmpty();
672 }
673
674 /**
675 * Returns true if request metric collection is applicable to the given
676 * request; false otherwise.
677 */
678 protected final boolean isRequestMetricsEnabled(AmazonWebServiceRequest req) {
679 RequestMetricCollector c = req.getRequestMetricCollector(); // request level collector
680 if (c != null && c.isEnabled()) {
681 return true;
682 }
683 return isRMCEnabledAtClientOrSdkLevel();
684 }
685
686 /**
687 * Returns true if request metric collection is enabled at the service
688 * client or AWS SDK level request; false otherwise.
689 */
690 private boolean isRMCEnabledAtClientOrSdkLevel() {
691 RequestMetricCollector c = requestMetricCollector();
692 return c != null && c.isEnabled();
693 }
694
695 /**
696 * Sets the optional value for time offset for this client. This
697 * value will be applied to all requests processed through this client.
698 * Value is in seconds, positive values imply the current clock is "fast",
699 * negative values imply clock is slow.
700 *
701 * @param timeOffset
702 * The optional value for time offset (in seconds) for this client.
703 */
704 public void setTimeOffset(int timeOffset) {
705 checkMutability();
706 this.timeOffset = timeOffset;
707 }
708
709 /**
710 * Sets the optional value for time offset for this client. This
711 * value will be applied to all requests processed through this client.
712 * Value is in seconds, positive values imply the current clock is "fast",
713 * negative values imply clock is slow.
714 *
715 * @param timeOffset
716 * The optional value for time offset (in seconds) for this client.
717 *
718 * @return the updated web service client
719 */
720 public AmazonWebServiceClient withTimeOffset(int timeOffset) {
721 checkMutability();
722 setTimeOffset(timeOffset);
723 return this;
724 }
725
726 /**
727 * Returns the optional value for time offset for this client. This
728 * value will be applied to all requests processed through this client.
729 * Value is in seconds, positive values imply the current clock is "fast",
730 * negative values imply clock is slow.
731 *
732 * @return The optional value for time offset (in seconds) for this client.
733 */
734 public int getTimeOffset() {
735 return timeOffset;
736 }
737
738 /**
739 * Returns the client specific {@link RequestMetricCollector}; or null if
740 * there is none.
741 */
742 public RequestMetricCollector getRequestMetricsCollector() {
743 return client.getRequestMetricCollector();
744 }
745
746 /**
747 * Returns {@link MonitoringListener}; or null if there is none.
748 */
749 public Collection<MonitoringListener> getMonitoringListeners() {
750 return Collections.unmodifiableCollection(monitoringListeners);
751 }
752
753 /**
754 * Returns the client specific request metric collector if there is one; or
755 * the one at the AWS SDK level otherwise.
756 */
757 protected RequestMetricCollector requestMetricCollector() {
758 RequestMetricCollector mc = client.getRequestMetricCollector();
759 return mc == null ? AwsSdkMetrics.getRequestMetricCollector() : mc;
760 }
761
762 /**
763 * Returns the most specific request metric collector, starting from the request level, then
764 * client level, then finally the AWS SDK level.
765 */
766 private final RequestMetricCollector findRequestMetricCollector(
767 RequestMetricCollector reqLevelMetricsCollector) {
768
769 RequestMetricCollector requestMetricCollector;
770
771 if (reqLevelMetricsCollector != null) {
772 requestMetricCollector = reqLevelMetricsCollector;
773 } else if (getRequestMetricsCollector() != null) {
774 requestMetricCollector = getRequestMetricsCollector();
775 } else {
776 requestMetricCollector = AwsSdkMetrics.getRequestMetricCollector();
777 }
778 return requestMetricCollector;
779 }
780
781 /**
782 * Notify request handlers that we are about to start execution.
783 */
784 protected final <T extends AmazonWebServiceRequest> T beforeClientExecution(T request) {
785 T local = request;
786 for (RequestHandler2 handler : requestHandler2s) {
787 local = (T) handler.beforeExecution(local);
788 }
789 return local;
790 }
791
792 /**
793 * Convenient method to end the client execution without logging the
794 * awsRequestMetrics.
795 */
796 protected final void endClientExecution(
797 AWSRequestMetrics awsRequestMetrics, Request<?> request,
798 Response<?> response) {
799 this.endClientExecution(awsRequestMetrics, request, response,
800 !LOGGING_AWS_REQUEST_METRIC);
801 }
802
803 /**
804 * Common routine to end a client AWS request/response execution and collect
805 * the request metrics. Caller of this routine is responsible for starting
806 * the event for {@link Field#ClientExecuteTime} and call this method
807 * in a try-finally block.
808 *
809 * @param loggingAwsRequestMetrics deprecated and ignored
810 */
811 protected final void endClientExecution(
812 AWSRequestMetrics awsRequestMetrics, Request<?> request,
813 Response<?> response, @Deprecated boolean loggingAwsRequestMetrics) {
814 if (request != null) {
815 awsRequestMetrics.endEvent(Field.ClientExecuteTime);
816 awsRequestMetrics.getTimingInfo().endTiming();
817 RequestMetricCollector c = findRequestMetricCollector(
818 request.getOriginalRequest().getRequestMetricCollector());
819 c.collectMetrics(request, response);
820 awsRequestMetrics.log();
821 }
822 }
823
824 /**
825 * @deprecated by {@link #getServiceName()}.
826 */
827 @Deprecated
828 protected String getServiceAbbreviation() {
829 return getServiceNameIntern();
830 }
831
832 /**
833 * Returns the service abbreviation for this service, used for identifying
834 * service endpoints by region, identifying the necessary signer, etc.
835 * Used to be call "getServiceAbbreviation".
836 */
837 public String getServiceName() {
838 return getServiceNameIntern();
839 }
840
841 /**
842 * @return the service name that should be used when computing the region
843 * endpoints. This method returns the value of the
844 * regionMetadataServiceName configuration in the internal config
845 * file if such configuration is specified for the current client,
846 * otherwise it returns the same service name that is used for
847 * request signing.
848 */
849 public String getEndpointPrefix() {
850 if (endpointPrefix != null) {
851 return endpointPrefix;
852 }
853
854 String httpClientName = getHttpClientName();
855 String serviceNameInRegionMetadata = ServiceNameFactory.
856 getServiceNameInRegionMetadata(httpClientName);
857
858 synchronized (this) {
859 if (endpointPrefix != null) {
860 return endpointPrefix;
861 }
862 if (serviceNameInRegionMetadata != null) {
863 return endpointPrefix = serviceNameInRegionMetadata;
864 } else {
865 return endpointPrefix = getServiceNameIntern();
866 }
867 }
868 }
869
870 /**
871 * @return The region used to sign requests with AWS SigV4 auth.
872 */
873 @SdkProtectedApi
874 protected String getSigningRegion() {
875 return this.signingRegion;
876 }
877
878 /**
879 * An internal method used to explicitly override the service name for region metadata.
880 * This service name is used to compute the region endpoints.
881 */
882 protected void setEndpointPrefix(String endpointPrefix) {
883 if (endpointPrefix == null) {
884 throw new IllegalArgumentException(
885 "The parameter endpointPrefix must be specified!");
886 }
887 this.endpointPrefix = endpointPrefix;
888 }
889
890 /**
891 * Internal method for implementing {@link #getServiceName()}. Method is
892 * protected by intent so peculiar subclass that don't follow the class
893 * naming convention can choose to return whatever service name as needed.
894 */
895 protected String getServiceNameIntern() {
896 if (serviceName == null) {
897 synchronized (this) {
898 if (serviceName == null) {
899 return serviceName = computeServiceName();
900 }
901 }
902 }
903 return serviceName;
904 }
905
906 /**
907 * An internal method used to explicitly override the service name
908 * computed by the default implementation. This method is not expected to be
909 * normally called except for AWS internal development purposes.
910 */
911 public final void setServiceNameIntern(String serviceName) {
912 if (serviceName == null)
913 throw new IllegalArgumentException(
914 "The parameter serviceName must be specified!");
915 this.serviceName = serviceName;
916 }
917
918 /**
919 * Returns the service name of this AWS http client by first looking it up from the SDK internal
920 * configuration, and if not found, derive it from the class name of the immediate subclass of
921 * {@link AmazonWebServiceClient}. No configuration is necessary if the simple class name of the
922 * http client follows the convention of <code>(Amazon|AWS).*(JavaClient|Client)</code>.
923 */
924 private String computeServiceName() {
925 final String httpClientName = getHttpClientName();
926 String service = ServiceNameFactory.getServiceName(httpClientName);
927 if (service != null) {
928 return service; // only if it is so explicitly configured
929 }
930 // Otherwise, make use of convention over configuration
931 int j = httpClientName.indexOf("JavaClient");
932 if (j == -1) {
933 j = httpClientName.indexOf("Client");
934 if (j == -1) {
935 throw new IllegalStateException(
936 "Unrecognized suffix for the AWS http client class name " + httpClientName);
937 }
938 }
939 int i = httpClientName.indexOf(AMAZON);
940 int len;
941 if (i == -1) {
942 i = httpClientName.indexOf(AWS);
943 if (i == -1) {
944 throw new IllegalStateException(
945 "Unrecognized prefix for the AWS http client class name " + httpClientName);
946 }
947 len = AWS.length();
948 } else {
949 len = AMAZON.length();
950 }
951 if (i >= j) {
952 throw new IllegalStateException(
953 "Unrecognized AWS http client class name " + httpClientName);
954 }
955 String serviceName = httpClientName.substring(i + len, j);
956 return StringUtils.lowerCase(serviceName);
957 }
958
959 private String getHttpClientName() {
960 Class<?> httpClientClass = Classes.childClassOf(AmazonWebServiceClient.class, this);
961 return httpClientClass.getSimpleName();
962 }
963
964 /**
965 * Returns the signer region override.
966 *
967 * @see #setSignerRegionOverride(String).
968 */
969 public final String getSignerRegionOverride() {
970 return signerRegionOverride;
971 }
972
973 /**
974 * An internal method used to explicitly override the internal signer region
975 * computed by the default implementation. This method is not expected to be
976 * normally called except for AWS internal development purposes.
977 */
978 public final void setSignerRegionOverride(String signerRegionOverride) {
979 checkMutability();
980 Signer signer = computeSignerByURI(endpoint, signerRegionOverride, true);
981 synchronized(this) {
982 this.signerRegionOverride = signerRegionOverride;
983 this.signerProvider = createSignerProvider(signer);
984 this.signingRegion = signerRegionOverride;
985 }
986 }
987
988 /**
989 * Fluent method for {@link #setRegion(Region)}.
990 *<pre>
991 * Example:
992 *
993 * AmazonDynamoDBClient client = new AmazonDynamoDBClient(...).<AmazonDynamoDBClient>withRegion(...);
994 *</pre>
995 * @see #setRegion(Region)
996 * @deprecated use {@link AwsClientBuilder#withRegion(Region)} for example:
997 * {@code AmazonSNSClientBuilder.standard().withRegion(region).build();}
998 */
999 @Deprecated
1000 public <T extends AmazonWebServiceClient> T withRegion(Region region) {
1001 setRegion(region);
1002 @SuppressWarnings("unchecked") T t= (T)this;
1003 return t;
1004 }
1005
1006 /**
1007 * Convenient fluent method for setting region.
1008 *
1009 * @param region region to set to; must not be null.
1010 *
1011 * @see #withRegion(Region)
1012 * @deprecated use {@link AwsClientBuilder#withRegion(Regions)} for example:
1013 * {@code AmazonSNSClientBuilder.standard().withRegion(region).build();}
1014 */
1015 @Deprecated
1016 public <T extends AmazonWebServiceClient> T withRegion(Regions region) {
1017 configureRegion(region);
1018 @SuppressWarnings("unchecked") T t= (T)this;
1019 return t;
1020 }
1021 /**
1022 * Fluent method for {@link #setEndpoint(String)}.
1023 *<pre>
1024 * Example:
1025 *
1026 * AmazonDynamoDBClient client = new AmazonDynamoDBClient(...).<AmazonDynamoDBClient>withEndPoint(...);
1027 *</pre>
1028 * @see #setEndpoint(String)
1029 * @deprecated use {@link AwsClientBuilder#withEndpointConfiguration(AwsClientBuilder.EndpointConfiguration)} for example:
1030 * {@code AmazonSNSClientBuilder.standard().withEndpointConfiguration(new EndpointConfiguration(endpoint, signingRegion)).build();}
1031 */
1032 @Deprecated
1033 public <T extends AmazonWebServiceClient> T withEndpoint(String endpoint) {
1034 setEndpoint(endpoint);
1035 @SuppressWarnings("unchecked") T t= (T)this;
1036 return t;
1037 }
1038
1039 /**
1040 * Internal only API to lock a client's mutable methods. Only intended for use by the fluent
1041 * builders.
1042 */
1043 @Deprecated
1044 @SdkInternalApi
1045 public final void makeImmutable() {
1046 this.isImmutable = true;
1047 }
1048
1049 /**
1050 * If the client has been marked as immutable then throw an {@link
1051 * UnsupportedOperationException}, otherwise do nothing. Should be called by each mutating
1052 * method.
1053 */
1054 @SdkProtectedApi
1055 protected final void checkMutability() {
1056 if (isImmutable) {
1057 throw new UnsupportedOperationException(
1058 "Client is immutable when created with the builder.");
1059 }
1060 }
1061
1062 /**
1063 * Hook to allow S3 client to disable strict hostname verification since it uses wildcard
1064 * certificates.
1065 *
1066 * @return True if strict hostname verification should be used, false otherwise.
1067 */
1068 protected boolean useStrictHostNameVerification() {
1069 return true;
1070 }
1071
1072 /**
1073 * Hook to allow clients to override CRC32 calculation behavior. Currently, only exercised by DynamoDB.
1074 *
1075 * @return True if the service returns CRC32 checksum from the compressed data, false otherwise.
1076 */
1077 protected boolean calculateCRC32FromCompressedData() {
1078 return false;
1079 }
1080
1081 public String getSignerOverride() {
1082 return clientConfiguration.getSignerOverride();
1083 }
1084
1085 public ClientConfiguration getClientConfiguration() {
1086 return new ClientConfiguration(clientConfiguration);
1087 }
1088
1089 /**
1090 * @return {@code true} if Client Side Monitoring is enabled, {@code false}
1091 * otherwise.
1092 */
1093 protected final boolean isCsmEnabled() {
1094 return csmConfiguration != null && csmConfiguration.isEnabled();
1095 }
1096
1097 protected String getClientId() {
1098 if (csmConfiguration == null) {
1099 return DEFAULT_CLIENT_ID;
1100 }
1101 return csmConfiguration.getClientId();
1102 }
1103
1104
1105 /**
1106 * Convenience method to return {@code null} if the provider throws {@code
1107 * SdkClientException}.
1108 */
1109 private CsmConfiguration getCsmConfiguration(
1110 CsmConfigurationProvider csmConfigurationProvider) {
1111 try {
1112 return csmConfigurationProvider.getConfiguration();
1113 } catch (SdkClientException e) {
1114 return null;
1115 }
1116 }
1117 }
1118