ablog

不器用で落着きのない技術者のメモ

Athena に JDBC Driver 経由で接続してクエリを発行する

簡単な Java プログラムを作成して JDBC Driver 経由で Athena に接続してみた。CloudTrail を確認すると、JDBC接続しても JDBC Driver に同梱されている AWS SDK for java から API を実行していることが分かる。

準備

$ wget https://s3.amazonaws.com/athena-downloads/drivers/JDBC/SimbaAthenaJDBC_2.0.5/AthenaJDBC41_2.0.5.jar
import java.sql.*;
import java.util.Properties;

public class AthenaJDBCDemo {
	static final String athenaUrl ="jdbc:awsathena://AwsRegion=ap-northeast-1;";
	public static void main(String[] args) {
	Connection conn = null;
	Statement statement = null;
	try {
		Class.forName("com.simba.athena.jdbc.Driver");
		Properties info = new Properties();
		info.put("S3OutputLocation", "s3://aws-athena-query-results-<Account ID>-ap-northeast-1/");
		info.put("LogPath", "/home/ec2-user");
		info.put("LogLevel","6");
		info.put("AwsCredentialsProviderClass","com.simba.athena.amazonaws.auth.PropertiesFileCredentialsProvider");
		info.put("AwsCredentialsProviderArguments","/home/ec2-user/.athenaCredentials");
		String databaseName = "default";
		System.out.println("Connecting to Athena...");
		conn = DriverManager.getConnection(athenaUrl,info);
		System.out.println("Listing tables...");
		String sql = "show tables in "+ databaseName;
		statement = conn.createStatement();
		ResultSet rs = statement.executeQuery(sql);
		while (rs.next()) {
			//Retrieve table column.
			String name = rs.getString("tab_name");
			//Display values.
			System.out.println("Name: " + name);
		}
		rs.close();
		conn.close();
	} catch (Exception ex) {
		ex.printStackTrace();
	} finally {
    	try {
	    	if (statement != null)
		    	statement.close();
		    } catch (Exception ex) {
	    }
	    try {
		    if (conn != null)
			    conn.close();
	    	} catch (Exception ex) {
		    	ex.printStackTrace();
		    }
	    }
	    System.out.println("Finished connectivity test.");
    }
}
$ javac -classpath ./AthenaJDBC41_2.0.5.jar AthenaJDBCDemo.java
  • クレデンシャルを作成する
$ vi .athenaCredentials
accessKey=...
secretKey=...

実行する

$ java -cp .:./AthenaJDBC41_2.0.5.jar AthenaJDBCDemo
Connecting to Athena...
log4j:WARN No appenders could be found for logger (com.simba.athena.amazonaws.AmazonWebServiceClient).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
Listing tables...
Name: cloudtrail_logs_cloudtrail_123456789012_do_not_delete
Finished connectivity test.

実行結果を確認する

  • CloudTrail を確認する

  • CloudTrail のイベントの詳細

{
    "eventVersion": "1.05",
    "userIdentity": {
        "type": "IAMUser",
        "principalId": "...",
        "arn": "arn:aws:iam::...:user/...",
        "accountId": "...",
        "accessKeyId": "...",
        "userName": "..."
    },
    "eventTime": "2018-09-15T19:17:23Z",
    "eventSource": "athena.amazonaws.com",
    "eventName": "GetQueryExecution",
    "awsRegion": "ap-northeast-1",
    "sourceIPAddress": "**.***.42.228", ★EC2のグローバルIP
    "userAgent": "sbAthenaJDBCDriver/2.0, aws-sdk-java/1.11.335 Linux/4.14.62-65.117.amzn1.x86_64 OpenJDK_64-Bit_Server_VM/24.191-b01 java/1.7.0_191", ★AWS SDK for Java in JDBC Driver からアクセスしている
    "requestParameters": {
        "queryExecutionId": "c7a91af0-8965-41f9-9694-b5996f766b35"
    },
    "responseElements": null,
    "requestID": "bdb5f440-3eda-498f-a254-a668f2c9fffe",
    "eventID": "eae75fbd-37eb-4937-a652-c1e50fe2b5a0",
    "eventType": "AwsApiCall", ★ APIを発行している
    "recipientAccountId": "..."
}

{
    "eventVersion": "1.05",
    "userIdentity": {
        "type": "IAMUser",
        "principalId": "...",
        "arn": "arn:aws:iam::...:user/...",
        "accountId": "...",
        "accessKeyId": "...",
        "userName": "..."
    },
    "eventTime": "2018-09-15T19:17:24Z",
    "eventSource": "athena.amazonaws.com",
    "eventName": "GetQueryResultsStream",
    "awsRegion": "ap-northeast-1",
    "sourceIPAddress": "**.***.42.228",
    "userAgent": "sbAthenaJDBCDriver/2.0, aws-sdk-java/1.11.335 Linux/4.14.62-65.117.amzn1.x86_64 OpenJDK_64-Bit_Server_VM/24.191-b01 java/1.7.0_191",
    "requestParameters": {
        "queryExecutionId": "c7a91af0-8965-41f9-9694-b5996f766b35",
        "maxResults": 10000
    },
    "responseElements": null,
    "requestID": "62dac377-e0f2-4727-8643-982cf07f18cc",
    "eventID": "bf4426a4-bec2-4d8a-97f8-fbf3e130c9db",
    "eventType": "AwsApiCall",
    "recipientAccountId": "..."
}

