/* * Copyright 2010-2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. * A copy of the License is located at * * http://aws.amazon.com/apache2.0 * * or in the "license" file accompanying this file. This file is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language governing * permissions and limitations under the License. */ package com.amazonaws; import static com.amazonaws.SDKGlobalConfiguration.PROFILING_SYSTEM_PROPERTY; import com.amazonaws.annotation.SdkInternalApi; import com.amazonaws.annotation.SdkProtectedApi; import com.amazonaws.auth.AWSCredentialsProvider; import com.amazonaws.auth.EndpointPrefixAwareSigner; import com.amazonaws.auth.RegionAwareSigner; import com.amazonaws.auth.RegionFromEndpointResolverAwareSigner; import com.amazonaws.auth.Signer; import com.amazonaws.auth.SignerFactory; import com.amazonaws.client.AwsSyncClientParams; import com.amazonaws.client.builder.AwsClientBuilder; import com.amazonaws.handlers.RequestHandler; import com.amazonaws.handlers.RequestHandler2; import com.amazonaws.http.AmazonHttpClient; import com.amazonaws.http.ExecutionContext; import com.amazonaws.internal.DefaultServiceEndpointBuilder; import com.amazonaws.internal.auth.DefaultSignerProvider; import com.amazonaws.internal.auth.SignerProvider; import com.amazonaws.internal.auth.SignerProviderContext; import com.amazonaws.log.CommonsLogFactory; import com.amazonaws.metrics.AwsSdkMetrics; import com.amazonaws.metrics.RequestMetricCollector; import com.amazonaws.monitoring.CsmConfiguration; import com.amazonaws.monitoring.CsmConfigurationProvider; import com.amazonaws.monitoring.DefaultCsmConfigurationProviderChain; import com.amazonaws.monitoring.MonitoringListener; import com.amazonaws.monitoring.internal.AgentMonitoringListener; import com.amazonaws.monitoring.internal.ClientSideMonitoringRequestHandler; import com.amazonaws.regions.EndpointToRegion; import com.amazonaws.regions.MetadataSupportedRegionFromEndpointProvider; import com.amazonaws.regions.Region; import com.amazonaws.regions.Regions; import com.amazonaws.util.AWSRequestMetrics; import com.amazonaws.util.AWSRequestMetrics.Field; import com.amazonaws.util.Classes; import com.amazonaws.util.RuntimeHttpUtils; import com.amazonaws.util.StringUtils; import java.net.URI; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * Abstract base class for Amazon Web Service Java clients. *
* Responsible for basic client capabilities that are the same across all AWS * SDK Java clients (ex: setting the client endpoint). */ public abstract class AmazonWebServiceClient { /** * @deprecated No longer used. */ @Deprecated public static final boolean LOGGING_AWS_REQUEST_METRIC = true; private static final String AMAZON = "Amazon"; private static final String AWS = "AWS"; private static final String DEFAULT_CLIENT_ID = ""; private static final Log log = LogFactory.getLog(AmazonWebServiceClient.class); static { // Configures the internal logging of the signers and core // classes to use Jakarta Commons Logging to stay consistent with the // rest of the library. boolean success = com.amazonaws.log.InternalLogFactory.configureFactory( new CommonsLogFactory()); if (log.isDebugEnabled()) log.debug("Internal logging successfully configured to commons logger: " + success); } /** * Flag indicating whether a client is mutable or not. Legacy clients built via the constructors * are mutable. Clients built with the fluent builders are immutable. */ private volatile boolean isImmutable = false; /** * The service endpoint to which this client will send requests. *
* Subclass should only read but not assign to this field, at least not
* without synchronization on the enclosing object for thread-safety
* reason. If this value is changed to effectively override the endpoint, then the 'isEndpointOverridden' property
* should also be set to 'true' within the same synchronized block of code.
*/
protected volatile URI endpoint;
/**
* A boolean flag that indicates whether the endpoint has been overridden either on construction or later mutated
* due to a call to setEndpoint(). If the endpoint property is updated directly then the method doing that update
* also has the responsibility to update this flag as part of an atomic threadsafe operation.
*/
protected volatile boolean isEndpointOverridden = false;
/**
* Used to explicitly override the internal signer region computed by the
* default implementation. This field is typically null.
*/
private volatile String signerRegionOverride;
/** The client configuration */
protected ClientConfiguration clientConfiguration;
/** Low level client for sending requests to AWS services. */
protected AmazonHttpClient client;
/** Optional request handlers for additional request processing. */
protected final List
* Note, however, the signer configured for S3 is incomplete at this stage
* as the information on the S3 bucket and key is not yet known.
*/
@Deprecated
protected Signer getSigner() {
return signerProvider.getSigner(SignerProviderContext.builder().build());
}
/**
* Returns a flag that indicates whether the endpoint for this client has been overridden or not.
* @return true if the configured endpoint is an override; false if not.
*/
@SdkProtectedApi
protected boolean isEndpointOverridden() {
return this.isEndpointOverridden;
}
/**
* @return Current SignerProvider instance.
*/
@SdkProtectedApi
protected SignerProvider getSignerProvider() {
return signerProvider;
}
/**
* Overrides the default endpoint for this client. Callers can use this
* method to control which AWS region they want to work with.
*
* This method is not threadsafe. Endpoints should be configured when the
* client is created and before any service requests are made. Changing it
* afterwards creates inevitable race conditions for any service requests in
* transit.
*
* Callers can pass in just the endpoint (ex: "ec2.amazonaws.com") or a full
* URL, including the protocol (ex: "https://ec2.amazonaws.com"). If the
* protocol is not specified here, the default protocol from this client's
* {@link ClientConfiguration} will be used, which by default is HTTPS.
*
* For more information on using AWS regions with the AWS SDK for Java, and
* a complete list of all available endpoints for all AWS services, see:
*
* https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/java-dg-region-selection.html#region-selection-choose-endpoint
*
* @param endpoint
* The endpoint (ex: "ec2.amazonaws.com") or a full URL,
* including the protocol (ex: "https://ec2.amazonaws.com") of
* the region specific AWS endpoint this client will communicate
* with.
* @throws IllegalArgumentException
* If any problems are detected with the specified endpoint.
*
* @deprecated use {@link AwsClientBuilder#setEndpointConfiguration(AwsClientBuilder.EndpointConfiguration)} for example:
* {@code builder.setEndpointConfiguration(new EndpointConfiguration(endpoint, signingRegion));}
*/
@Deprecated
public void setEndpoint(String endpoint) throws IllegalArgumentException {
checkMutability();
URI uri = toURI(endpoint);
Signer signer = computeSignerByURI(uri, signerRegionOverride, false);
synchronized (this) {
this.isEndpointOverridden = true;
this.endpoint = uri;
this.signerProvider = createSignerProvider(signer);
this.signingRegion = EndpointToRegion.guessRegionNameForEndpoint(endpoint, getEndpointPrefix());
}
}
/** Returns the endpoint as a URI. */
private URI toURI(String endpoint) throws IllegalArgumentException {
return RuntimeHttpUtils.toUri(endpoint, clientConfiguration);
}
/**
* Allows specifying the endpoint along with signing information (service name and signing region). This method will
* overwrite any information set previously by any set/with/configure Region/Endpoint methods.
*
* Overrides the default endpoint for this client
* ("http://dynamodb.us-east-1.amazonaws.com/") and explicitly provides an
* AWS region ID and AWS service name to use when the client calculates a
* signature for requests. In almost all cases, this region ID and service
* name are automatically determined from the endpoint, and callers should
* use the simpler one-argument form of setEndpoint instead of this method.
*
* Callers can pass in just the endpoint (ex:
* "dynamodb.us-east-1.amazonaws.com/") or a full URL, including the
* protocol (ex: "http://dynamodb.us-east-1.amazonaws.com/"). If the
* protocol is not specified here, the default protocol from this client's
* {@link ClientConfiguration} will be used, which by default is HTTPS.
*
* For more information on using AWS regions with the AWS SDK for Java, and
* a complete list of all available endpoints for all AWS services, see: http://developer.amazonwebservices.com/connect/entry.jspa?externalID=
* 3912
*
* @param endpoint
* The endpoint (ex: "dynamodb.us-east-1.amazonaws.com/") or a
* full URL, including the protocol (ex:
* "http://dynamodb.us-east-1.amazonaws.com/") of the region
* specific AWS endpoint this client will communicate with.
* @param serviceName
* This parameter is ignored.
* @param regionId
* The ID of the region in which this service resides AND the
* overriding region for signing purposes.
*
* @throws IllegalArgumentException
* If any problems are detected with the specified endpoint.
* @deprecated Please use the client builders instead. The
* {@link AwsClientBuilder#withEndpointConfiguration(AwsClientBuilder.EndpointConfiguration)} method on the builder allows
* setting both endpoint and signing region. See
* Creating Service Clients
* for more information.
*/
@Deprecated
public void setEndpoint(String endpoint, String serviceName, String regionId) {
URI uri = toURI(endpoint);
Signer signer = computeSignerByServiceRegion(serviceName, regionId,
regionId, true);
synchronized (this) {
setServiceNameIntern(serviceName);
this.signerProvider = createSignerProvider(signer);
this.isEndpointOverridden = true;
this.endpoint = uri;
this.signerRegionOverride = regionId;
this.signingRegion = regionId;
}
}
/**
* Returns the signer based on the given URI and the current AWS client
* configuration. Currently only the SQS client can have different region on
* a per request basis. For other AWS clients, the region remains the same
* on a per AWS client level.
*
* Note, however, the signer returned for S3 is incomplete at this stage as
* the information on the S3 bucket and key is not yet known.
*/
public Signer getSignerByURI(URI uri) {
return computeSignerByURI(uri, signerRegionOverride, true);
}
/**
* Returns the signer for the given uri and the current client
* configuration.
*
* Note, however, the signer returned for S3 is incomplete at this stage as
* the information on the S3 bucket and key is not yet known.
*
* @param signerRegionOverride
* the overriding signer region; or null if there is none.
* @param isRegionIdAsSignerParam
* true if the "regionId" is used to configure the signer if
* applicable; false if this method is called for the purpose of
* purely setting the communication end point of this AWS client,
* and therefore the "regionId" parameter will not be used
* directly for configuring the signer.
*/
private Signer computeSignerByURI(URI uri, String signerRegionOverride,
boolean isRegionIdAsSignerParam) {
if (uri == null) {
throw new IllegalArgumentException(
"Endpoint is not set. Use setEndpoint to set an endpoint before performing any request.");
}
if (uri.getHost() == null) {
throw new IllegalArgumentException("Endpoint does not contain a valid host name: " + uri);
}
String service = getServiceNameIntern();
String region = EndpointToRegion.guessRegionNameForEndpointWithDefault(uri.getHost(), getEndpointPrefix(), "us-east-1");
return computeSignerByServiceRegion(
service, region, signerRegionOverride, isRegionIdAsSignerParam);
}
/**
* Returns the signer for the given service name, region id, and the current
* client configuration.
*
* Note, however, the signer returned for S3 is incomplete at this stage as
* the information on the S3 bucket and key is not yet known.
*
* @param regionId
* the region for sending AWS requests
* @param signerRegionOverride
* the overriding signer region; or null if there is none.
* @param isRegionIdAsSignerParam
* true if the "regionId" is used to configure the signer if
* applicable; false if this method is called for the purpose of
* purely setting the communication end point of this AWS client,
* and therefore the "regionId" parameter will not be used
* directly for configuring the signer.
*/
private Signer computeSignerByServiceRegion(
String serviceName, String regionId,
String signerRegionOverride,
boolean isRegionIdAsSignerParam) {
String signerType = clientConfiguration.getSignerOverride();
Signer signer = signerType == null
? SignerFactory.getSigner(serviceName, regionId)
: SignerFactory.getSignerByTypeAndService(signerType, serviceName)
;
if (signer instanceof RegionAwareSigner) {
// Overrides the default region computed
RegionAwareSigner regionAwareSigner = (RegionAwareSigner)signer;
// (signerRegionOverride != null) means that it is likely to be AWS
// internal dev work, as "signerRegionOverride" is typically null
// when used in the external release
if (signerRegionOverride != null)
regionAwareSigner.setRegionName(signerRegionOverride);
else if (regionId != null && isRegionIdAsSignerParam)
regionAwareSigner.setRegionName(regionId);
}
if (signer instanceof EndpointPrefixAwareSigner) {
EndpointPrefixAwareSigner endpointPrefixAwareSigner = (EndpointPrefixAwareSigner) signer;
/*
* This will be used to compute the region name required for signing
* if signerRegionOverride is not provided
*/
endpointPrefixAwareSigner.setEndpointPrefix(endpointPrefix);
}
if (signer instanceof RegionFromEndpointResolverAwareSigner) {
// Allow the signer to assess the endpoints.json file for regions
RegionFromEndpointResolverAwareSigner awareSigner = (RegionFromEndpointResolverAwareSigner) signer;
awareSigner.setRegionFromEndpointResolver(new MetadataSupportedRegionFromEndpointProvider());
}
return signer;
}
/**
* An alternative to {@link AmazonWebServiceClient#setEndpoint(String)}, sets the regional
* endpoint for this client's service calls. Callers can use this method to control which AWS
* region they want to work with.
*
* This method is not threadsafe. A region should be configured when the client is created
* and before any service requests are made. Changing it afterwards creates inevitable race
* conditions for any service requests in transit or retrying.
*
* By default, all service endpoints in all regions use the https protocol. To use http instead,
* specify it in the {@link ClientConfiguration} supplied at construction.
*
* @param region
* The region this client will communicate with. See
* {@link Region#getRegion(com.amazonaws.regions.Regions)} for accessing a given
* region.
* @throws java.lang.IllegalArgumentException
* If the given region is null, or if this service isn't available in the given
* region. See {@link Region#isServiceSupported(String)}
* @see Region#getRegion(com.amazonaws.regions.Regions)
* @see Region#createClient(Class, com.amazonaws.auth.AWSCredentialsProvider,
* ClientConfiguration)
* @deprecated use {@link AwsClientBuilder#setRegion(String)}
*/
@Deprecated
public void setRegion(Region region) throws IllegalArgumentException {
checkMutability();
if (region == null) {
throw new IllegalArgumentException("No region provided");
}
final String serviceNameForEndpoint = getEndpointPrefix();
final String serviceNameForSigner = getServiceNameIntern();
URI uri = new DefaultServiceEndpointBuilder(serviceNameForEndpoint, clientConfiguration.getProtocol()
.toString()).withRegion(region).getServiceEndpoint();
Signer signer = computeSignerByServiceRegion(serviceNameForSigner, region.getName(), signerRegionOverride, false);
synchronized (this) {
this.isEndpointOverridden = false;
this.endpoint = uri;
this.signerProvider = createSignerProvider(signer);
this.signingRegion = EndpointToRegion.guessRegionNameForEndpoint(endpoint.toString(), getEndpointPrefix());
}
}
/**
* Convenient method for setting region.
*
* @param region region to set to; must not be null.
*
* @see #setRegion(Region)
* @deprecated use {@link AwsClientBuilder#setRegion(String)}
*/
@Deprecated
public final void configureRegion(Regions region) {
checkMutability();
if (region == null)
throw new IllegalArgumentException("No region provided");
this.setRegion(Region.getRegion(region));
}
/**
* Shuts down this client object, releasing any resources that might be held
* open. If this method is not invoked, resources may be leaked. Once a client
* has been shutdown, it should not be used to make any more requests.
*/
public void shutdown() {
if (agentMonitoringListener != null) {
agentMonitoringListener.shutdown();
}
client.shutdown();
}
/**
* @deprecated by {@link #addRequestHandler(RequestHandler2)}.
*
* Appends a request handler to the list of registered handlers that are run
* as part of a request's lifecycle.
*
* @param requestHandler
* The new handler to add to the current list of request
* handlers.
*/
@Deprecated
public void addRequestHandler(RequestHandler requestHandler) {
checkMutability();
requestHandler2s.add(RequestHandler2.adapt(requestHandler));
}
/**
* Appends a request handler to the list of registered handlers that are run
* as part of a request's lifecycle.
*
* @param requestHandler2
* The new handler to add to the current list of request
* handlers.
* @deprecated use {@link AwsClientBuilder#withRequestHandlers(RequestHandler2...)}
*/
@Deprecated
public void addRequestHandler(RequestHandler2 requestHandler2) {
checkMutability();
requestHandler2s.add(requestHandler2);
}
/**
* Removes a request handler from the list of registered handlers that are run
* as part of a request's lifecycle.
*
* @param requestHandler
* The handler to remove from the current list of request
* handlers.
* @deprecated use {@link AwsClientBuilder#withRequestHandlers(RequestHandler2...)}
*/
@Deprecated
public void removeRequestHandler(RequestHandler requestHandler) {
checkMutability();
requestHandler2s.remove(RequestHandler2.adapt(requestHandler));
}
/**
* @deprecated use {@link AwsClientBuilder#withRequestHandlers(RequestHandler2...)}
*/
@Deprecated
public void removeRequestHandler(RequestHandler2 requestHandler2) {
checkMutability();
requestHandler2s.remove(requestHandler2);
}
/**
* Runs the {@code beforeMarshalling} method of any
* {@code RequestHandler2}s associated with this client.
*
* @param request the request passed in from the user
* @return the (possibly different) request to marshal
*/
@SuppressWarnings("unchecked")
protected final (Amazon|AWS).*(JavaClient|Client)
.
*/
private String computeServiceName() {
final String httpClientName = getHttpClientName();
String service = ServiceNameFactory.getServiceName(httpClientName);
if (service != null) {
return service; // only if it is so explicitly configured
}
// Otherwise, make use of convention over configuration
int j = httpClientName.indexOf("JavaClient");
if (j == -1) {
j = httpClientName.indexOf("Client");
if (j == -1) {
throw new IllegalStateException(
"Unrecognized suffix for the AWS http client class name " + httpClientName);
}
}
int i = httpClientName.indexOf(AMAZON);
int len;
if (i == -1) {
i = httpClientName.indexOf(AWS);
if (i == -1) {
throw new IllegalStateException(
"Unrecognized prefix for the AWS http client class name " + httpClientName);
}
len = AWS.length();
} else {
len = AMAZON.length();
}
if (i >= j) {
throw new IllegalStateException(
"Unrecognized AWS http client class name " + httpClientName);
}
String serviceName = httpClientName.substring(i + len, j);
return StringUtils.lowerCase(serviceName);
}
private String getHttpClientName() {
Class> httpClientClass = Classes.childClassOf(AmazonWebServiceClient.class, this);
return httpClientClass.getSimpleName();
}
/**
* Returns the signer region override.
*
* @see #setSignerRegionOverride(String).
*/
public final String getSignerRegionOverride() {
return signerRegionOverride;
}
/**
* An internal method used to explicitly override the internal signer region
* computed by the default implementation. This method is not expected to be
* normally called except for AWS internal development purposes.
*/
public final void setSignerRegionOverride(String signerRegionOverride) {
checkMutability();
Signer signer = computeSignerByURI(endpoint, signerRegionOverride, true);
synchronized(this) {
this.signerRegionOverride = signerRegionOverride;
this.signerProvider = createSignerProvider(signer);
this.signingRegion = signerRegionOverride;
}
}
/**
* Fluent method for {@link #setRegion(Region)}.
*
* Example:
*
* AmazonDynamoDBClient client = new AmazonDynamoDBClient(...).
* @see #setRegion(Region)
* @deprecated use {@link AwsClientBuilder#withRegion(Region)} for example:
* {@code AmazonSNSClientBuilder.standard().withRegion(region).build();}
*/
@Deprecated
public
* Example:
*
* AmazonDynamoDBClient client = new AmazonDynamoDBClient(...).
* @see #setEndpoint(String)
* @deprecated use {@link AwsClientBuilder#withEndpointConfiguration(AwsClientBuilder.EndpointConfiguration)} for example:
* {@code AmazonSNSClientBuilder.standard().withEndpointConfiguration(new EndpointConfiguration(endpoint, signingRegion)).build();}
*/
@Deprecated
public