補足

  • JDBC Driver を解凍してみると、
$ unzip AthenaJDBC41_2.0.5.jar
$ tree -d com
com
├── amazonaws
│&#160;&#160; └── auth
└── simba
    ├── athena
    │&#160;&#160; ├── amazonaws
    │&#160;&#160; │&#160;&#160; ├── adapters
    │&#160;&#160; │&#160;&#160; │&#160;&#160; └── types
    │&#160;&#160; │&#160;&#160; ├── annotation
    │&#160;&#160; │&#160;&#160; ├── auth
    │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── internal
    │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── policy
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── actions
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── conditions
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; └── internal
    │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── presign
    │&#160;&#160; │&#160;&#160; │&#160;&#160; └── profile
    │&#160;&#160; │&#160;&#160; │&#160;&#160;     └── internal
    │&#160;&#160; │&#160;&#160; │&#160;&#160;         └── securitytoken
    │&#160;&#160; │&#160;&#160; ├── client
    │&#160;&#160; │&#160;&#160; │&#160;&#160; └── builder
    │&#160;&#160; │&#160;&#160; ├── event
    │&#160;&#160; │&#160;&#160; │&#160;&#160; └── request
    │&#160;&#160; │&#160;&#160; ├── handlers
    │&#160;&#160; │&#160;&#160; ├── http
    │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── apache
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── client
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; └── impl
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── request
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; └── impl
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; └── utils
    │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── client
    │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── conn
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; └── ssl
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160;     └── privileged
    │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── exception
    │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── impl
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; └── client
    │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── protocol
    │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── request
    │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── response
    │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── settings
    │&#160;&#160; │&#160;&#160; │&#160;&#160; └── timers
    │&#160;&#160; │&#160;&#160; │&#160;&#160;     ├── client
    │&#160;&#160; │&#160;&#160; │&#160;&#160;     └── request
    │&#160;&#160; │&#160;&#160; ├── internal
    │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── auth
    │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── config
    │&#160;&#160; │&#160;&#160; │&#160;&#160; └── http
    │&#160;&#160; │&#160;&#160; ├── jmx
    │&#160;&#160; │&#160;&#160; │&#160;&#160; └── spi
    │&#160;&#160; │&#160;&#160; ├── log
    │&#160;&#160; │&#160;&#160; ├── metrics
    │&#160;&#160; │&#160;&#160; │&#160;&#160; └── internal
    │&#160;&#160; │&#160;&#160; ├── partitions
    │&#160;&#160; │&#160;&#160; │&#160;&#160; └── model
    │&#160;&#160; │&#160;&#160; ├── profile
    │&#160;&#160; │&#160;&#160; │&#160;&#160; └── path
    │&#160;&#160; │&#160;&#160; │&#160;&#160;     ├── config
    │&#160;&#160; │&#160;&#160; │&#160;&#160;     └── cred
    │&#160;&#160; │&#160;&#160; ├── protocol
    │&#160;&#160; │&#160;&#160; │&#160;&#160; └── json
    │&#160;&#160; │&#160;&#160; │&#160;&#160;     └── internal
    │&#160;&#160; │&#160;&#160; ├── regions
    │&#160;&#160; │&#160;&#160; ├── retry
    │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── internal
    │&#160;&#160; │&#160;&#160; │&#160;&#160; └── v2
    │&#160;&#160; │&#160;&#160; ├── sdk
    │&#160;&#160; │&#160;&#160; ├── services
    │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── athena
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; └── model
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160;     └── transform
    │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── athenastreamingservice
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; └── model
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160;     └── transform
    │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── glue
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; └── model
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160;     └── transform
    │&#160;&#160; │&#160;&#160; │&#160;&#160; └── securitytoken
    │&#160;&#160; │&#160;&#160; │&#160;&#160;     ├── internal
    │&#160;&#160; │&#160;&#160; │&#160;&#160;     └── model
    │&#160;&#160; │&#160;&#160; │&#160;&#160;         └── transform
    │&#160;&#160; │&#160;&#160; ├── transform
    │&#160;&#160; │&#160;&#160; ├── util
    │&#160;&#160; │&#160;&#160; │&#160;&#160; └── json
    │&#160;&#160; │&#160;&#160; └── waiters
    │&#160;&#160; ├── athena
    │&#160;&#160; │&#160;&#160; ├── api
    │&#160;&#160; │&#160;&#160; ├── core
    │&#160;&#160; │&#160;&#160; ├── dataengine
    │&#160;&#160; │&#160;&#160; │&#160;&#160; └── metadata
    │&#160;&#160; │&#160;&#160; ├── exceptions
    │&#160;&#160; │&#160;&#160; ├── jdbc41
    │&#160;&#160; │&#160;&#160; ├── model
    │&#160;&#160; │&#160;&#160; └── utilities
    │&#160;&#160; ├── commons
    │&#160;&#160; │&#160;&#160; └── codec
    │&#160;&#160; │&#160;&#160;     ├── binary
    │&#160;&#160; │&#160;&#160;     ├── digest
    │&#160;&#160; │&#160;&#160;     ├── language
    │&#160;&#160; │&#160;&#160;     └── net
    │&#160;&#160; ├── dsi
    │&#160;&#160; │&#160;&#160; ├── core
    │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── impl
    │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── interfaces
    │&#160;&#160; │&#160;&#160; │&#160;&#160; └── utilities
    │&#160;&#160; │&#160;&#160; ├── dataengine
    │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── filters
    │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── impl
    │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── interfaces
    │&#160;&#160; │&#160;&#160; │&#160;&#160; └── utilities
    │&#160;&#160; │&#160;&#160; ├── exceptions
    │&#160;&#160; │&#160;&#160; └── utilities
    │&#160;&#160; ├── exceptions
    │&#160;&#160; │&#160;&#160; └── jdbc4
    │&#160;&#160; ├── jdbc
    │&#160;&#160; │&#160;&#160; ├── classloader
    │&#160;&#160; │&#160;&#160; ├── common
    │&#160;&#160; │&#160;&#160; │&#160;&#160; └── utilities
    │&#160;&#160; │&#160;&#160; ├── core
    │&#160;&#160; │&#160;&#160; ├── exceptions
    │&#160;&#160; │&#160;&#160; ├── jdbc41
    │&#160;&#160; │&#160;&#160; │&#160;&#160; └── utilities
    │&#160;&#160; │&#160;&#160; └── utils
    │&#160;&#160; ├── jdbc41
    │&#160;&#160; ├── license
    │&#160;&#160; │&#160;&#160; ├── interfaces
    │&#160;&#160; │&#160;&#160; └── validators
    │&#160;&#160; ├── shaded
    │&#160;&#160; │&#160;&#160; ├── apache
    │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── commons
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── codec
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── binary
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── digest
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── language
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; └── bm
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; └── net
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── csv
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; └── logging
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160;     └── impl
    │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── http
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── annotation
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── auth
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; └── params
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── client
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── config
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── entity
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── methods
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── params
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── protocol
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; └── utils
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── concurrent
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── config
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── conn
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── params
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── routing
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── scheme
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── socket
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── ssl
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; └── util
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── cookie
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; └── params
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── entity
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── impl
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── auth
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── bootstrap
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── client
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── conn
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; └── tsccm
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── cookie
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── entity
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── execchain
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── io
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; └── pool
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── io
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── message
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── params
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── pool
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── protocol
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; ├── ssl
    │&#160;&#160; │&#160;&#160; │&#160;&#160; │&#160;&#160; └── util
    │&#160;&#160; │&#160;&#160; │&#160;&#160; └── log4j
    │&#160;&#160; │&#160;&#160; │&#160;&#160;     ├── chainsaw
    │&#160;&#160; │&#160;&#160; │&#160;&#160;     ├── config
    │&#160;&#160; │&#160;&#160; │&#160;&#160;     ├── helpers
    │&#160;&#160; │&#160;&#160; │&#160;&#160;     ├── jdbc
    │&#160;&#160; │&#160;&#160; │&#160;&#160;     ├── jmx
    │&#160;&#160; │&#160;&#160; │&#160;&#160;     ├── lf5
    │&#160;&#160; │&#160;&#160; │&#160;&#160;     │&#160;&#160; ├── config
    │&#160;&#160; │&#160;&#160; │&#160;&#160;     │&#160;&#160; ├── util
    │&#160;&#160; │&#160;&#160; │&#160;&#160;     │&#160;&#160; └── viewer
    │&#160;&#160; │&#160;&#160; │&#160;&#160;     │&#160;&#160;     ├── categoryexplorer
    │&#160;&#160; │&#160;&#160; │&#160;&#160;     │&#160;&#160;     ├── configure
    │&#160;&#160; │&#160;&#160; │&#160;&#160;     │&#160;&#160;     └── images
    │&#160;&#160; │&#160;&#160; │&#160;&#160;     ├── net
    │&#160;&#160; │&#160;&#160; │&#160;&#160;     ├── nt
    │&#160;&#160; │&#160;&#160; │&#160;&#160;     ├── or
    │&#160;&#160; │&#160;&#160; │&#160;&#160;     │&#160;&#160; ├── jms
    │&#160;&#160; │&#160;&#160; │&#160;&#160;     │&#160;&#160; └── sax
    │&#160;&#160; │&#160;&#160; │&#160;&#160;     ├── pattern
    │&#160;&#160; │&#160;&#160; │&#160;&#160;     ├── rewrite
    │&#160;&#160; │&#160;&#160; │&#160;&#160;     ├── spi
    │&#160;&#160; │&#160;&#160; │&#160;&#160;     ├── varia
    │&#160;&#160; │&#160;&#160; │&#160;&#160;     └── xml
    │&#160;&#160; │&#160;&#160; ├── fasterxml
    │&#160;&#160; │&#160;&#160; │&#160;&#160; └── jackson
    │&#160;&#160; │&#160;&#160; │&#160;&#160;     ├── annotation
    │&#160;&#160; │&#160;&#160; │&#160;&#160;     ├── core
    │&#160;&#160; │&#160;&#160; │&#160;&#160;     │&#160;&#160; ├── async
    │&#160;&#160; │&#160;&#160; │&#160;&#160;     │&#160;&#160; ├── base
    │&#160;&#160; │&#160;&#160; │&#160;&#160;     │&#160;&#160; ├── filter
    │&#160;&#160; │&#160;&#160; │&#160;&#160;     │&#160;&#160; ├── format
    │&#160;&#160; │&#160;&#160; │&#160;&#160;     │&#160;&#160; ├── io
    │&#160;&#160; │&#160;&#160; │&#160;&#160;     │&#160;&#160; ├── json
    │&#160;&#160; │&#160;&#160; │&#160;&#160;     │&#160;&#160; │&#160;&#160; └── async
    │&#160;&#160; │&#160;&#160; │&#160;&#160;     │&#160;&#160; ├── sym
    │&#160;&#160; │&#160;&#160; │&#160;&#160;     │&#160;&#160; ├── type
    │&#160;&#160; │&#160;&#160; │&#160;&#160;     │&#160;&#160; └── util
    │&#160;&#160; │&#160;&#160; │&#160;&#160;     └── databind
    │&#160;&#160; │&#160;&#160; │&#160;&#160;         ├── annotation
    │&#160;&#160; │&#160;&#160; │&#160;&#160;         ├── cfg
    │&#160;&#160; │&#160;&#160; │&#160;&#160;         ├── deser
    │&#160;&#160; │&#160;&#160; │&#160;&#160;         │&#160;&#160; ├── impl
    │&#160;&#160; │&#160;&#160; │&#160;&#160;         │&#160;&#160; └── std
    │&#160;&#160; │&#160;&#160; │&#160;&#160;         ├── exc
    │&#160;&#160; │&#160;&#160; │&#160;&#160;         ├── ext
    │&#160;&#160; │&#160;&#160; │&#160;&#160;         ├── introspect
    │&#160;&#160; │&#160;&#160; │&#160;&#160;         ├── jsonFormatVisitors
    │&#160;&#160; │&#160;&#160; │&#160;&#160;         ├── jsonschema
    │&#160;&#160; │&#160;&#160; │&#160;&#160;         ├── jsontype
    │&#160;&#160; │&#160;&#160; │&#160;&#160;         │&#160;&#160; └── impl
    │&#160;&#160; │&#160;&#160; │&#160;&#160;         ├── module
    │&#160;&#160; │&#160;&#160; │&#160;&#160;         ├── node
    │&#160;&#160; │&#160;&#160; │&#160;&#160;         ├── ser
    │&#160;&#160; │&#160;&#160; │&#160;&#160;         │&#160;&#160; ├── impl
    │&#160;&#160; │&#160;&#160; │&#160;&#160;         │&#160;&#160; └── std
    │&#160;&#160; │&#160;&#160; │&#160;&#160;         ├── type
    │&#160;&#160; │&#160;&#160; │&#160;&#160;         └── util
    │&#160;&#160; │&#160;&#160; └── joda
    │&#160;&#160; │&#160;&#160;     └── time
    │&#160;&#160; │&#160;&#160;         ├── base
    │&#160;&#160; │&#160;&#160;         ├── chrono
    │&#160;&#160; │&#160;&#160;         ├── convert
    │&#160;&#160; │&#160;&#160;         ├── field
    │&#160;&#160; │&#160;&#160;         ├── format
    │&#160;&#160; │&#160;&#160;         └── tz
    │&#160;&#160; │&#160;&#160;             └── data
    │&#160;&#160; │&#160;&#160;                 ├── Africa
    │&#160;&#160; │&#160;&#160;                 ├── America
    │&#160;&#160; │&#160;&#160;                 │&#160;&#160; ├── Argentina
    │&#160;&#160; │&#160;&#160;                 │&#160;&#160; ├── Indiana
    │&#160;&#160; │&#160;&#160;                 │&#160;&#160; ├── Kentucky
    │&#160;&#160; │&#160;&#160;                 │&#160;&#160; └── North_Dakota
    │&#160;&#160; │&#160;&#160;                 ├── Antarctica
    │&#160;&#160; │&#160;&#160;                 ├── Arctic
    │&#160;&#160; │&#160;&#160;                 ├── Asia
    │&#160;&#160; │&#160;&#160;                 ├── Atlantic
    │&#160;&#160; │&#160;&#160;                 ├── Australia
    │&#160;&#160; │&#160;&#160;                 ├── Etc
    │&#160;&#160; │&#160;&#160;                 ├── Europe
    │&#160;&#160; │&#160;&#160;                 ├── Indian
    │&#160;&#160; │&#160;&#160;                 └── Pacific
    │&#160;&#160; ├── streams
    │&#160;&#160; │&#160;&#160; ├── parameters
    │&#160;&#160; │&#160;&#160; ├── parametersoutput
    │&#160;&#160; │&#160;&#160; ├── resultset
    │&#160;&#160; │&#160;&#160; └── resultsetinput
    │&#160;&#160; ├── support
    │&#160;&#160; │&#160;&#160; ├── channels
    │&#160;&#160; │&#160;&#160; ├── conv
    │&#160;&#160; │&#160;&#160; ├── exceptions
    │&#160;&#160; │&#160;&#160; └── security
    │&#160;&#160; └── utilities
    │&#160;&#160;     └── conversion
    └── support

274 directories
$ ls -l|head -10
total 296
-rw-rw-r--  1 ec2-user ec2-user   916 Jul 13 03:56 AbortedException.class
drwxrwxr-x  3 ec2-user ec2-user  4096 Jul 13 03:56 adapters
-rw-rw-r--  1 ec2-user ec2-user   957 Jul 13 03:56 AmazonClientException.class
-rw-rw-r--  1 ec2-user ec2-user  4172 Jul 13 03:56 AmazonServiceException.class
-rw-rw-r--  1 ec2-user ec2-user  1336 Jul 13 03:56 AmazonServiceException$ErrorType.class
-rw-rw-r--  1 ec2-user ec2-user 21418 Jul 13 03:56 AmazonWebServiceClient.class
-rw-rw-r--  1 ec2-user ec2-user   669 Jul 13 03:56 AmazonWebServiceRequest$1.class
-rw-rw-r--  1 ec2-user ec2-user 10498 Jul 13 03:56 AmazonWebServiceRequest.class
-rw-rw-r--  1 ec2-user ec2-user  1487 Jul 13 03:56 AmazonWebServiceResponse.class
/*
 * Copyright 2010-2018 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 com.amazonaws.annotation.NotThreadSafe;
import com.amazonaws.annotation.SdkInternalApi;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.event.ProgressListener;
import com.amazonaws.handlers.HandlerContextKey;
import com.amazonaws.internal.StaticCredentialsProvider;
import com.amazonaws.metrics.RequestMetricCollector;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * Base class for all user facing web service requests.
 */
@NotThreadSafe
public abstract class AmazonWebServiceRequest implements Cloneable, ReadLimitInfo, HandlerContextAware {

    public static final AmazonWebServiceRequest NOOP = new AmazonWebServiceRequest() {
    };

    /**
     * The optional progress listener for receiving updates about the progress of the request.
     */
    private ProgressListener progressListener = ProgressListener.NOOP;

    /**
     * Arbitrary options storage for individual {@link AmazonWebServiceRequest}s. This field is not
     * intended to be used by clients.
     */
    private final RequestClientOptions requestClientOptions = new RequestClientOptions();

    /**
     * A request metric collector used for this specific service request; or null if there is none.
     * This collector always takes precedence over the ones specified at the http client level and
     * AWS SDK level.
     */
    private RequestMetricCollector requestMetricCollector;

    /**
     * The optional credentials to use for this request - overrides the default credentials set at
     * the client level.
     */
    private AWSCredentialsProvider credentialsProvider;

    /**
     * A map of custom header names to header values.
     */
    private Map<String, String> customRequestHeaders;

    /**
     * Custom query parameters for the request.
     */
    private Map<String, List<String>> customQueryParameters;

    /**
     * User-defined context for the request.
     */
    private transient Map<HandlerContextKey<?>, Object> handlerContext = new HashMap<HandlerContextKey<?>, Object>();

    /**
     * The source object from which the current object was cloned; or null if there isn't one.
     */
    private AmazonWebServiceRequest cloneSource;

    private Integer sdkRequestTimeout = null;

    private Integer sdkClientExecutionTimeout = null;

    /**
     * Sets the optional credentials to use for this request, overriding the default credentials set at the client level.
     *
     * @param credentials The optional AWS security credentials to use for this request, overriding the default credentials set at
     *                    the client level.
     * @deprecated by {@link #setRequestCredentialsProvider(AWSCredentialsProvider)}. If you must use {@link AWSCredentials} you
     * can wrap it with a {@link com.amazonaws.auth.AWSStaticCredentialsProvider}.
     */
    @Deprecated
    public void setRequestCredentials(AWSCredentials credentials) {
        this.credentialsProvider = credentials == null ? null : new StaticCredentialsProvider(credentials);
    }

    /**
     * Returns the optional credentials to use to sign this request, overriding the default
     * credentials set at the client level.
     *
     * @return The optional credentials to use to sign this request, overriding the default
     *         credentials set at the client level.
     *
     * @deprecated by {@link #getRequestCredentialsProvider()}
     */
    @Deprecated
    public AWSCredentials getRequestCredentials() {
        return credentialsProvider == null ? null : credentialsProvider.getCredentials();
    }

    /**
     * Sets the optional credentials provider to use for this request, overriding the default credentials
     * provider at the client level.
     *
     * @param credentialsProvider
     *            The optional AWS security credentials provider to use for this request, overriding the
     *            default credentials provider at the client level.
     */
    public void setRequestCredentialsProvider(AWSCredentialsProvider credentialsProvider) {
        this.credentialsProvider = credentialsProvider;
    }

    /**
     * Returns the optional credentials provider to use to sign this request, overriding the default
     * credentials provider at the client level.
     *
     * @return The optional credentials provider to use to sign this request, overriding the default
     *         credentials provider at the client level.
     */
    public AWSCredentialsProvider getRequestCredentialsProvider() {
        return credentialsProvider;
    }

    /**
     * Sets the optional credentials provider to use for this request, overriding the default credentials
     * provider at the client level.
     *
     * @param credentialsProvider
     *            The optional AWS security credentials provider to use for this request, overriding the
     *            default credentials provider at the client level.
     * @return A reference to this updated object so that method calls can be chained together.
     */
    public <T extends AmazonWebServiceRequest> T withRequestCredentialsProvider(final AWSCredentialsProvider credentialsProvider) {
        setRequestCredentialsProvider(credentialsProvider);
        @SuppressWarnings("unchecked")
        T t = (T) this;
        return t;
    }

    /**
     * Gets the options stored with this request object. Intended for internal use only.
     */
    public RequestClientOptions getRequestClientOptions() {
        return requestClientOptions;
    }

    /**
     * Returns a request level metric collector; or null if not specified.
     */
    public RequestMetricCollector getRequestMetricCollector() {
        return requestMetricCollector;
    }

    /**
     * Sets a request level request metric collector which takes precedence over the ones at the
     * http client level and AWS SDK level.
     */
    public void setRequestMetricCollector(RequestMetricCollector requestMetricCollector) {
        this.requestMetricCollector = requestMetricCollector;
    }

    /**
     * Specifies a request level metric collector which takes precedence over the ones at the http
     * client level and AWS SDK level.
     */
    public <T extends AmazonWebServiceRequest> T withRequestMetricCollector(RequestMetricCollector metricCollector) {
        setRequestMetricCollector(metricCollector);
        @SuppressWarnings("unchecked")
        T t = (T) this;
        return t;
    }

    /**
     * Sets the optional progress listener for receiving updates about the progress of the request.
     *
     * @param progressListener
     *            The new progress listener.
     */
    public void setGeneralProgressListener(ProgressListener progressListener) {
        this.progressListener = progressListener == null ? ProgressListener.NOOP : progressListener;
    }

    /**
     * Returns the optional progress listener for receiving updates about the progress of the
     * request.
     *
     * @return the optional progress listener for receiving updates about the progress of the
     *         request.
     */
    public ProgressListener getGeneralProgressListener() {
        return progressListener;
    }

    /**
     * Sets the optional progress listener for receiving updates about the progress of the request,
     * and returns a reference to this object so that method calls can be chained together.
     *
     * @param progressListener
     *            The new progress listener.
     * @return A reference to this updated object so that method calls can be chained together.
     */
    public <T extends AmazonWebServiceRequest> T withGeneralProgressListener(ProgressListener progressListener) {
        setGeneralProgressListener(progressListener);
        @SuppressWarnings("unchecked")
        T t = (T) this;
        return t;
    }

    /**
     * Returns an immutable map of custom header names to header values.
     *
     * @return The immutable map of custom header names to header values.
     */
    public Map<String, String> getCustomRequestHeaders() {
        if (customRequestHeaders == null) {
            return null;
        }
        return Collections.unmodifiableMap(customRequestHeaders);
    }

    /**
     * Put a new custom header to the map of custom header names to custom header values, and return
     * the previous value if the header has already been set in this map.
     * <p>
     * Any custom headers that are defined are used in the HTTP request to the AWS service. These
     * headers will be silently ignored in the event that AWS does not recognize them.
     * <p>
     * NOTE: Custom header values set via this method will overwrite any conflicting values coming
     * from the request parameters.
     *
     * @param name
     *            The name of the header to add
     * @param value
     *            The value of the header to add
     * @return the previous value for the name if it was set, null otherwise
     */
    public String putCustomRequestHeader(String name, String value) {
        if (customRequestHeaders == null) {
            customRequestHeaders = new HashMap<String, String>();
        }
        return customRequestHeaders.put(name, value);
    }

    /**
     * @return the immutable map of custom query parameters. The parameter value is modeled as a
     *         list of strings because multiple values can be specified for the same parameter name.
     */
    public Map<String, List<String>> getCustomQueryParameters() {
        if (customQueryParameters == null) {
            return null;
        }
        return Collections.unmodifiableMap(customQueryParameters);
    }

    /**
     * Add a custom query parameter for the request. Since multiple values are allowed for the same
     * query parameter, this method does NOT overwrite any existing parameter values in the request.
     * <p>
     * Any custom query parameters that are defined are used in the HTTP request to the AWS service.
     *
     * @param name
     *            The name of the query parameter
     * @param value
     *            The value of the query parameter. Only the parameter name will be added in the URI
     *            if the value is set to null. For example, putCustomQueryParameter("param", null)
     *            will be serialized to "?param", while putCustomQueryParameter("param", "") will be
     *            serialized to "?param=".
     */
    public void putCustomQueryParameter(String name, String value) {
        if (customQueryParameters == null) {
            customQueryParameters = new HashMap<String, List<String>>();
        }
        List<String> paramList = customQueryParameters.get(name);
        if (paramList == null) {
            paramList = new LinkedList<String>();
            customQueryParameters.put(name, paramList);
        }
        paramList.add(value);
    }

    @Override
    public final int getReadLimit() {
        return requestClientOptions.getReadLimit();
    }

    /**
     * Copies the internal state of this base class to that of the target request.
     *
     * @return the target request
     */
    protected final <T extends AmazonWebServiceRequest> T copyBaseTo(T target) {
        if (customRequestHeaders != null) {
            for (Map.Entry<String, String> e : customRequestHeaders.entrySet())
                target.putCustomRequestHeader(e.getKey(), e.getValue());
        }
        if (customQueryParameters != null) {
            for (Map.Entry<String, List<String>> e : customQueryParameters.entrySet()) {
                if (e.getValue() != null) {
                    for (String value : e.getValue()) {
                        target.putCustomQueryParameter(e.getKey(), value);
                    }
                }
            }
        }

        target.setRequestCredentialsProvider(credentialsProvider);
        target.setGeneralProgressListener(progressListener);
        target.setRequestMetricCollector(requestMetricCollector);
        requestClientOptions.copyTo(target.getRequestClientOptions());
        return target;
    }

    /**
     * Returns the source object from which the current object was cloned; or null if there isn't
     * one.
     */
    public AmazonWebServiceRequest getCloneSource() {
        return cloneSource;
    }

    /**
     * Returns the root object from which the current object was cloned; or null if there isn't one.
     */
    public AmazonWebServiceRequest getCloneRoot() {
        AmazonWebServiceRequest cloneRoot = cloneSource;
        if (cloneRoot != null) {
            while (cloneRoot.getCloneSource() != null) {
                cloneRoot = cloneRoot.getCloneSource();
            }
        }
        return cloneRoot;
    }

    private void setCloneSource(AmazonWebServiceRequest cloneSource) {
        this.cloneSource = cloneSource;
    }

    /**
     * Returns the amount of time to wait (in milliseconds) for the request to complete before
     * giving up and timing out. A non-positive value disables this feature.
     * <p>
     * This feature requires buffering the entire response (for non-streaming APIs) into memory to
     * enforce a hard timeout when reading the response. For APIs that return large responses this
     * could be expensive.
     * <p>
     * <p>
     * The request timeout feature doesn't have strict guarantees on how quickly a request is
     * aborted when the timeout is breached. The typical case aborts the request within a few
     * milliseconds but there may occasionally be requests that don't get aborted until several
     * seconds after the timer has been breached. Because of this the request timeout feature should
     * not be used when absolute precision is needed.
     * </p>
     * <p>
     * <b>Note:</b> This feature is not compatible with Java 1.6.
     * </p>
     *
     * @return The amount of time to wait (in milliseconds) for the request to complete before
     *         giving up and timing out. A non-positive value disables the timeout for this request.
     * @see {@link AmazonWebServiceRequest#setSdkClientExecutionTimeout(int)} to enforce a timeout
     *      across all retries
     */
    public Integer getSdkRequestTimeout() {
        return sdkRequestTimeout;
    }

    /**
     * Sets the amount of time to wait (in milliseconds) for the request to complete before giving
     * up and timing out. A non-positive value disables this feature.
     * <p>
     * This feature requires buffering the entire response (for non-streaming APIs) into memory to
     * enforce a hard timeout when reading the response. For APIs that return large responses this
     * could be expensive.
     * <p>
     * <p>
     * The request timeout feature doesn't have strict guarantees on how quickly a request is
     * aborted when the timeout is breached. The typical case aborts the request within a few
     * milliseconds but there may occasionally be requests that don't get aborted until several
     * seconds after the timer has been breached. Because of this the request timeout feature should
     * not be used when absolute precision is needed.
     * </p>
     * <p>
     * <b>Note:</b> This feature is not compatible with Java 1.6.
     * </p>
     *
     * @param sdkRequestTimeout
     *            The amount of time to wait (in milliseconds) for the request to complete before
     *            giving up and timing out. A non-positive value disables the timeout for this
     *            request.
     * @see {@link AmazonWebServiceRequest#setSdkClientExecutionTimeout(int)} to enforce a timeout
     *      across all retries
     */
    public void setSdkRequestTimeout(int sdkRequestTimeout) {
        this.sdkRequestTimeout = sdkRequestTimeout;
    }

    /**
     * Sets the amount of time to wait (in milliseconds) for the request to complete before giving
     * up and timing out. A non-positive value disables this feature. Returns the updated
     * AmazonWebServiceRequest object so that additional method calls may be chained together.
     * <p>
     * This feature requires buffering the entire response (for non-streaming APIs) into memory to
     * enforce a hard timeout when reading the response. For APIs that return large responses this
     * could be expensive.
     * <p>
     * <p>
     * The request timeout feature doesn't have strict guarantees on how quickly a request is
     * aborted when the timeout is breached. The typical case aborts the request within a few
     * milliseconds but there may occasionally be requests that don't get aborted until several
     * seconds after the timer has been breached. Because of this the request timeout feature should
     * not be used when absolute precision is needed.
     * </p>
     * <p>
     * <b>Note:</b> This feature is not compatible with Java 1.6.
     * </p>
     *
     * @param sdkRequestTimeout
     *            The amount of time to wait (in milliseconds) for the request to complete before
     *            giving up and timing out. A non-positive value disables the timeout for this
     *            request.
     * @return The updated {@link AmazonWebServiceRequest} object.
     * @see {@link AmazonWebServiceRequest#setSdkClientExecutionTimeout(int)} to enforce a timeout
     *      across all retries
     */
    public <T extends AmazonWebServiceRequest> T withSdkRequestTimeout(int sdkRequestTimeout) {
        setSdkRequestTimeout(sdkRequestTimeout);
        @SuppressWarnings("unchecked")
        T t = (T) this;
        return t;
    }

    /**
     * Returns the amount of time (in milliseconds) to allow the client to complete the execution of
     * an API call. This timeout covers the entire client execution except for marshalling. This
     * includes request handler execution, all HTTP request including retries, unmarshalling, etc.
     * <p>
     * This feature requires buffering the entire response (for non-streaming APIs) into memory to
     * enforce a hard timeout when reading the response. For APIs that return large responses this
     * could be expensive.
     * <p>
     * <p>
     * The client execution timeout feature doesn't have strict guarantees on how quickly a request
     * is aborted when the timeout is breached. The typical case aborts the request within a few
     * milliseconds but there may occasionally be requests that don't get aborted until several
     * seconds after the timer has been breached. Because of this the client execution timeout
     * feature should not be used when absolute precision is needed.
     * </p>
     * <p>
     * This may be used together with {@link AmazonWebServiceRequest#setSdkRequestTimeout(int)} to
     * enforce both a timeout on each individual HTTP request (i.e. each retry) and the total time
     * spent on all requests across retries (i.e. the 'client execution' time). A non-positive value
     * disables this feature.
     * </p>
     * <p>
     * <b>Note:</b> This feature is not compatible with Java 1.6.
     * </p>
     *
     * @return The amount of time (in milliseconds) to allow the client to complete the execution of
     *         an API call. A non-positive value disables the timeout for this request.
     * @see {@link AmazonWebServiceRequest#setSdkRequestTimeout(int)} to enforce a timeout per HTTP
     *      request
     */
    public Integer getSdkClientExecutionTimeout() {
        return this.sdkClientExecutionTimeout;
    }

    /**
     * Sets the amount of time (in milliseconds) to allow the client to complete the execution of
     * an API call. This timeout covers the entire client execution except for marshalling. This
     * includes request handler execution, all HTTP request including retries, unmarshalling, etc.
     * <p>
     * This feature requires buffering the entire response (for non-streaming APIs) into memory to
     * enforce a hard timeout when reading the response. For APIs that return large responses this
     * could be expensive.
     * <p>
     * <p>
     * The client execution timeout feature doesn't have strict guarantees on how quickly a request
     * is aborted when the timeout is breached. The typical case aborts the request within a few
     * milliseconds but there may occasionally be requests that don't get aborted until several
     * seconds after the timer has been breached. Because of this the client execution timeout
     * feature should not be used when absolute precision is needed.
     * </p>
     * <p>
     * This may be used together with {@link AmazonWebServiceRequest#setSdkRequestTimeout(int)} to
     * enforce both a timeout on each individual HTTP request (i.e. each retry) and the total time
     * spent on all requests across retries (i.e. the 'client execution' time). A non-positive value
     * disables this feature.
     * </p>
     * <p>
     * <b>Note:</b> This feature is not compatible with Java 1.6.
     * </p>
     *
     * @param sdkClientExecutionTimeout
     *            The amount of time (in milliseconds) to allow the client to complete the execution
     *            of an API call. A non-positive value disables the timeout for this request.
     * @see {@link AmazonWebServiceRequest#setSdkRequestTimeout(int)} to enforce a timeout per HTTP
     *      request
     */
    public void setSdkClientExecutionTimeout(int sdkClientExecutionTimeout) {
        this.sdkClientExecutionTimeout = sdkClientExecutionTimeout;
    }

    /**
     * Sets the amount of time (in milliseconds) to allow the client to complete the execution of
     * an API call. This timeout covers the entire client execution except for marshalling. This
     * includes request handler execution, all HTTP request including retries, unmarshalling, etc.
     * <p>
     * This feature requires buffering the entire response (for non-streaming APIs) into memory to
     * enforce a hard timeout when reading the response. For APIs that return large responses this
     * could be expensive.
     * <p>
     * <p>
     * The client execution timeout feature doesn't have strict guarantees on how quickly a request
     * is aborted when the timeout is breached. The typical case aborts the request within a few
     * milliseconds but there may occasionally be requests that don't get aborted until several
     * seconds after the timer has been breached. Because of this the client execution timeout
     * feature should not be used when absolute precision is needed.
     * </p>
     * <p>
     * This may be used together with {@link AmazonWebServiceRequest#setSdkRequestTimeout(int)} to
     * enforce both a timeout on each individual HTTP request (i.e. each retry) and the total time
     * spent on all requests across retries (i.e. the 'client execution' time). A non-positive value
     * disables this feature.
     * </p>
     * <p>
     * <b>Note:</b> This feature is not compatible with Java 1.6.
     * </p>
     *
     * @param sdkClientExecutionTimeout
     *            The amount of time (in milliseconds) to allow the client to complete the execution
     *            of an API call. A non-positive value disables the timeout for this request.
     * @return The updated AmazonWebServiceRequest object for method chaining
     * @see {@link AmazonWebServiceRequest#setSdkRequestTimeout(int)} to enforce a timeout per HTTP
     *      request
     */
    public <T extends AmazonWebServiceRequest> T withSdkClientExecutionTimeout(int sdkClientExecutionTimeout) {
        setSdkClientExecutionTimeout(sdkClientExecutionTimeout);
        @SuppressWarnings("unchecked")
        T t = (T) this;
        return t;
    }

    @Override
    public <X> void addHandlerContext(HandlerContextKey<X> key, X value) {
        this.handlerContext.put(key, value);
    }

    @Override
    @SuppressWarnings("unchecked")
    public <X> X getHandlerContext(HandlerContextKey<X> key) {
        return (X) this.handlerContext.get(key);
    }

    /**
     * Retrieve an unmodifiable collection of all handler context objects. This allows a {@link Request} derived from a
     * {@link AmazonWebServiceRequest} to inherit its context. This does not protect the objects within the map from being
     * modified.
     *
     * <p>This should not be used by customers.</p>
     */
    @SdkInternalApi
    Map<HandlerContextKey<?>, Object> getHandlerContext() {
        return Collections.unmodifiableMap(this.handlerContext);
    }

    /**
     * Creates a shallow clone of this object for all fields except the handler context. Explicitly does <em>not</em> clone the
     * deep structure of the other fields in the message.
     *
     * @see Object#clone()
     */
    @Override
    public AmazonWebServiceRequest clone() {
        try {
            AmazonWebServiceRequest cloned = (AmazonWebServiceRequest) super.clone();
            cloned.setCloneSource(this);

            // Deep-copy context to ensure modifications made by the handlers do not leak back to the caller or other uses of the
            // same request.
            cloned.handlerContext = new HashMap<HandlerContextKey<?>, Object>(cloned.handlerContext);

            return cloned;
        } catch (CloneNotSupportedException e) {
            throw new IllegalStateException(
                    "Got a CloneNotSupportedException from Object.clone() " + "even though we're Cloneable!", e);
        }
    }
}