diff --git a/.all-contributorsrc b/.all-contributorsrc
index c927666bb1a9..eac27828eed2 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -796,6 +796,24 @@
"contributions": [
"code"
]
+ },
+ {
+ "login": "scrocquesel",
+ "name": "Sébastien Crocquesel",
+ "avatar_url": "https://avatars.githubusercontent.com/u/88554524?v=4",
+ "profile": "https://www.inulogic.fr",
+ "contributions": [
+ "code"
+ ]
+ },
+ {
+ "login": "dave-fn",
+ "name": "David Negrete",
+ "avatar_url": "https://avatars.githubusercontent.com/u/21349334?v=4",
+ "profile": "https://github.com/dave-fn",
+ "contributions": [
+ "code"
+ ]
}
],
"contributorsPerLine": 7,
@@ -804,5 +822,6 @@
"repoType": "github",
"repoHost": "https://github.com",
"skipCi": true,
- "commitConvention": "angular"
+ "commitConvention": "angular",
+ "commitType": "docs"
}
diff --git a/.brazil.json b/.brazil.json
index 9059e5646031..1f0931d0747b 100644
--- a/.brazil.json
+++ b/.brazil.json
@@ -109,6 +109,7 @@
"io.netty:netty-common": { "packageName": "Netty4", "packageVersion": "4.1" },
"io.netty:netty-handler": { "packageName": "Netty4", "packageVersion": "4.1" },
"io.netty:netty-resolver": { "packageName": "Netty4", "packageVersion": "4.1" },
+ "io.netty:netty-resolver-dns": { "packageName": "Netty4", "packageVersion": "4.1" },
"io.netty:netty-transport": { "packageName": "Netty4", "packageVersion": "4.1" },
"io.netty:netty-transport-classes-epoll": { "packageName": "Netty4", "packageVersion": "4.1" },
"io.netty:netty-transport-native-unix-common": { "packageName": "Netty4", "packageVersion": "4.1" },
diff --git a/.changes/2.20.68.json b/.changes/2.20.68.json
new file mode 100644
index 000000000000..1b3791065e1b
--- /dev/null
+++ b/.changes/2.20.68.json
@@ -0,0 +1,84 @@
+{
+ "version": "2.20.68",
+ "date": "2023-05-18",
+ "entries": [
+ {
+ "type": "bugfix",
+ "category": "AWS SDK for Java v2",
+ "contributor": "",
+ "description": "This update fixes an issue where CompletableFutures are leaked/never completed when the submission to the FUTURE_COMPLETE_EXECUTOR is rejected.\n\nBy default, the SDK uses `2 * number of cores` (with a maximum of 64), and uses bounded queue of size 1000. In cases where the throughput to the client exceeds the executor's ability to keep up, it would reject executions. Before this change this would lead to leaked futures."
+ },
+ {
+ "type": "bugfix",
+ "category": "S3 Transfer Manager",
+ "contributor": "",
+ "description": "Fixed the issue where S3 Transfer Manager attempted to load AWS CRT classes when Java based S3 client was used. See [#3936](https://github.com/aws/aws-sdk-java-v2/issues/3936)."
+ },
+ {
+ "type": "feature",
+ "category": "AWS CloudTrail",
+ "contributor": "",
+ "description": "Add ConflictException to PutEventSelectors, add (Channel/EDS)ARNInvalidException to Tag APIs. These exceptions provide customers with more specific error messages instead of internal errors."
+ },
+ {
+ "type": "feature",
+ "category": "AWS Compute Optimizer",
+ "contributor": "",
+ "description": "In this launch, we add support for showing integration status with external metric providers such as Instana, Datadog ...etc in GetEC2InstanceRecommendations and ExportEC2InstanceRecommendations apis"
+ },
+ {
+ "type": "feature",
+ "category": "AWS Elemental MediaConvert",
+ "contributor": "",
+ "description": "This release introduces a new MXF Profile for XDCAM which is strictly compliant with the SMPTE RDD 9 standard and improved handling of output name modifiers."
+ },
+ {
+ "type": "feature",
+ "category": "AWS Security Token Service",
+ "contributor": "",
+ "description": "API updates for the AWS Security Token Service"
+ },
+ {
+ "type": "feature",
+ "category": "Amazon Athena",
+ "contributor": "",
+ "description": "Removing SparkProperties from EngineConfiguration object for StartSession API call"
+ },
+ {
+ "type": "feature",
+ "category": "Amazon Connect Service",
+ "contributor": "",
+ "description": "You can programmatically create and manage prompts using APIs, for example, to extract prompts stored within Amazon Connect and add them to your Amazon S3 bucket. AWS CloudTrail, AWS CloudFormation and tagging are supported."
+ },
+ {
+ "type": "feature",
+ "category": "Amazon EC2 Container Service",
+ "contributor": "",
+ "description": "Documentation only release to address various tickets."
+ },
+ {
+ "type": "feature",
+ "category": "Amazon Elastic Compute Cloud",
+ "contributor": "",
+ "description": "Add support for i4g.large, i4g.xlarge, i4g.2xlarge, i4g.4xlarge, i4g.8xlarge and i4g.16xlarge instances powered by AWS Graviton2 processors that deliver up to 15% better compute performance than our other storage-optimized instances."
+ },
+ {
+ "type": "feature",
+ "category": "Amazon Relational Database Service",
+ "contributor": "",
+ "description": "RDS documentation update for the EngineVersion parameter of ModifyDBSnapshot"
+ },
+ {
+ "type": "feature",
+ "category": "Amazon SageMaker geospatial capabilities",
+ "contributor": "",
+ "description": "This release makes ExecutionRoleArn a required field in the StartEarthObservationJob API."
+ },
+ {
+ "type": "feature",
+ "category": "AWS SDK for Java v2",
+ "contributor": "",
+ "description": "Updated endpoint and partition metadata."
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.changes/2.20.69.json b/.changes/2.20.69.json
new file mode 100644
index 000000000000..17e6dc9949f9
--- /dev/null
+++ b/.changes/2.20.69.json
@@ -0,0 +1,36 @@
+{
+ "version": "2.20.69",
+ "date": "2023-05-19",
+ "entries": [
+ {
+ "type": "feature",
+ "category": "AWS Backup",
+ "contributor": "",
+ "description": "Add ResourceArn, ResourceType, and BackupVaultName to ListRecoveryPointsByLegalHold API response."
+ },
+ {
+ "type": "feature",
+ "category": "AWS Elemental MediaPackage v2",
+ "contributor": "",
+ "description": "Adds support for the MediaPackage Live v2 API"
+ },
+ {
+ "type": "feature",
+ "category": "Amazon Connect Cases",
+ "contributor": "",
+ "description": "This release adds the ability to create fields with type Url through the CreateField API. For more information see https://docs.aws.amazon.com/cases/latest/APIReference/Welcome.html"
+ },
+ {
+ "type": "feature",
+ "category": "Amazon Simple Email Service",
+ "contributor": "",
+ "description": "This release allows customers to update scaling mode property of dedicated IP pools with PutDedicatedIpPoolScalingAttributes call."
+ },
+ {
+ "type": "feature",
+ "category": "AWS SDK for Java v2",
+ "contributor": "",
+ "description": "Updated endpoint and partition metadata."
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.changes/2.20.70.json b/.changes/2.20.70.json
new file mode 100644
index 000000000000..23ab3ff3f262
--- /dev/null
+++ b/.changes/2.20.70.json
@@ -0,0 +1,36 @@
+{
+ "version": "2.20.70",
+ "date": "2023-05-22",
+ "entries": [
+ {
+ "type": "feature",
+ "category": "AWS Backup",
+ "contributor": "",
+ "description": "Added support for tags on restore."
+ },
+ {
+ "type": "feature",
+ "category": "AWS SDK for Java v2",
+ "contributor": "scrocquesel",
+ "description": "Add client configuration overriding of SCHEDULED_EXECUTOR_SERVICE option"
+ },
+ {
+ "type": "feature",
+ "category": "AWS SDK for Java v2",
+ "contributor": "",
+ "description": "This commit adds a new ErrorType core metric that is recorded for all failed API call attempts. The ErrorType records the general category of error that ocurred for a failed API call attempt. Those categories are:\n\n - Throttling errors\n - Service errors other than throttling\n - I/O errors\n - API call or API call attempt timeouts\n\n The intent of this metric is to help locate possible issues at a glance and help direct further debugging or investigation."
+ },
+ {
+ "type": "feature",
+ "category": "Amazon Pinpoint",
+ "contributor": "",
+ "description": "Amazon Pinpoint is deprecating the tags parameter in the UpdateSegment, UpdateCampaign, UpdateEmailTemplate, UpdateSmsTemplate, UpdatePushTemplate, UpdateInAppTemplate and UpdateVoiceTemplate. Amazon Pinpoint will end support tags parameter by May 22, 2023."
+ },
+ {
+ "type": "feature",
+ "category": "Amazon QuickSight",
+ "contributor": "",
+ "description": "Add support for Asset Bundle, Geospatial Heatmaps."
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.changes/2.20.71.json b/.changes/2.20.71.json
new file mode 100644
index 000000000000..63de87aa67f9
--- /dev/null
+++ b/.changes/2.20.71.json
@@ -0,0 +1,30 @@
+{
+ "version": "2.20.71",
+ "date": "2023-05-23",
+ "entries": [
+ {
+ "type": "feature",
+ "category": "Amazon SageMaker Service",
+ "contributor": "",
+ "description": "Added ModelNameEquals, ModelPackageVersionArnEquals in request and ModelName, SamplePayloadUrl, ModelPackageVersionArn in response of ListInferenceRecommendationsJobs API. Added Invocation timestamps in response of DescribeInferenceRecommendationsJob API & ListInferenceRecommendationsJobSteps API."
+ },
+ {
+ "type": "feature",
+ "category": "Amazon Translate",
+ "contributor": "",
+ "description": "Added support for calling TranslateDocument API."
+ },
+ {
+ "type": "feature",
+ "category": "Firewall Management Service",
+ "contributor": "",
+ "description": "Fixes issue that could cause calls to GetAdminScope and ListAdminAccountsForOrganization to return a 500 Internal Server error."
+ },
+ {
+ "type": "feature",
+ "category": "AWS SDK for Java v2",
+ "contributor": "",
+ "description": "Updated endpoint and partition metadata."
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.changes/2.20.72.json b/.changes/2.20.72.json
new file mode 100644
index 000000000000..6ff0568bf94b
--- /dev/null
+++ b/.changes/2.20.72.json
@@ -0,0 +1,36 @@
+{
+ "version": "2.20.72",
+ "date": "2023-05-24",
+ "entries": [
+ {
+ "type": "feature",
+ "category": "AWS AppSync",
+ "contributor": "",
+ "description": "This release introduces AppSync Merged APIs, which provide the ability to compose multiple source APIs into a single federated/merged API."
+ },
+ {
+ "type": "feature",
+ "category": "AWS Cost and Usage Report Service",
+ "contributor": "",
+ "description": "Add support for split cost allocation data on a report."
+ },
+ {
+ "type": "feature",
+ "category": "Amazon Connect Service",
+ "contributor": "",
+ "description": "Amazon Connect Evaluation Capabilities: validation improvements"
+ },
+ {
+ "type": "feature",
+ "category": "Amazon SageMaker Service",
+ "contributor": "",
+ "description": "SageMaker now provides an instantaneous deployment recommendation through the DescribeModel API"
+ },
+ {
+ "type": "feature",
+ "category": "AWS SDK for Java v2",
+ "contributor": "",
+ "description": "Updated endpoint and partition metadata."
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.changes/2.20.73.json b/.changes/2.20.73.json
new file mode 100644
index 000000000000..c5f0134238f2
--- /dev/null
+++ b/.changes/2.20.73.json
@@ -0,0 +1,48 @@
+{
+ "version": "2.20.73",
+ "date": "2023-05-25",
+ "entries": [
+ {
+ "type": "feature",
+ "category": "AWS CodePipeline",
+ "contributor": "",
+ "description": "Add PollingDisabledAt time information in PipelineMetadata object of GetPipeline API."
+ },
+ {
+ "type": "feature",
+ "category": "AWS Glue",
+ "contributor": "",
+ "description": "Added ability to create data quality rulesets for shared, cross-account Glue Data Catalog tables. Added support for dataset comparison rules through a new parameter called AdditionalDataSources. Enhanced the data quality results with a map containing profiled metric values."
+ },
+ {
+ "type": "feature",
+ "category": "AWS Migration Hub Refactor Spaces",
+ "contributor": "",
+ "description": "This SDK update allows for path parameter syntax to be passed to the CreateRoute API. Path parameter syntax require parameters to be enclosed in {} characters. This update also includes a new AppendSourcePath field which lets users forward the source path to the Service URL endpoint."
+ },
+ {
+ "type": "feature",
+ "category": "Amazon GameLift",
+ "contributor": "",
+ "description": "GameLift FleetIQ users can now filter game server claim requests to exclude servers on instances that are draining."
+ },
+ {
+ "type": "feature",
+ "category": "Amazon SageMaker Service",
+ "contributor": "",
+ "description": "Amazon SageMaker Automatic Model Tuning now supports enabling Autotune for tuning jobs which can choose tuning job configurations."
+ },
+ {
+ "type": "feature",
+ "category": "Application Auto Scaling",
+ "contributor": "",
+ "description": "With this release, ElastiCache customers will be able to use predefined metricType \"ElastiCacheDatabaseCapacityUsageCountedForEvictPercentage\" for their ElastiCache instances."
+ },
+ {
+ "type": "feature",
+ "category": "AWS SDK for Java v2",
+ "contributor": "",
+ "description": "Updated endpoint and partition metadata."
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.changes/2.20.74.json b/.changes/2.20.74.json
new file mode 100644
index 000000000000..aabdc6fbb1e2
--- /dev/null
+++ b/.changes/2.20.74.json
@@ -0,0 +1,30 @@
+{
+ "version": "2.20.74",
+ "date": "2023-05-26",
+ "entries": [
+ {
+ "type": "feature",
+ "category": "AWS IoT Wireless",
+ "contributor": "",
+ "description": "Add Multicast Group support in Network Analyzer Configuration."
+ },
+ {
+ "type": "feature",
+ "category": "Amazon Connect Service",
+ "contributor": "",
+ "description": "Documentation update for a new Initiation Method value in DescribeContact API"
+ },
+ {
+ "type": "feature",
+ "category": "Amazon SageMaker Service",
+ "contributor": "",
+ "description": "Added ml.p4d and ml.inf1 as supported instance type families for SageMaker Notebook Instances."
+ },
+ {
+ "type": "feature",
+ "category": "AWS SDK for Java v2",
+ "contributor": "",
+ "description": "Updated endpoint and partition metadata."
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.changes/2.20.75.json b/.changes/2.20.75.json
new file mode 100644
index 000000000000..9bd2fb1a23f2
--- /dev/null
+++ b/.changes/2.20.75.json
@@ -0,0 +1,78 @@
+{
+ "version": "2.20.75",
+ "date": "2023-05-30",
+ "entries": [
+ {
+ "type": "feature",
+ "category": "AWS Glue",
+ "contributor": "",
+ "description": "Added Runtime parameter to allow selection of Ray Runtime"
+ },
+ {
+ "type": "feature",
+ "category": "AWS Ground Station",
+ "contributor": "",
+ "description": "Updating description of GetMinuteUsage to be clearer."
+ },
+ {
+ "type": "feature",
+ "category": "AWS IoT FleetWise",
+ "contributor": "",
+ "description": "Campaigns now support selecting Timestream or S3 as the data destination, Signal catalogs now support \"Deprecation\" keyword released in VSS v2.1 and \"Comment\" keyword released in VSS v3.0"
+ },
+ {
+ "type": "feature",
+ "category": "AWS SecurityHub",
+ "contributor": "",
+ "description": "Added new resource detail objects to ASFF, including resources for AwsGuardDutyDetector, AwsAmazonMqBroker, AwsEventSchemasRegistry, AwsAppSyncGraphQlApi and AwsStepFunctionStateMachine."
+ },
+ {
+ "type": "feature",
+ "category": "AWS WAFV2",
+ "contributor": "",
+ "description": "This SDK release provides customers the ability to use Header Order as a field to match."
+ },
+ {
+ "type": "feature",
+ "category": "Amazon Chime SDK Voice",
+ "contributor": "",
+ "description": "Added optional CallLeg field to StartSpeakerSearchTask API request"
+ },
+ {
+ "type": "feature",
+ "category": "Amazon Location Service",
+ "contributor": "",
+ "description": "This release adds API support for political views for the maps service APIs: CreateMap, UpdateMap, DescribeMap."
+ },
+ {
+ "type": "feature",
+ "category": "Amazon MemoryDB",
+ "contributor": "",
+ "description": "Amazon MemoryDB for Redis now supports AWS Identity and Access Management authentication access to Redis clusters starting with redis-engine version 7.0"
+ },
+ {
+ "type": "feature",
+ "category": "Amazon Personalize",
+ "contributor": "",
+ "description": "This release provides support for the exclusion of certain columns for training when creating a solution and creating or updating a recommender with Amazon Personalize."
+ },
+ {
+ "type": "feature",
+ "category": "Amazon Polly",
+ "contributor": "",
+ "description": "Amazon Polly adds 2 new voices - Sofie (da-DK) and Niamh (en-IE)"
+ },
+ {
+ "type": "feature",
+ "category": "Amazon Security Lake",
+ "contributor": "",
+ "description": "Log sources are now versioned. AWS log sources and custom sources will now come with a version identifier that enables producers to vend multiple schema versions to subscribers. Security Lake API have been refactored to more closely align with AWS API conventions."
+ },
+ {
+ "type": "feature",
+ "category": "AWS SDK for Java v2",
+ "contributor": "",
+ "description": "Updated endpoint and partition metadata."
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.changes/2.20.76.json b/.changes/2.20.76.json
new file mode 100644
index 000000000000..1de8fa20aecf
--- /dev/null
+++ b/.changes/2.20.76.json
@@ -0,0 +1,54 @@
+{
+ "version": "2.20.76",
+ "date": "2023-05-31",
+ "entries": [
+ {
+ "type": "bugfix",
+ "category": "AWS SDK for Java v2",
+ "contributor": "",
+ "description": "Fix an issue where the optimal number of parts calculated could be higher than 10,000"
+ },
+ {
+ "type": "feature",
+ "category": "AWS Config",
+ "contributor": "",
+ "description": "Resource Types Exclusion feature launch by AWS Config"
+ },
+ {
+ "type": "feature",
+ "category": "AWSMainframeModernization",
+ "contributor": "",
+ "description": "Adds an optional create-only 'roleArn' property to Application resources. Enables PS and PO data set org types."
+ },
+ {
+ "type": "feature",
+ "category": "AWS Service Catalog",
+ "contributor": "",
+ "description": "Documentation updates for ServiceCatalog."
+ },
+ {
+ "type": "feature",
+ "category": "Amazon Fraud Detector",
+ "contributor": "",
+ "description": "This release enables publishing event predictions from Amazon Fraud Detector (AFD) to Amazon EventBridge. For example, after getting predictions from AFD, Amazon EventBridge rules can be configured to trigger notification through an SNS topic, send a message with SES, or trigger Lambda workflows."
+ },
+ {
+ "type": "feature",
+ "category": "Amazon HealthLake",
+ "contributor": "",
+ "description": "This release adds a new request parameter to the CreateFHIRDatastore API operation. IdentityProviderConfiguration specifies how you want to authenticate incoming requests to your Healthlake Data Store."
+ },
+ {
+ "type": "feature",
+ "category": "Amazon Relational Database Service",
+ "contributor": "",
+ "description": "This release adds support for changing the engine for Oracle using the ModifyDbInstance API"
+ },
+ {
+ "type": "feature",
+ "category": "Amazon WorkSpaces Web",
+ "contributor": "",
+ "description": "WorkSpaces Web now allows you to control which IP addresses your WorkSpaces Web portal may be accessed from."
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.changes/2.20.77.json b/.changes/2.20.77.json
new file mode 100644
index 000000000000..9d11b565f6e7
--- /dev/null
+++ b/.changes/2.20.77.json
@@ -0,0 +1,48 @@
+{
+ "version": "2.20.77",
+ "date": "2023-06-01",
+ "entries": [
+ {
+ "type": "feature",
+ "category": "AWS WAFV2",
+ "contributor": "",
+ "description": "Corrected the information for the header order FieldToMatch setting"
+ },
+ {
+ "type": "feature",
+ "category": "Alexa For Business",
+ "contributor": "",
+ "description": "Alexa for Business has been deprecated and is no longer supported."
+ },
+ {
+ "type": "feature",
+ "category": "Amazon Appflow",
+ "contributor": "",
+ "description": "Added ability to select DataTransferApiType for DescribeConnector and CreateFlow requests when using Async supported connectors. Added supportedDataTransferType to DescribeConnector/DescribeConnectors/ListConnector response."
+ },
+ {
+ "type": "feature",
+ "category": "Amazon Connect Customer Profiles",
+ "contributor": "",
+ "description": "This release introduces calculated attribute related APIs."
+ },
+ {
+ "type": "feature",
+ "category": "Amazon Interactive Video Service",
+ "contributor": "",
+ "description": "API Update for IVS Advanced Channel type"
+ },
+ {
+ "type": "feature",
+ "category": "Amazon SageMaker Service",
+ "contributor": "",
+ "description": "Amazon Sagemaker Autopilot adds support for Parquet file input to NLP text classification jobs."
+ },
+ {
+ "type": "feature",
+ "category": "AWS SDK for Java v2",
+ "contributor": "",
+ "description": "Updated endpoint and partition metadata."
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.changes/2.20.78.json b/.changes/2.20.78.json
new file mode 100644
index 000000000000..da0c2eac15b5
--- /dev/null
+++ b/.changes/2.20.78.json
@@ -0,0 +1,36 @@
+{
+ "version": "2.20.78",
+ "date": "2023-06-02",
+ "entries": [
+ {
+ "type": "feature",
+ "category": "AWS CloudTrail",
+ "contributor": "",
+ "description": "This feature allows users to start and stop event ingestion on a CloudTrail Lake event data store."
+ },
+ {
+ "type": "feature",
+ "category": "AWS WAFV2",
+ "contributor": "",
+ "description": "Added APIs to describe managed products. The APIs retrieve information about rule groups that are managed by AWS and by AWS Marketplace sellers."
+ },
+ {
+ "type": "feature",
+ "category": "Amazon Athena",
+ "contributor": "",
+ "description": "This release introduces the DeleteCapacityReservation API and the ability to manage capacity reservations using CloudFormation"
+ },
+ {
+ "type": "feature",
+ "category": "Amazon SageMaker Service",
+ "contributor": "",
+ "description": "This release adds Selective Execution feature that allows SageMaker Pipelines users to run selected steps in a pipeline."
+ },
+ {
+ "type": "feature",
+ "category": "AWS SDK for Java v2",
+ "contributor": "",
+ "description": "Updated endpoint and partition metadata."
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.changes/2.20.79.json b/.changes/2.20.79.json
new file mode 100644
index 000000000000..b6b2fe779124
--- /dev/null
+++ b/.changes/2.20.79.json
@@ -0,0 +1,66 @@
+{
+ "version": "2.20.79",
+ "date": "2023-06-05",
+ "entries": [
+ {
+ "type": "bugfix",
+ "category": "AWS SDK for Java v2",
+ "contributor": "",
+ "description": "Upgrading AWS CRT dependency to v0.21.17. This version contains minor fixes and updates"
+ },
+ {
+ "type": "feature",
+ "category": "AWS CloudFormation",
+ "contributor": "",
+ "description": "AWS CloudFormation StackSets provides customers with three new APIs to activate, deactivate, and describe AWS Organizations trusted access which is needed to get started with service-managed StackSets."
+ },
+ {
+ "type": "feature",
+ "category": "AWS Key Management Service",
+ "contributor": "",
+ "description": "This release includes feature to import customer's asymmetric (RSA and ECC) and HMAC keys into KMS. It also includes feature to allow customers to specify number of days to schedule a KMS key deletion as a policy condition key."
+ },
+ {
+ "type": "feature",
+ "category": "AWS Lambda",
+ "contributor": "",
+ "description": "Add Ruby 3.2 (ruby3.2) Runtime support to AWS Lambda."
+ },
+ {
+ "type": "feature",
+ "category": "Amazon Elastic Compute Cloud",
+ "contributor": "",
+ "description": "Making InstanceTagAttribute as the required parameter for the DeregisterInstanceEventNotificationAttributes and RegisterInstanceEventNotificationAttributes APIs."
+ },
+ {
+ "type": "feature",
+ "category": "Amazon Fraud Detector",
+ "contributor": "",
+ "description": "Added new variable types, new DateTime data type, and new rules engine functions for interacting and working with DateTime data types."
+ },
+ {
+ "type": "feature",
+ "category": "Amazon Keyspaces",
+ "contributor": "",
+ "description": "This release adds support for MRR GA launch, and includes multiregion support in create-keyspace, get-keyspace, and list-keyspace."
+ },
+ {
+ "type": "feature",
+ "category": "AmazonMWAA",
+ "contributor": "",
+ "description": "This release adds ROLLING_BACK and CREATING_SNAPSHOT environment statuses for Amazon MWAA environments."
+ },
+ {
+ "type": "feature",
+ "category": "FinSpace User Environment Management service",
+ "contributor": "",
+ "description": "Releasing new Managed kdb Insights APIs"
+ },
+ {
+ "type": "feature",
+ "category": "AWS SDK for Java v2",
+ "contributor": "",
+ "description": "Updated endpoint and partition metadata."
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.changes/2.20.80.json b/.changes/2.20.80.json
new file mode 100644
index 000000000000..f7cd296e01ef
--- /dev/null
+++ b/.changes/2.20.80.json
@@ -0,0 +1,72 @@
+{
+ "version": "2.20.80",
+ "date": "2023-06-06",
+ "entries": [
+ {
+ "type": "feature",
+ "category": "AWS Identity and Access Management",
+ "contributor": "",
+ "description": "This release updates the AccountAlias regex pattern with the same length restrictions enforced by the length constraint."
+ },
+ {
+ "type": "feature",
+ "category": "AWS IoT",
+ "contributor": "",
+ "description": "Adding IoT Device Management Software Package Catalog APIs to register, store, and report system software packages, along with their versions and metadata in a centralized location."
+ },
+ {
+ "type": "feature",
+ "category": "AWS IoT Data Plane",
+ "contributor": "",
+ "description": "Update thing shadow name regex to allow '$' character"
+ },
+ {
+ "type": "feature",
+ "category": "AWS Signer",
+ "contributor": "",
+ "description": "AWS Signer is launching Container Image Signing, a new feature that enables you to sign and verify container images. This feature enables you to validate that only container images you approve are used in your enterprise."
+ },
+ {
+ "type": "feature",
+ "category": "Amazon Connect Service",
+ "contributor": "",
+ "description": "GetMetricDataV2 API is now available in AWS GovCloud(US) region."
+ },
+ {
+ "type": "feature",
+ "category": "Amazon EMR",
+ "contributor": "",
+ "description": "This release provides customers the ability to specify an allocation strategies amongst PRICE_CAPACITY_OPTIMIZED, CAPACITY_OPTIMIZED, LOWEST_PRICE, DIVERSIFIED for Spot instances in Instance Feet cluster. This enables customers to choose an allocation strategy best suited for their workload."
+ },
+ {
+ "type": "feature",
+ "category": "Amazon Lex Model Building V2",
+ "contributor": "",
+ "description": "This release adds support for Lex Developers to create test sets and to execute those test-sets against their bots."
+ },
+ {
+ "type": "feature",
+ "category": "Amazon QuickSight",
+ "contributor": "",
+ "description": "QuickSight support for pivot table field collapse state, radar chart range scale and multiple scope options in conditional formatting."
+ },
+ {
+ "type": "feature",
+ "category": "Amazon Simple Queue Service",
+ "contributor": "",
+ "description": "Amazon SQS adds three new APIs - StartMessageMoveTask, CancelMessageMoveTask, and ListMessageMoveTasks to automate redriving messages from dead-letter queues to source queues or a custom destination."
+ },
+ {
+ "type": "feature",
+ "category": "Inspector2",
+ "contributor": "",
+ "description": "Adds new response properties and request parameters for 'last scanned at' on the ListCoverage operation. This feature allows you to search and view the date of which your resources were last scanned by Inspector."
+ },
+ {
+ "type": "feature",
+ "category": "AWS SDK for Java v2",
+ "contributor": "",
+ "description": "Updated endpoint and partition metadata."
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.changes/2.20.81.json b/.changes/2.20.81.json
new file mode 100644
index 000000000000..edbde98b38f4
--- /dev/null
+++ b/.changes/2.20.81.json
@@ -0,0 +1,42 @@
+{
+ "version": "2.20.81",
+ "date": "2023-06-07",
+ "entries": [
+ {
+ "type": "feature",
+ "category": "AWS CloudFormation",
+ "contributor": "",
+ "description": "AWS CloudFormation StackSets is updating the deployment experience for all stackset operations to skip suspended AWS accounts during deployments. StackSets will skip target AWS accounts that are suspended and set the Detailed Status of the corresponding stack instances as SKIPPED_SUSPENDED_ACCOUNT"
+ },
+ {
+ "type": "feature",
+ "category": "AWS Direct Connect",
+ "contributor": "",
+ "description": "This update corrects the jumbo frames mtu values from 9100 to 8500 for transit virtual interfaces."
+ },
+ {
+ "type": "feature",
+ "category": "AWS IoT Core Device Advisor",
+ "contributor": "",
+ "description": "AWS IoT Core Device Advisor now supports new Qualification Suite test case list. With this update, customers can more easily create new qualification test suite with an empty rootGroup input."
+ },
+ {
+ "type": "feature",
+ "category": "Amazon CloudWatch Logs",
+ "contributor": "",
+ "description": "This change adds support for account level data protection policies using 3 new APIs, PutAccountPolicy, DeleteAccountPolicy and DescribeAccountPolicy. DescribeLogGroup API has been modified to indicate if account level policy is applied to the LogGroup via \"inheritedProperties\" list in the response."
+ },
+ {
+ "type": "feature",
+ "category": "Amazon Connect Customer Profiles",
+ "contributor": "",
+ "description": "This release introduces event stream related APIs."
+ },
+ {
+ "type": "feature",
+ "category": "Amazon EMR Containers",
+ "contributor": "",
+ "description": "EMR on EKS adds support for log rotation of Spark container logs with EMR-6.11.0 onwards, to the StartJobRun API."
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.changes/2.20.82.json b/.changes/2.20.82.json
new file mode 100644
index 000000000000..9ca6d3458ec4
--- /dev/null
+++ b/.changes/2.20.82.json
@@ -0,0 +1,54 @@
+{
+ "version": "2.20.82",
+ "date": "2023-06-08",
+ "entries": [
+ {
+ "type": "feature",
+ "category": "AWS Comprehend Medical",
+ "contributor": "",
+ "description": "This release supports a new set of entities and traits."
+ },
+ {
+ "type": "feature",
+ "category": "AWS STS",
+ "contributor": "",
+ "description": "Updates the core STS credential provider logic to return AwsSessionCredentials instead of an STS-specific class, and adds expirationTime to AwsSessionCredentials"
+ },
+ {
+ "type": "feature",
+ "category": "AWS Service Catalog",
+ "contributor": "",
+ "description": "New parameter added in ServiceCatalog DescribeProvisioningArtifact api - IncludeProvisioningArtifactParameters. This parameter can be used to return information about the parameters used to provision the product"
+ },
+ {
+ "type": "feature",
+ "category": "Amazon Athena",
+ "contributor": "",
+ "description": "You can now define custom spark properties at start of the session for use cases like cluster encryption, table formats, and general Spark tuning."
+ },
+ {
+ "type": "feature",
+ "category": "Amazon Timestream Write",
+ "contributor": "",
+ "description": "This release adds the capability for customers to define how their data should be partitioned, optimizing for certain access patterns. This definition will take place as a part of the table creation."
+ },
+ {
+ "type": "feature",
+ "category": "Payment Cryptography Control Plane",
+ "contributor": "",
+ "description": "Initial release of AWS Payment Cryptography Control Plane service for creating and managing cryptographic keys used during card payment processing."
+ },
+ {
+ "type": "feature",
+ "category": "Payment Cryptography Data Plane",
+ "contributor": "",
+ "description": "Initial release of AWS Payment Cryptography DataPlane Plane service for performing cryptographic operations typically used during card payment processing."
+ },
+ {
+ "type": "feature",
+ "category": "AWS SDK for Java v2",
+ "contributor": "",
+ "description": "Updated endpoint and partition metadata."
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.changes/2.20.83.json b/.changes/2.20.83.json
new file mode 100644
index 000000000000..42dffd691342
--- /dev/null
+++ b/.changes/2.20.83.json
@@ -0,0 +1,30 @@
+{
+ "version": "2.20.83",
+ "date": "2023-06-09",
+ "entries": [
+ {
+ "type": "bugfix",
+ "category": "AWS SDK for Java v2",
+ "contributor": "",
+ "description": "Fixed issue with leased connection leaks when threads executing HTTP connections with Apache HttpClient were interrupted while the connection was in progress."
+ },
+ {
+ "type": "bugfix",
+ "category": "Netty NIO HTTP Client",
+ "contributor": "martinKindall",
+ "description": "By default, Netty threads are blocked during dns resolution, namely InetAddress.getByName is used under the hood. Now, there's an option to configure the NettyNioAsyncHttpClient in order to use a non blocking dns resolution strategy."
+ },
+ {
+ "type": "feature",
+ "category": "AWS Certificate Manager Private Certificate Authority",
+ "contributor": "",
+ "description": "Document-only update to refresh CLI documentation for AWS Private CA. No change to the service."
+ },
+ {
+ "type": "feature",
+ "category": "Amazon Connect Service",
+ "contributor": "",
+ "description": "This release adds search APIs for Prompts, Quick Connects and Hours of Operations, which can be used to search for those resources within a Connect Instance."
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.changes/2.20.84.json b/.changes/2.20.84.json
new file mode 100644
index 000000000000..297e632d6fb7
--- /dev/null
+++ b/.changes/2.20.84.json
@@ -0,0 +1,66 @@
+{
+ "version": "2.20.84",
+ "date": "2023-06-12",
+ "entries": [
+ {
+ "type": "feature",
+ "category": "AWS Amplify UI Builder",
+ "contributor": "",
+ "description": "AWS Amplify UIBuilder is launching Codegen UI, a new feature that enables you to generate your amplify uibuilder components and forms."
+ },
+ {
+ "type": "feature",
+ "category": "AWS SDK for Java v2",
+ "contributor": "",
+ "description": "`IdleConnectionReaper` now does not prevent `HttpClientConnectionManager` from getting GC'd in the case where an SDK client is created per request and not closed."
+ },
+ {
+ "type": "feature",
+ "category": "AWS SDK for Java v2",
+ "contributor": "StephenFlavin",
+ "description": "Add \"unsafe\" and \"fromRemaining\" AsyncRequestBody constructors for byte arrays and ByteBuffers"
+ },
+ {
+ "type": "feature",
+ "category": "Amazon DynamoDB",
+ "contributor": "",
+ "description": "Documentation updates for DynamoDB"
+ },
+ {
+ "type": "feature",
+ "category": "Amazon DynamoDB Streams",
+ "contributor": "",
+ "description": "Documentation updates for DynamoDB Streams"
+ },
+ {
+ "type": "feature",
+ "category": "Amazon FSx",
+ "contributor": "",
+ "description": "Amazon FSx for NetApp ONTAP now supports joining a storage virtual machine (SVM) to Active Directory after the SVM has been created."
+ },
+ {
+ "type": "feature",
+ "category": "Amazon OpenSearch Service",
+ "contributor": "",
+ "description": "This release adds support for SkipUnavailable connection property for cross cluster search"
+ },
+ {
+ "type": "feature",
+ "category": "Amazon Rekognition",
+ "contributor": "",
+ "description": "This release adds support for improved accuracy with user vector in Amazon Rekognition Face Search. Adds new APIs: AssociateFaces, CreateUser, DeleteUser, DisassociateFaces, ListUsers, SearchUsers, SearchUsersByImage. Also adds new face metadata that can be stored: user vector."
+ },
+ {
+ "type": "feature",
+ "category": "Amazon SageMaker Service",
+ "contributor": "",
+ "description": "Sagemaker Neo now supports compilation for inferentia2 (ML_INF2) and Trainium1 (ML_TRN1) as available targets. With these devices, you can run your workloads at highest performance with lowest cost. inferentia2 (ML_INF2) is available in CMH and Trainium1 (ML_TRN1) is available in IAD currently"
+ },
+ {
+ "type": "feature",
+ "category": "AWS SDK for Java v2",
+ "contributor": "",
+ "description": "Updated endpoint and partition metadata."
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.changes/2.20.85.json b/.changes/2.20.85.json
new file mode 100644
index 000000000000..ce7650672d7d
--- /dev/null
+++ b/.changes/2.20.85.json
@@ -0,0 +1,84 @@
+{
+ "version": "2.20.85",
+ "date": "2023-06-13",
+ "entries": [
+ {
+ "type": "feature",
+ "category": "AWS CloudTrail",
+ "contributor": "",
+ "description": "This feature allows users to view dashboards for CloudTrail Lake event data stores."
+ },
+ {
+ "type": "feature",
+ "category": "AWS SecurityHub",
+ "contributor": "",
+ "description": "Add support for Security Hub Automation Rules"
+ },
+ {
+ "type": "feature",
+ "category": "AWS SimSpace Weaver",
+ "contributor": "",
+ "description": "This release fixes using aws-us-gov ARNs in API calls and adds documentation for snapshot APIs."
+ },
+ {
+ "type": "feature",
+ "category": "AWS WAFV2",
+ "contributor": "",
+ "description": "You can now detect and block fraudulent account creation attempts with the new AWS WAF Fraud Control account creation fraud prevention (ACFP) managed rule group AWSManagedRulesACFPRuleSet."
+ },
+ {
+ "type": "feature",
+ "category": "AWS Well-Architected Tool",
+ "contributor": "",
+ "description": "AWS Well-Architected now supports Profiles that help customers prioritize which questions to focus on first by providing a list of prioritized questions that are better aligned with their business goals and outcomes."
+ },
+ {
+ "type": "feature",
+ "category": "Amazon CodeGuru Security",
+ "contributor": "",
+ "description": "Initial release of Amazon CodeGuru Security APIs"
+ },
+ {
+ "type": "feature",
+ "category": "Amazon Elastic Compute Cloud",
+ "contributor": "",
+ "description": "This release introduces a new feature, EC2 Instance Connect Endpoint, that enables you to connect to a resource over TCP, without requiring the resource to have a public IPv4 address."
+ },
+ {
+ "type": "feature",
+ "category": "Amazon Lightsail",
+ "contributor": "",
+ "description": "This release adds pagination for the Get Certificates API operation."
+ },
+ {
+ "type": "feature",
+ "category": "Amazon Simple Storage Service",
+ "contributor": "",
+ "description": "Integrate double encryption feature to SDKs."
+ },
+ {
+ "type": "feature",
+ "category": "Amazon Verified Permissions",
+ "contributor": "",
+ "description": "GA release of Amazon Verified Permissions."
+ },
+ {
+ "type": "feature",
+ "category": "EC2 Image Builder",
+ "contributor": "",
+ "description": "Change the Image Builder ImagePipeline dateNextRun field to more accurately describe the data."
+ },
+ {
+ "type": "feature",
+ "category": "Elastic Disaster Recovery Service",
+ "contributor": "",
+ "description": "Added APIs to support network replication and recovery using AWS Elastic Disaster Recovery."
+ },
+ {
+ "type": "feature",
+ "category": "AWS SDK for Java v2",
+ "contributor": "",
+ "description": "Updated endpoint and partition metadata."
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.changes/2.20.86.json b/.changes/2.20.86.json
new file mode 100644
index 000000000000..33a7fb16a578
--- /dev/null
+++ b/.changes/2.20.86.json
@@ -0,0 +1,48 @@
+{
+ "version": "2.20.86",
+ "date": "2023-06-15",
+ "entries": [
+ {
+ "type": "bugfix",
+ "category": "Amazon DynamoDB Enhanced",
+ "contributor": "breader124",
+ "description": "Thanks to this bugfix it'll be possible to create DynamoDB table containing\nsecondary indices when using no arugments `createTable` method from `DefaultDynamoDbTable`\nclass. Information about their presence might be expressed using annotations, but it was ignored\nand created tables didn't contain specified indices. Plase note that it is still not possible\nto specify projections for indices using annotations. By default, all fields will be projected."
+ },
+ {
+ "type": "feature",
+ "category": "AWS Audit Manager",
+ "contributor": "",
+ "description": "This release introduces 2 Audit Manager features: CSV exports and new manual evidence options. You can now export your evidence finder results in CSV format. In addition, you can now add manual evidence to a control by entering free-form text or uploading a file from your browser."
+ },
+ {
+ "type": "feature",
+ "category": "Amazon Elastic File System",
+ "contributor": "",
+ "description": "Documentation updates for EFS."
+ },
+ {
+ "type": "feature",
+ "category": "Amazon GuardDuty",
+ "contributor": "",
+ "description": "Updated descriptions for some APIs."
+ },
+ {
+ "type": "feature",
+ "category": "Amazon Location Service",
+ "contributor": "",
+ "description": "Amazon Location Service adds categories to places, including filtering on those categories in searches. Also, you can now add metadata properties to your geofences."
+ },
+ {
+ "type": "feature",
+ "category": "DynamoDB Enhanced Client",
+ "contributor": "bmaizels",
+ "description": "Add EnhancedType parameters to static builder methods of StaticTableSchema and StaticImmitableTableSchema"
+ },
+ {
+ "type": "feature",
+ "category": "AWS SDK for Java v2",
+ "contributor": "",
+ "description": "Updated endpoint and partition metadata."
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.changes/2.20.87.json b/.changes/2.20.87.json
new file mode 100644
index 000000000000..78a820206e9e
--- /dev/null
+++ b/.changes/2.20.87.json
@@ -0,0 +1,42 @@
+{
+ "version": "2.20.87",
+ "date": "2023-06-16",
+ "entries": [
+ {
+ "type": "bugfix",
+ "category": "Amazon DynamoDB",
+ "contributor": "martinKindall",
+ "description": "Created static method EnumAttributeConverter::createWithNameAsKeys which creates a converter based on the Enum::name method to identify enums, rather than Enum::toString. This is preferable because Enum::name is final and cannot be overwritten, as opposed to Enum::toString. EnumAttributeConverter::create is kept as it is, for backward compatibility."
+ },
+ {
+ "type": "feature",
+ "category": "AWS Account",
+ "contributor": "",
+ "description": "Improve pagination support for ListRegions"
+ },
+ {
+ "type": "feature",
+ "category": "AWS Application Discovery Service",
+ "contributor": "",
+ "description": "Add Amazon EC2 instance recommendations export"
+ },
+ {
+ "type": "feature",
+ "category": "AWS Identity and Access Management",
+ "contributor": "",
+ "description": "Documentation updates for AWS Identity and Access Management (IAM)."
+ },
+ {
+ "type": "feature",
+ "category": "Amazon Connect Service",
+ "contributor": "",
+ "description": "Updates the *InstanceStorageConfig APIs to support a new ResourceType: SCREEN_RECORDINGS to enable screen recording and specify the storage configurations for publishing the recordings. Also updates DescribeInstance and ListInstances APIs to include InstanceAccessUrl attribute in the API response."
+ },
+ {
+ "type": "feature",
+ "category": "Amazon Simple Storage Service",
+ "contributor": "",
+ "description": "This release adds SDK support for request-payer request header and request-charged response header in the \"GetBucketAccelerateConfiguration\", \"ListMultipartUploads\", \"ListObjects\", \"ListObjectsV2\" and \"ListObjectVersions\" S3 APIs."
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.changes/2.20.88.json b/.changes/2.20.88.json
new file mode 100644
index 000000000000..41dfe7f02632
--- /dev/null
+++ b/.changes/2.20.88.json
@@ -0,0 +1,54 @@
+{
+ "version": "2.20.88",
+ "date": "2023-06-19",
+ "entries": [
+ {
+ "type": "bugfix",
+ "category": "AWS SDK for Java v2",
+ "contributor": "flittev",
+ "description": "`WaiterExecutor` recursive implementation changed to iterative"
+ },
+ {
+ "type": "feature",
+ "category": "AWS CloudFormation",
+ "contributor": "",
+ "description": "Specify desired CloudFormation behavior in the event of ChangeSet execution failure using the CreateChangeSet OnStackFailure parameter"
+ },
+ {
+ "type": "feature",
+ "category": "AWS Glue",
+ "contributor": "",
+ "description": "This release adds support for creating cross region table/database resource links"
+ },
+ {
+ "type": "feature",
+ "category": "AWS Price List Service",
+ "contributor": "",
+ "description": "This release updates the PriceListArn regex pattern."
+ },
+ {
+ "type": "feature",
+ "category": "Amazon EC2 Container Service",
+ "contributor": "",
+ "description": "Documentation only update to address various tickets."
+ },
+ {
+ "type": "feature",
+ "category": "Amazon Elastic Compute Cloud",
+ "contributor": "",
+ "description": "API changes to AWS Verified Access to include data from trust providers in logs"
+ },
+ {
+ "type": "feature",
+ "category": "Amazon Route 53 Domains",
+ "contributor": "",
+ "description": "Update MaxItems upper bound to 1000 for ListPricesRequest"
+ },
+ {
+ "type": "feature",
+ "category": "Amazon SageMaker Service",
+ "contributor": "",
+ "description": "Amazon Sagemaker Autopilot releases CreateAutoMLJobV2 and DescribeAutoMLJobV2 for Autopilot customers with ImageClassification, TextClassification and Tabular problem type config support."
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.changes/2.20.89.json b/.changes/2.20.89.json
new file mode 100644
index 000000000000..bc9f9f8cbf13
--- /dev/null
+++ b/.changes/2.20.89.json
@@ -0,0 +1,42 @@
+{
+ "version": "2.20.89",
+ "date": "2023-06-20",
+ "entries": [
+ {
+ "type": "feature",
+ "category": "AWS Config",
+ "contributor": "",
+ "description": "Updated ResourceType enum with new resource types onboarded by AWS Config in May 2023."
+ },
+ {
+ "type": "feature",
+ "category": "AWS Lambda",
+ "contributor": "",
+ "description": "This release adds RecursiveInvocationException to the Invoke API and InvokeWithResponseStream API."
+ },
+ {
+ "type": "feature",
+ "category": "Amazon Appflow",
+ "contributor": "",
+ "description": "This release adds new API to reset connector metadata cache"
+ },
+ {
+ "type": "feature",
+ "category": "Amazon Elastic Compute Cloud",
+ "contributor": "",
+ "description": "Adds support for targeting Dedicated Host allocations by assetIds in AWS Outposts"
+ },
+ {
+ "type": "feature",
+ "category": "Amazon Redshift",
+ "contributor": "",
+ "description": "Added support for custom domain names for Redshift Provisioned clusters. This feature enables customers to create a custom domain name and use ACM to generate fully secure connections to it."
+ },
+ {
+ "type": "feature",
+ "category": "AWS SDK for Java v2",
+ "contributor": "",
+ "description": "Updated endpoint and partition metadata."
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.changes/2.20.90.json b/.changes/2.20.90.json
new file mode 100644
index 000000000000..a94f1ff1743c
--- /dev/null
+++ b/.changes/2.20.90.json
@@ -0,0 +1,48 @@
+{
+ "version": "2.20.90",
+ "date": "2023-06-21",
+ "entries": [
+ {
+ "type": "feature",
+ "category": "AWS Elemental MediaConvert",
+ "contributor": "",
+ "description": "This release introduces the bandwidth reduction filter for the HEVC encoder, increases the limits of outputs per job, and updates support for the Nagra SDK to version 1.14.7."
+ },
+ {
+ "type": "feature",
+ "category": "AWS Transfer Family",
+ "contributor": "",
+ "description": "This release adds a new parameter StructuredLogDestinations to CreateServer, UpdateServer APIs."
+ },
+ {
+ "type": "feature",
+ "category": "Amazon DynamoDB",
+ "contributor": "",
+ "description": "Documentation updates for DynamoDB"
+ },
+ {
+ "type": "feature",
+ "category": "Amazon EMR",
+ "contributor": "",
+ "description": "This release introduces a new Amazon EMR EPI called ListSupportedInstanceTypes that returns a list of all instance types supported by a given EMR release."
+ },
+ {
+ "type": "feature",
+ "category": "AmazonMQ",
+ "contributor": "",
+ "description": "The Cross Region Disaster Recovery feature allows to replicate a brokers state from one region to another in order to provide customers with multi-region resiliency in the event of a regional outage."
+ },
+ {
+ "type": "feature",
+ "category": "Amazon SageMaker Service",
+ "contributor": "",
+ "description": "This release provides support in SageMaker for output files in training jobs to be uploaded without compression and enable customer to deploy uncompressed model from S3 to real-time inference Endpoints. In addition, ml.trn1n.32xlarge is added to supported instance type list in training job."
+ },
+ {
+ "type": "feature",
+ "category": "Inspector2",
+ "contributor": "",
+ "description": "This release adds support for Software Bill of Materials (SBOM) export and the general availability of code scanning for AWS Lambda functions."
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.changes/2.20.91.json b/.changes/2.20.91.json
new file mode 100644
index 000000000000..f31d6bdac006
--- /dev/null
+++ b/.changes/2.20.91.json
@@ -0,0 +1,36 @@
+{
+ "version": "2.20.91",
+ "date": "2023-06-22",
+ "entries": [
+ {
+ "type": "bugfix",
+ "category": "Maven config",
+ "contributor": "jensim",
+ "description": "Fix the scm.url in the maven project"
+ },
+ {
+ "type": "feature",
+ "category": "AWSKendraFrontendService",
+ "contributor": "",
+ "description": "Introducing Amazon Kendra Retrieve API that can be used to retrieve relevant passages or text excerpts given an input query."
+ },
+ {
+ "type": "feature",
+ "category": "AWS Step Functions",
+ "contributor": "",
+ "description": "Adds support for Versions and Aliases. Adds 8 operations: PublishStateMachineVersion, DeleteStateMachineVersion, ListStateMachineVersions, CreateStateMachineAlias, DescribeStateMachineAlias, UpdateStateMachineAlias, DeleteStateMachineAlias, ListStateMachineAliases"
+ },
+ {
+ "type": "feature",
+ "category": "Amazon Chime SDK Identity",
+ "contributor": "",
+ "description": "AppInstanceBots can be configured to be invoked or not using the Target or the CHIME.mentions attribute for ChannelMessages"
+ },
+ {
+ "type": "feature",
+ "category": "Amazon Chime SDK Messaging",
+ "contributor": "",
+ "description": "ChannelMessages can be made visible to sender and intended recipient rather than all channel members with the target attribute. For example, a user can send messages to a bot and receive messages back in a group channel without other members seeing them."
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.changes/2.20.92.json b/.changes/2.20.92.json
new file mode 100644
index 000000000000..e433eae88956
--- /dev/null
+++ b/.changes/2.20.92.json
@@ -0,0 +1,30 @@
+{
+ "version": "2.20.92",
+ "date": "2023-06-23",
+ "entries": [
+ {
+ "type": "feature",
+ "category": "Amazon DevOps Guru",
+ "contributor": "",
+ "description": "This release adds support for encryption via customer managed keys."
+ },
+ {
+ "type": "feature",
+ "category": "Amazon FSx",
+ "contributor": "",
+ "description": "Update to Amazon FSx documentation."
+ },
+ {
+ "type": "feature",
+ "category": "Amazon Relational Database Service",
+ "contributor": "",
+ "description": "Documentation improvements for create, describe, and modify DB clusters and DB instances."
+ },
+ {
+ "type": "feature",
+ "category": "Amazon Verified Permissions",
+ "contributor": "",
+ "description": "Added improved descriptions and new code samples to SDK documentation."
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.github/workflows/stale-issue.yml b/.github/workflows/stale-issue.yml
index 13307c9a5a25..cf1afebdff5f 100644
--- a/.github/workflows/stale-issue.yml
+++ b/.github/workflows/stale-issue.yml
@@ -44,7 +44,7 @@ jobs:
# Issue timing
days-before-stale: 5
days-before-close: 2
- days-before-ancient: 1095
+ days-before-ancient: 36500
# If you don't want to mark a issue as being ancient based on a
# threshold of "upvotes", you can set this here. An "upvote" is
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 78ce06942be0..a6efa51ef75e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,786 @@
+# __2.20.92__ __2023-06-23__
+## __Amazon DevOps Guru__
+ - ### Features
+ - This release adds support for encryption via customer managed keys.
+
+## __Amazon FSx__
+ - ### Features
+ - Update to Amazon FSx documentation.
+
+## __Amazon Relational Database Service__
+ - ### Features
+ - Documentation improvements for create, describe, and modify DB clusters and DB instances.
+
+## __Amazon Verified Permissions__
+ - ### Features
+ - Added improved descriptions and new code samples to SDK documentation.
+
+# __2.20.91__ __2023-06-22__
+## __AWS Step Functions__
+ - ### Features
+ - Adds support for Versions and Aliases. Adds 8 operations: PublishStateMachineVersion, DeleteStateMachineVersion, ListStateMachineVersions, CreateStateMachineAlias, DescribeStateMachineAlias, UpdateStateMachineAlias, DeleteStateMachineAlias, ListStateMachineAliases
+
+## __AWSKendraFrontendService__
+ - ### Features
+ - Introducing Amazon Kendra Retrieve API that can be used to retrieve relevant passages or text excerpts given an input query.
+
+## __Amazon Chime SDK Identity__
+ - ### Features
+ - AppInstanceBots can be configured to be invoked or not using the Target or the CHIME.mentions attribute for ChannelMessages
+
+## __Amazon Chime SDK Messaging__
+ - ### Features
+ - ChannelMessages can be made visible to sender and intended recipient rather than all channel members with the target attribute. For example, a user can send messages to a bot and receive messages back in a group channel without other members seeing them.
+
+## __Maven config__
+ - ### Bugfixes
+ - Fix the scm.url in the maven project
+ - Contributed by: [@jensim](https://github.com/jensim)
+
+## __Contributors__
+Special thanks to the following contributors to this release:
+
+[@jensim](https://github.com/jensim)
+# __2.20.90__ __2023-06-21__
+## __AWS Elemental MediaConvert__
+ - ### Features
+ - This release introduces the bandwidth reduction filter for the HEVC encoder, increases the limits of outputs per job, and updates support for the Nagra SDK to version 1.14.7.
+
+## __AWS Transfer Family__
+ - ### Features
+ - This release adds a new parameter StructuredLogDestinations to CreateServer, UpdateServer APIs.
+
+## __Amazon DynamoDB__
+ - ### Features
+ - Documentation updates for DynamoDB
+
+## __Amazon EMR__
+ - ### Features
+ - This release introduces a new Amazon EMR EPI called ListSupportedInstanceTypes that returns a list of all instance types supported by a given EMR release.
+
+## __Amazon SageMaker Service__
+ - ### Features
+ - This release provides support in SageMaker for output files in training jobs to be uploaded without compression and enable customer to deploy uncompressed model from S3 to real-time inference Endpoints. In addition, ml.trn1n.32xlarge is added to supported instance type list in training job.
+
+## __AmazonMQ__
+ - ### Features
+ - The Cross Region Disaster Recovery feature allows to replicate a brokers state from one region to another in order to provide customers with multi-region resiliency in the event of a regional outage.
+
+## __Inspector2__
+ - ### Features
+ - This release adds support for Software Bill of Materials (SBOM) export and the general availability of code scanning for AWS Lambda functions.
+
+# __2.20.89__ __2023-06-20__
+## __AWS Config__
+ - ### Features
+ - Updated ResourceType enum with new resource types onboarded by AWS Config in May 2023.
+
+## __AWS Lambda__
+ - ### Features
+ - This release adds RecursiveInvocationException to the Invoke API and InvokeWithResponseStream API.
+
+## __AWS SDK for Java v2__
+ - ### Features
+ - Updated endpoint and partition metadata.
+
+## __Amazon Appflow__
+ - ### Features
+ - This release adds new API to reset connector metadata cache
+
+## __Amazon Elastic Compute Cloud__
+ - ### Features
+ - Adds support for targeting Dedicated Host allocations by assetIds in AWS Outposts
+
+## __Amazon Redshift__
+ - ### Features
+ - Added support for custom domain names for Redshift Provisioned clusters. This feature enables customers to create a custom domain name and use ACM to generate fully secure connections to it.
+
+# __2.20.88__ __2023-06-19__
+## __AWS CloudFormation__
+ - ### Features
+ - Specify desired CloudFormation behavior in the event of ChangeSet execution failure using the CreateChangeSet OnStackFailure parameter
+
+## __AWS Glue__
+ - ### Features
+ - This release adds support for creating cross region table/database resource links
+
+## __AWS Price List Service__
+ - ### Features
+ - This release updates the PriceListArn regex pattern.
+
+## __AWS SDK for Java v2__
+ - ### Bugfixes
+ - `WaiterExecutor` recursive implementation changed to iterative
+ - Contributed by: [@flittev](https://github.com/flittev)
+
+## __Amazon EC2 Container Service__
+ - ### Features
+ - Documentation only update to address various tickets.
+
+## __Amazon Elastic Compute Cloud__
+ - ### Features
+ - API changes to AWS Verified Access to include data from trust providers in logs
+
+## __Amazon Route 53 Domains__
+ - ### Features
+ - Update MaxItems upper bound to 1000 for ListPricesRequest
+
+## __Amazon SageMaker Service__
+ - ### Features
+ - Amazon Sagemaker Autopilot releases CreateAutoMLJobV2 and DescribeAutoMLJobV2 for Autopilot customers with ImageClassification, TextClassification and Tabular problem type config support.
+
+## __Contributors__
+Special thanks to the following contributors to this release:
+
+[@flittev](https://github.com/flittev)
+# __2.20.87__ __2023-06-16__
+## __AWS Account__
+ - ### Features
+ - Improve pagination support for ListRegions
+
+## __AWS Application Discovery Service__
+ - ### Features
+ - Add Amazon EC2 instance recommendations export
+
+## __AWS Identity and Access Management__
+ - ### Features
+ - Documentation updates for AWS Identity and Access Management (IAM).
+
+## __Amazon Connect Service__
+ - ### Features
+ - Updates the *InstanceStorageConfig APIs to support a new ResourceType: SCREEN_RECORDINGS to enable screen recording and specify the storage configurations for publishing the recordings. Also updates DescribeInstance and ListInstances APIs to include InstanceAccessUrl attribute in the API response.
+
+## __Amazon DynamoDB__
+ - ### Bugfixes
+ - Created static method EnumAttributeConverter::createWithNameAsKeys which creates a converter based on the Enum::name method to identify enums, rather than Enum::toString. This is preferable because Enum::name is final and cannot be overwritten, as opposed to Enum::toString. EnumAttributeConverter::create is kept as it is, for backward compatibility.
+ - Contributed by: [@martinKindall](https://github.com/martinKindall)
+
+## __Amazon Simple Storage Service__
+ - ### Features
+ - This release adds SDK support for request-payer request header and request-charged response header in the "GetBucketAccelerateConfiguration", "ListMultipartUploads", "ListObjects", "ListObjectsV2" and "ListObjectVersions" S3 APIs.
+
+## __Contributors__
+Special thanks to the following contributors to this release:
+
+[@martinKindall](https://github.com/martinKindall)
+# __2.20.86__ __2023-06-15__
+## __AWS Audit Manager__
+ - ### Features
+ - This release introduces 2 Audit Manager features: CSV exports and new manual evidence options. You can now export your evidence finder results in CSV format. In addition, you can now add manual evidence to a control by entering free-form text or uploading a file from your browser.
+
+## __AWS SDK for Java v2__
+ - ### Features
+ - Updated endpoint and partition metadata.
+
+## __Amazon DynamoDB Enhanced__
+ - ### Bugfixes
+ - Thanks to this bugfix it'll be possible to create DynamoDB table containing
+ secondary indices when using no arugments `createTable` method from `DefaultDynamoDbTable`
+ class. Information about their presence might be expressed using annotations, but it was ignored
+ and created tables didn't contain specified indices. Plase note that it is still not possible
+ to specify projections for indices using annotations. By default, all fields will be projected.
+ - Contributed by: [@breader124](https://github.com/breader124)
+
+## __Amazon Elastic File System__
+ - ### Features
+ - Documentation updates for EFS.
+
+## __Amazon GuardDuty__
+ - ### Features
+ - Updated descriptions for some APIs.
+
+## __Amazon Location Service__
+ - ### Features
+ - Amazon Location Service adds categories to places, including filtering on those categories in searches. Also, you can now add metadata properties to your geofences.
+
+## __DynamoDB Enhanced Client__
+ - ### Features
+ - Add EnhancedType parameters to static builder methods of StaticTableSchema and StaticImmitableTableSchema
+ - Contributed by: [@bmaizels](https://github.com/bmaizels)
+
+## __Contributors__
+Special thanks to the following contributors to this release:
+
+[@bmaizels](https://github.com/bmaizels), [@breader124](https://github.com/breader124)
+# __2.20.85__ __2023-06-13__
+## __AWS CloudTrail__
+ - ### Features
+ - This feature allows users to view dashboards for CloudTrail Lake event data stores.
+
+## __AWS SDK for Java v2__
+ - ### Features
+ - Updated endpoint and partition metadata.
+
+## __AWS SecurityHub__
+ - ### Features
+ - Add support for Security Hub Automation Rules
+
+## __AWS SimSpace Weaver__
+ - ### Features
+ - This release fixes using aws-us-gov ARNs in API calls and adds documentation for snapshot APIs.
+
+## __AWS WAFV2__
+ - ### Features
+ - You can now detect and block fraudulent account creation attempts with the new AWS WAF Fraud Control account creation fraud prevention (ACFP) managed rule group AWSManagedRulesACFPRuleSet.
+
+## __AWS Well-Architected Tool__
+ - ### Features
+ - AWS Well-Architected now supports Profiles that help customers prioritize which questions to focus on first by providing a list of prioritized questions that are better aligned with their business goals and outcomes.
+
+## __Amazon CodeGuru Security__
+ - ### Features
+ - Initial release of Amazon CodeGuru Security APIs
+
+## __Amazon Elastic Compute Cloud__
+ - ### Features
+ - This release introduces a new feature, EC2 Instance Connect Endpoint, that enables you to connect to a resource over TCP, without requiring the resource to have a public IPv4 address.
+
+## __Amazon Lightsail__
+ - ### Features
+ - This release adds pagination for the Get Certificates API operation.
+
+## __Amazon Simple Storage Service__
+ - ### Features
+ - Integrate double encryption feature to SDKs.
+
+## __Amazon Verified Permissions__
+ - ### Features
+ - GA release of Amazon Verified Permissions.
+
+## __EC2 Image Builder__
+ - ### Features
+ - Change the Image Builder ImagePipeline dateNextRun field to more accurately describe the data.
+
+## __Elastic Disaster Recovery Service__
+ - ### Features
+ - Added APIs to support network replication and recovery using AWS Elastic Disaster Recovery.
+
+# __2.20.84__ __2023-06-12__
+## __AWS Amplify UI Builder__
+ - ### Features
+ - AWS Amplify UIBuilder is launching Codegen UI, a new feature that enables you to generate your amplify uibuilder components and forms.
+
+## __AWS SDK for Java v2__
+ - ### Features
+ - Add "unsafe" and "fromRemaining" AsyncRequestBody constructors for byte arrays and ByteBuffers
+ - Contributed by: [@StephenFlavin](https://github.com/StephenFlavin)
+ - Updated endpoint and partition metadata.
+ - `IdleConnectionReaper` now does not prevent `HttpClientConnectionManager` from getting GC'd in the case where an SDK client is created per request and not closed.
+
+## __Amazon DynamoDB__
+ - ### Features
+ - Documentation updates for DynamoDB
+
+## __Amazon DynamoDB Streams__
+ - ### Features
+ - Documentation updates for DynamoDB Streams
+
+## __Amazon FSx__
+ - ### Features
+ - Amazon FSx for NetApp ONTAP now supports joining a storage virtual machine (SVM) to Active Directory after the SVM has been created.
+
+## __Amazon OpenSearch Service__
+ - ### Features
+ - This release adds support for SkipUnavailable connection property for cross cluster search
+
+## __Amazon Rekognition__
+ - ### Features
+ - This release adds support for improved accuracy with user vector in Amazon Rekognition Face Search. Adds new APIs: AssociateFaces, CreateUser, DeleteUser, DisassociateFaces, ListUsers, SearchUsers, SearchUsersByImage. Also adds new face metadata that can be stored: user vector.
+
+## __Amazon SageMaker Service__
+ - ### Features
+ - Sagemaker Neo now supports compilation for inferentia2 (ML_INF2) and Trainium1 (ML_TRN1) as available targets. With these devices, you can run your workloads at highest performance with lowest cost. inferentia2 (ML_INF2) is available in CMH and Trainium1 (ML_TRN1) is available in IAD currently
+
+## __Contributors__
+Special thanks to the following contributors to this release:
+
+[@StephenFlavin](https://github.com/StephenFlavin)
+# __2.20.83__ __2023-06-09__
+## __AWS Certificate Manager Private Certificate Authority__
+ - ### Features
+ - Document-only update to refresh CLI documentation for AWS Private CA. No change to the service.
+
+## __AWS SDK for Java v2__
+ - ### Bugfixes
+ - Fixed issue with leased connection leaks when threads executing HTTP connections with Apache HttpClient were interrupted while the connection was in progress.
+
+## __Amazon Connect Service__
+ - ### Features
+ - This release adds search APIs for Prompts, Quick Connects and Hours of Operations, which can be used to search for those resources within a Connect Instance.
+
+## __Netty NIO HTTP Client__
+ - ### Bugfixes
+ - By default, Netty threads are blocked during dns resolution, namely InetAddress.getByName is used under the hood. Now, there's an option to configure the NettyNioAsyncHttpClient in order to use a non blocking dns resolution strategy.
+ - Contributed by: [@martinKindall](https://github.com/martinKindall)
+
+## __Contributors__
+Special thanks to the following contributors to this release:
+
+[@martinKindall](https://github.com/martinKindall)
+# __2.20.82__ __2023-06-08__
+## __AWS Comprehend Medical__
+ - ### Features
+ - This release supports a new set of entities and traits.
+
+## __AWS SDK for Java v2__
+ - ### Features
+ - Updated endpoint and partition metadata.
+
+## __AWS STS__
+ - ### Features
+ - Updates the core STS credential provider logic to return AwsSessionCredentials instead of an STS-specific class, and adds expirationTime to AwsSessionCredentials
+
+## __AWS Service Catalog__
+ - ### Features
+ - New parameter added in ServiceCatalog DescribeProvisioningArtifact api - IncludeProvisioningArtifactParameters. This parameter can be used to return information about the parameters used to provision the product
+
+## __Amazon Athena__
+ - ### Features
+ - You can now define custom spark properties at start of the session for use cases like cluster encryption, table formats, and general Spark tuning.
+
+## __Amazon Timestream Write__
+ - ### Features
+ - This release adds the capability for customers to define how their data should be partitioned, optimizing for certain access patterns. This definition will take place as a part of the table creation.
+
+## __Payment Cryptography Control Plane__
+ - ### Features
+ - Initial release of AWS Payment Cryptography Control Plane service for creating and managing cryptographic keys used during card payment processing.
+
+## __Payment Cryptography Data Plane__
+ - ### Features
+ - Initial release of AWS Payment Cryptography DataPlane Plane service for performing cryptographic operations typically used during card payment processing.
+
+# __2.20.81__ __2023-06-07__
+## __AWS CloudFormation__
+ - ### Features
+ - AWS CloudFormation StackSets is updating the deployment experience for all stackset operations to skip suspended AWS accounts during deployments. StackSets will skip target AWS accounts that are suspended and set the Detailed Status of the corresponding stack instances as SKIPPED_SUSPENDED_ACCOUNT
+
+## __AWS Direct Connect__
+ - ### Features
+ - This update corrects the jumbo frames mtu values from 9100 to 8500 for transit virtual interfaces.
+
+## __AWS IoT Core Device Advisor__
+ - ### Features
+ - AWS IoT Core Device Advisor now supports new Qualification Suite test case list. With this update, customers can more easily create new qualification test suite with an empty rootGroup input.
+
+## __Amazon CloudWatch Logs__
+ - ### Features
+ - This change adds support for account level data protection policies using 3 new APIs, PutAccountPolicy, DeleteAccountPolicy and DescribeAccountPolicy. DescribeLogGroup API has been modified to indicate if account level policy is applied to the LogGroup via "inheritedProperties" list in the response.
+
+## __Amazon Connect Customer Profiles__
+ - ### Features
+ - This release introduces event stream related APIs.
+
+## __Amazon EMR Containers__
+ - ### Features
+ - EMR on EKS adds support for log rotation of Spark container logs with EMR-6.11.0 onwards, to the StartJobRun API.
+
+# __2.20.80__ __2023-06-06__
+## __AWS Identity and Access Management__
+ - ### Features
+ - This release updates the AccountAlias regex pattern with the same length restrictions enforced by the length constraint.
+
+## __AWS IoT__
+ - ### Features
+ - Adding IoT Device Management Software Package Catalog APIs to register, store, and report system software packages, along with their versions and metadata in a centralized location.
+
+## __AWS IoT Data Plane__
+ - ### Features
+ - Update thing shadow name regex to allow '$' character
+
+## __AWS SDK for Java v2__
+ - ### Features
+ - Updated endpoint and partition metadata.
+
+## __AWS Signer__
+ - ### Features
+ - AWS Signer is launching Container Image Signing, a new feature that enables you to sign and verify container images. This feature enables you to validate that only container images you approve are used in your enterprise.
+
+## __Amazon Connect Service__
+ - ### Features
+ - GetMetricDataV2 API is now available in AWS GovCloud(US) region.
+
+## __Amazon EMR__
+ - ### Features
+ - This release provides customers the ability to specify an allocation strategies amongst PRICE_CAPACITY_OPTIMIZED, CAPACITY_OPTIMIZED, LOWEST_PRICE, DIVERSIFIED for Spot instances in Instance Feet cluster. This enables customers to choose an allocation strategy best suited for their workload.
+
+## __Amazon Lex Model Building V2__
+ - ### Features
+ - This release adds support for Lex Developers to create test sets and to execute those test-sets against their bots.
+
+## __Amazon QuickSight__
+ - ### Features
+ - QuickSight support for pivot table field collapse state, radar chart range scale and multiple scope options in conditional formatting.
+
+## __Amazon Simple Queue Service__
+ - ### Features
+ - Amazon SQS adds three new APIs - StartMessageMoveTask, CancelMessageMoveTask, and ListMessageMoveTasks to automate redriving messages from dead-letter queues to source queues or a custom destination.
+
+## __Inspector2__
+ - ### Features
+ - Adds new response properties and request parameters for 'last scanned at' on the ListCoverage operation. This feature allows you to search and view the date of which your resources were last scanned by Inspector.
+
+# __2.20.79__ __2023-06-05__
+## __AWS CloudFormation__
+ - ### Features
+ - AWS CloudFormation StackSets provides customers with three new APIs to activate, deactivate, and describe AWS Organizations trusted access which is needed to get started with service-managed StackSets.
+
+## __AWS Key Management Service__
+ - ### Features
+ - This release includes feature to import customer's asymmetric (RSA and ECC) and HMAC keys into KMS. It also includes feature to allow customers to specify number of days to schedule a KMS key deletion as a policy condition key.
+
+## __AWS Lambda__
+ - ### Features
+ - Add Ruby 3.2 (ruby3.2) Runtime support to AWS Lambda.
+
+## __AWS SDK for Java v2__
+ - ### Features
+ - Updated endpoint and partition metadata.
+
+ - ### Bugfixes
+ - Upgrading AWS CRT dependency to v0.21.17. This version contains minor fixes and updates
+
+## __Amazon Elastic Compute Cloud__
+ - ### Features
+ - Making InstanceTagAttribute as the required parameter for the DeregisterInstanceEventNotificationAttributes and RegisterInstanceEventNotificationAttributes APIs.
+
+## __Amazon Fraud Detector__
+ - ### Features
+ - Added new variable types, new DateTime data type, and new rules engine functions for interacting and working with DateTime data types.
+
+## __Amazon Keyspaces__
+ - ### Features
+ - This release adds support for MRR GA launch, and includes multiregion support in create-keyspace, get-keyspace, and list-keyspace.
+
+## __AmazonMWAA__
+ - ### Features
+ - This release adds ROLLING_BACK and CREATING_SNAPSHOT environment statuses for Amazon MWAA environments.
+
+## __FinSpace User Environment Management service__
+ - ### Features
+ - Releasing new Managed kdb Insights APIs
+
+# __2.20.78__ __2023-06-02__
+## __AWS CloudTrail__
+ - ### Features
+ - This feature allows users to start and stop event ingestion on a CloudTrail Lake event data store.
+
+## __AWS SDK for Java v2__
+ - ### Features
+ - Updated endpoint and partition metadata.
+
+## __AWS WAFV2__
+ - ### Features
+ - Added APIs to describe managed products. The APIs retrieve information about rule groups that are managed by AWS and by AWS Marketplace sellers.
+
+## __Amazon Athena__
+ - ### Features
+ - This release introduces the DeleteCapacityReservation API and the ability to manage capacity reservations using CloudFormation
+
+## __Amazon SageMaker Service__
+ - ### Features
+ - This release adds Selective Execution feature that allows SageMaker Pipelines users to run selected steps in a pipeline.
+
+# __2.20.77__ __2023-06-01__
+## __AWS SDK for Java v2__
+ - ### Features
+ - Updated endpoint and partition metadata.
+
+## __AWS WAFV2__
+ - ### Features
+ - Corrected the information for the header order FieldToMatch setting
+
+## __Alexa For Business__
+ - ### Features
+ - Alexa for Business has been deprecated and is no longer supported.
+
+## __Amazon Appflow__
+ - ### Features
+ - Added ability to select DataTransferApiType for DescribeConnector and CreateFlow requests when using Async supported connectors. Added supportedDataTransferType to DescribeConnector/DescribeConnectors/ListConnector response.
+
+## __Amazon Connect Customer Profiles__
+ - ### Features
+ - This release introduces calculated attribute related APIs.
+
+## __Amazon Interactive Video Service__
+ - ### Features
+ - API Update for IVS Advanced Channel type
+
+## __Amazon SageMaker Service__
+ - ### Features
+ - Amazon Sagemaker Autopilot adds support for Parquet file input to NLP text classification jobs.
+
+# __2.20.76__ __2023-05-31__
+## __AWS Config__
+ - ### Features
+ - Resource Types Exclusion feature launch by AWS Config
+
+## __AWS SDK for Java v2__
+ - ### Bugfixes
+ - Fix an issue where the optimal number of parts calculated could be higher than 10,000
+
+## __AWS Service Catalog__
+ - ### Features
+ - Documentation updates for ServiceCatalog.
+
+## __AWSMainframeModernization__
+ - ### Features
+ - Adds an optional create-only 'roleArn' property to Application resources. Enables PS and PO data set org types.
+
+## __Amazon Fraud Detector__
+ - ### Features
+ - This release enables publishing event predictions from Amazon Fraud Detector (AFD) to Amazon EventBridge. For example, after getting predictions from AFD, Amazon EventBridge rules can be configured to trigger notification through an SNS topic, send a message with SES, or trigger Lambda workflows.
+
+## __Amazon HealthLake__
+ - ### Features
+ - This release adds a new request parameter to the CreateFHIRDatastore API operation. IdentityProviderConfiguration specifies how you want to authenticate incoming requests to your Healthlake Data Store.
+
+## __Amazon Relational Database Service__
+ - ### Features
+ - This release adds support for changing the engine for Oracle using the ModifyDbInstance API
+
+## __Amazon WorkSpaces Web__
+ - ### Features
+ - WorkSpaces Web now allows you to control which IP addresses your WorkSpaces Web portal may be accessed from.
+
+# __2.20.75__ __2023-05-30__
+## __AWS Glue__
+ - ### Features
+ - Added Runtime parameter to allow selection of Ray Runtime
+
+## __AWS Ground Station__
+ - ### Features
+ - Updating description of GetMinuteUsage to be clearer.
+
+## __AWS IoT FleetWise__
+ - ### Features
+ - Campaigns now support selecting Timestream or S3 as the data destination, Signal catalogs now support "Deprecation" keyword released in VSS v2.1 and "Comment" keyword released in VSS v3.0
+
+## __AWS SDK for Java v2__
+ - ### Features
+ - Updated endpoint and partition metadata.
+
+## __AWS SecurityHub__
+ - ### Features
+ - Added new resource detail objects to ASFF, including resources for AwsGuardDutyDetector, AwsAmazonMqBroker, AwsEventSchemasRegistry, AwsAppSyncGraphQlApi and AwsStepFunctionStateMachine.
+
+## __AWS WAFV2__
+ - ### Features
+ - This SDK release provides customers the ability to use Header Order as a field to match.
+
+## __Amazon Chime SDK Voice__
+ - ### Features
+ - Added optional CallLeg field to StartSpeakerSearchTask API request
+
+## __Amazon Location Service__
+ - ### Features
+ - This release adds API support for political views for the maps service APIs: CreateMap, UpdateMap, DescribeMap.
+
+## __Amazon MemoryDB__
+ - ### Features
+ - Amazon MemoryDB for Redis now supports AWS Identity and Access Management authentication access to Redis clusters starting with redis-engine version 7.0
+
+## __Amazon Personalize__
+ - ### Features
+ - This release provides support for the exclusion of certain columns for training when creating a solution and creating or updating a recommender with Amazon Personalize.
+
+## __Amazon Polly__
+ - ### Features
+ - Amazon Polly adds 2 new voices - Sofie (da-DK) and Niamh (en-IE)
+
+## __Amazon Security Lake__
+ - ### Features
+ - Log sources are now versioned. AWS log sources and custom sources will now come with a version identifier that enables producers to vend multiple schema versions to subscribers. Security Lake API have been refactored to more closely align with AWS API conventions.
+
+# __2.20.74__ __2023-05-26__
+## __AWS IoT Wireless__
+ - ### Features
+ - Add Multicast Group support in Network Analyzer Configuration.
+
+## __AWS SDK for Java v2__
+ - ### Features
+ - Updated endpoint and partition metadata.
+
+## __Amazon Connect Service__
+ - ### Features
+ - Documentation update for a new Initiation Method value in DescribeContact API
+
+## __Amazon SageMaker Service__
+ - ### Features
+ - Added ml.p4d and ml.inf1 as supported instance type families for SageMaker Notebook Instances.
+
+# __2.20.73__ __2023-05-25__
+## __AWS CodePipeline__
+ - ### Features
+ - Add PollingDisabledAt time information in PipelineMetadata object of GetPipeline API.
+
+## __AWS Glue__
+ - ### Features
+ - Added ability to create data quality rulesets for shared, cross-account Glue Data Catalog tables. Added support for dataset comparison rules through a new parameter called AdditionalDataSources. Enhanced the data quality results with a map containing profiled metric values.
+
+## __AWS Migration Hub Refactor Spaces__
+ - ### Features
+ - This SDK update allows for path parameter syntax to be passed to the CreateRoute API. Path parameter syntax require parameters to be enclosed in {} characters. This update also includes a new AppendSourcePath field which lets users forward the source path to the Service URL endpoint.
+
+## __AWS SDK for Java v2__
+ - ### Features
+ - Updated endpoint and partition metadata.
+
+## __Amazon GameLift__
+ - ### Features
+ - GameLift FleetIQ users can now filter game server claim requests to exclude servers on instances that are draining.
+
+## __Amazon SageMaker Service__
+ - ### Features
+ - Amazon SageMaker Automatic Model Tuning now supports enabling Autotune for tuning jobs which can choose tuning job configurations.
+
+## __Application Auto Scaling__
+ - ### Features
+ - With this release, ElastiCache customers will be able to use predefined metricType "ElastiCacheDatabaseCapacityUsageCountedForEvictPercentage" for their ElastiCache instances.
+
+# __2.20.72__ __2023-05-24__
+## __AWS AppSync__
+ - ### Features
+ - This release introduces AppSync Merged APIs, which provide the ability to compose multiple source APIs into a single federated/merged API.
+
+## __AWS Cost and Usage Report Service__
+ - ### Features
+ - Add support for split cost allocation data on a report.
+
+## __AWS SDK for Java v2__
+ - ### Features
+ - Updated endpoint and partition metadata.
+
+## __Amazon Connect Service__
+ - ### Features
+ - Amazon Connect Evaluation Capabilities: validation improvements
+
+## __Amazon SageMaker Service__
+ - ### Features
+ - SageMaker now provides an instantaneous deployment recommendation through the DescribeModel API
+
+# __2.20.71__ __2023-05-23__
+## __AWS SDK for Java v2__
+ - ### Features
+ - Updated endpoint and partition metadata.
+
+## __Amazon SageMaker Service__
+ - ### Features
+ - Added ModelNameEquals, ModelPackageVersionArnEquals in request and ModelName, SamplePayloadUrl, ModelPackageVersionArn in response of ListInferenceRecommendationsJobs API. Added Invocation timestamps in response of DescribeInferenceRecommendationsJob API & ListInferenceRecommendationsJobSteps API.
+
+## __Amazon Translate__
+ - ### Features
+ - Added support for calling TranslateDocument API.
+
+## __Firewall Management Service__
+ - ### Features
+ - Fixes issue that could cause calls to GetAdminScope and ListAdminAccountsForOrganization to return a 500 Internal Server error.
+
+# __2.20.70__ __2023-05-22__
+## __AWS Backup__
+ - ### Features
+ - Added support for tags on restore.
+
+## __AWS SDK for Java v2__
+ - ### Features
+ - Add client configuration overriding of SCHEDULED_EXECUTOR_SERVICE option
+ - Contributed by: [@scrocquesel](https://github.com/scrocquesel)
+ - This commit adds a new ErrorType core metric that is recorded for all failed API call attempts. The ErrorType records the general category of error that ocurred for a failed API call attempt. Those categories are:
+
+ - Throttling errors
+ - Service errors other than throttling
+ - I/O errors
+ - API call or API call attempt timeouts
+
+ The intent of this metric is to help locate possible issues at a glance and help direct further debugging or investigation.
+
+## __Amazon Pinpoint__
+ - ### Features
+ - Amazon Pinpoint is deprecating the tags parameter in the UpdateSegment, UpdateCampaign, UpdateEmailTemplate, UpdateSmsTemplate, UpdatePushTemplate, UpdateInAppTemplate and UpdateVoiceTemplate. Amazon Pinpoint will end support tags parameter by May 22, 2023.
+
+## __Amazon QuickSight__
+ - ### Features
+ - Add support for Asset Bundle, Geospatial Heatmaps.
+
+## __Contributors__
+Special thanks to the following contributors to this release:
+
+[@scrocquesel](https://github.com/scrocquesel)
+# __2.20.69__ __2023-05-19__
+## __AWS Backup__
+ - ### Features
+ - Add ResourceArn, ResourceType, and BackupVaultName to ListRecoveryPointsByLegalHold API response.
+
+## __AWS Elemental MediaPackage v2__
+ - ### Features
+ - Adds support for the MediaPackage Live v2 API
+
+## __AWS SDK for Java v2__
+ - ### Features
+ - Updated endpoint and partition metadata.
+
+## __Amazon Connect Cases__
+ - ### Features
+ - This release adds the ability to create fields with type Url through the CreateField API. For more information see https://docs.aws.amazon.com/cases/latest/APIReference/Welcome.html
+
+## __Amazon Simple Email Service__
+ - ### Features
+ - This release allows customers to update scaling mode property of dedicated IP pools with PutDedicatedIpPoolScalingAttributes call.
+
+# __2.20.68__ __2023-05-18__
+## __AWS CloudTrail__
+ - ### Features
+ - Add ConflictException to PutEventSelectors, add (Channel/EDS)ARNInvalidException to Tag APIs. These exceptions provide customers with more specific error messages instead of internal errors.
+
+## __AWS Compute Optimizer__
+ - ### Features
+ - In this launch, we add support for showing integration status with external metric providers such as Instana, Datadog ...etc in GetEC2InstanceRecommendations and ExportEC2InstanceRecommendations apis
+
+## __AWS Elemental MediaConvert__
+ - ### Features
+ - This release introduces a new MXF Profile for XDCAM which is strictly compliant with the SMPTE RDD 9 standard and improved handling of output name modifiers.
+
+## __AWS SDK for Java v2__
+ - ### Features
+ - Updated endpoint and partition metadata.
+
+ - ### Bugfixes
+ - This update fixes an issue where CompletableFutures are leaked/never completed when the submission to the FUTURE_COMPLETE_EXECUTOR is rejected.
+
+ By default, the SDK uses `2 * number of cores` (with a maximum of 64), and uses bounded queue of size 1000. In cases where the throughput to the client exceeds the executor's ability to keep up, it would reject executions. Before this change this would lead to leaked futures.
+
+## __AWS Security Token Service__
+ - ### Features
+ - API updates for the AWS Security Token Service
+
+## __Amazon Athena__
+ - ### Features
+ - Removing SparkProperties from EngineConfiguration object for StartSession API call
+
+## __Amazon Connect Service__
+ - ### Features
+ - You can programmatically create and manage prompts using APIs, for example, to extract prompts stored within Amazon Connect and add them to your Amazon S3 bucket. AWS CloudTrail, AWS CloudFormation and tagging are supported.
+
+## __Amazon EC2 Container Service__
+ - ### Features
+ - Documentation only release to address various tickets.
+
+## __Amazon Elastic Compute Cloud__
+ - ### Features
+ - Add support for i4g.large, i4g.xlarge, i4g.2xlarge, i4g.4xlarge, i4g.8xlarge and i4g.16xlarge instances powered by AWS Graviton2 processors that deliver up to 15% better compute performance than our other storage-optimized instances.
+
+## __Amazon Relational Database Service__
+ - ### Features
+ - RDS documentation update for the EngineVersion parameter of ModifyDBSnapshot
+
+## __Amazon SageMaker geospatial capabilities__
+ - ### Features
+ - This release makes ExecutionRoleArn a required field in the StartEarthObservationJob API.
+
+## __S3 Transfer Manager__
+ - ### Bugfixes
+ - Fixed the issue where S3 Transfer Manager attempted to load AWS CRT classes when Java based S3 client was used. See [#3936](https://github.com/aws/aws-sdk-java-v2/issues/3936).
+
# __2.20.67__ __2023-05-16__
## __AWS Direct Connect__
- ### Features
diff --git a/README.md b/README.md
index 7cad11c9dbac..1635c0cbb96c 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
[![Gitter](https://badges.gitter.im/aws/aws-sdk-java-v2.svg)](https://gitter.im/aws/aws-sdk-java-v2?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
[![codecov](https://codecov.io/gh/aws/aws-sdk-java-v2/branch/master/graph/badge.svg)](https://codecov.io/gh/aws/aws-sdk-java-v2)
-[![All Contributors](https://img.shields.io/badge/all_contributors-88-orange.svg?style=flat-square)](#contributors-)
+[![All Contributors](https://img.shields.io/badge/all_contributors-89-orange.svg?style=flat-square)](#contributors-)
The **AWS SDK for Java 2.0** is a rewrite of 1.0 with some great new features. As with version 1.0,
@@ -52,7 +52,7 @@ To automatically manage module versions (currently all modules have the same ver
software.amazon.awssdk
bom
- 2.20.67
+ 2.20.92
pom
import
@@ -86,12 +86,12 @@ Alternatively you can add dependencies for the specific services you use only:
software.amazon.awssdk
ec2
- 2.20.67
+ 2.20.92
software.amazon.awssdk
s3
- 2.20.67
+ 2.20.92
```
@@ -103,7 +103,7 @@ You can import the whole SDK into your project (includes *ALL* services). Please
software.amazon.awssdk
aws-sdk-java
- 2.20.67
+ 2.20.92
```
@@ -304,6 +304,8 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
Andy Kiesler 💻
Martin 💻
Paulo Lieuthier 💻
+ Sébastien Crocquesel 💻
+ David Negrete 💻
diff --git a/archetypes/archetype-app-quickstart/pom.xml b/archetypes/archetype-app-quickstart/pom.xml
index 88294e74dfbc..aeba019c29b0 100644
--- a/archetypes/archetype-app-quickstart/pom.xml
+++ b/archetypes/archetype-app-quickstart/pom.xml
@@ -20,7 +20,7 @@
archetypes
software.amazon.awssdk
- 2.20.68-SNAPSHOT
+ 2.20.93-SNAPSHOT
4.0.0
diff --git a/archetypes/archetype-lambda/pom.xml b/archetypes/archetype-lambda/pom.xml
index 82fed8b7c5fb..4c713738fbba 100644
--- a/archetypes/archetype-lambda/pom.xml
+++ b/archetypes/archetype-lambda/pom.xml
@@ -20,7 +20,7 @@
archetypes
software.amazon.awssdk
- 2.20.68-SNAPSHOT
+ 2.20.93-SNAPSHOT
4.0.0
archetype-lambda
diff --git a/archetypes/archetype-tools/pom.xml b/archetypes/archetype-tools/pom.xml
index 5608d0e3f6d4..a5fcd4de62be 100644
--- a/archetypes/archetype-tools/pom.xml
+++ b/archetypes/archetype-tools/pom.xml
@@ -20,7 +20,7 @@
archetypes
software.amazon.awssdk
- 2.20.68-SNAPSHOT
+ 2.20.93-SNAPSHOT
4.0.0
diff --git a/archetypes/pom.xml b/archetypes/pom.xml
index f9a82189cd41..045b2726a5f6 100644
--- a/archetypes/pom.xml
+++ b/archetypes/pom.xml
@@ -20,7 +20,7 @@
aws-sdk-java-pom
software.amazon.awssdk
- 2.20.68-SNAPSHOT
+ 2.20.93-SNAPSHOT
4.0.0
archetypes
diff --git a/aws-sdk-java/pom.xml b/aws-sdk-java/pom.xml
index 4584cc830b6a..27465ba8b86a 100644
--- a/aws-sdk-java/pom.xml
+++ b/aws-sdk-java/pom.xml
@@ -17,7 +17,7 @@
software.amazon.awssdk
aws-sdk-java-pom
- 2.20.68-SNAPSHOT
+ 2.20.93-SNAPSHOT
../pom.xml
aws-sdk-java
@@ -1738,6 +1738,31 @@ Amazon AutoScaling, etc).
osis
${awsjavasdk.version}
+
+ software.amazon.awssdk
+ mediapackagev2
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ paymentcryptographydata
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ paymentcryptography
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ codegurusecurity
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ verifiedpermissions
+ ${awsjavasdk.version}
+
${project.artifactId}-${project.version}
diff --git a/bom-internal/pom.xml b/bom-internal/pom.xml
index 78db2d4d7298..b08dff91d69d 100644
--- a/bom-internal/pom.xml
+++ b/bom-internal/pom.xml
@@ -20,7 +20,7 @@
aws-sdk-java-pom
software.amazon.awssdk
- 2.20.68-SNAPSHOT
+ 2.20.93-SNAPSHOT
4.0.0
@@ -134,6 +134,16 @@
netty-buffer
${netty.version}
+
+ io.netty
+ netty-resolver
+ ${netty.version}
+
+
+ io.netty
+ netty-resolver-dns
+ ${netty.version}
+
org.reactivestreams
reactive-streams
diff --git a/bom/pom.xml b/bom/pom.xml
index 40520dab3de2..b256978d1ff8 100644
--- a/bom/pom.xml
+++ b/bom/pom.xml
@@ -17,7 +17,7 @@
software.amazon.awssdk
aws-sdk-java-pom
- 2.20.68-SNAPSHOT
+ 2.20.93-SNAPSHOT
../pom.xml
bom
@@ -1888,6 +1888,31 @@
osis
${awsjavasdk.version}
+
+ software.amazon.awssdk
+ mediapackagev2
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ paymentcryptographydata
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ paymentcryptography
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ codegurusecurity
+ ${awsjavasdk.version}
+
+
+ software.amazon.awssdk
+ verifiedpermissions
+ ${awsjavasdk.version}
+
diff --git a/bundle/pom.xml b/bundle/pom.xml
index 7e245d7b1694..a4e78be89e8e 100644
--- a/bundle/pom.xml
+++ b/bundle/pom.xml
@@ -21,7 +21,7 @@
software.amazon.awssdk
aws-sdk-java-pom
- 2.20.68-SNAPSHOT
+ 2.20.93-SNAPSHOT
bundle
jar
diff --git a/codegen-lite-maven-plugin/pom.xml b/codegen-lite-maven-plugin/pom.xml
index 3717885c3cdd..143af638c99f 100644
--- a/codegen-lite-maven-plugin/pom.xml
+++ b/codegen-lite-maven-plugin/pom.xml
@@ -22,7 +22,7 @@
software.amazon.awssdk
aws-sdk-java-pom
- 2.20.68-SNAPSHOT
+ 2.20.93-SNAPSHOT
../pom.xml
codegen-lite-maven-plugin
diff --git a/codegen-lite/pom.xml b/codegen-lite/pom.xml
index c69a57ecc043..1dce75dc7b1d 100644
--- a/codegen-lite/pom.xml
+++ b/codegen-lite/pom.xml
@@ -21,7 +21,7 @@
software.amazon.awssdk
aws-sdk-java-pom
- 2.20.68-SNAPSHOT
+ 2.20.93-SNAPSHOT
codegen-lite
AWS Java SDK :: Code Generator Lite
diff --git a/codegen-maven-plugin/pom.xml b/codegen-maven-plugin/pom.xml
index 9315c59897f8..151355be6d21 100644
--- a/codegen-maven-plugin/pom.xml
+++ b/codegen-maven-plugin/pom.xml
@@ -22,7 +22,7 @@
software.amazon.awssdk
aws-sdk-java-pom
- 2.20.68-SNAPSHOT
+ 2.20.93-SNAPSHOT
../pom.xml
codegen-maven-plugin
diff --git a/codegen/pom.xml b/codegen/pom.xml
index 44b44f70c55d..d374e8618f34 100644
--- a/codegen/pom.xml
+++ b/codegen/pom.xml
@@ -21,7 +21,7 @@
software.amazon.awssdk
aws-sdk-java-pom
- 2.20.68-SNAPSHOT
+ 2.20.93-SNAPSHOT
codegen
AWS Java SDK :: Code Generator
diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/emitters/tasks/EndpointProviderTasks.java b/codegen/src/main/java/software/amazon/awssdk/codegen/emitters/tasks/EndpointProviderTasks.java
index 2205754fe02e..d6a91a967380 100644
--- a/codegen/src/main/java/software/amazon/awssdk/codegen/emitters/tasks/EndpointProviderTasks.java
+++ b/codegen/src/main/java/software/amazon/awssdk/codegen/emitters/tasks/EndpointProviderTasks.java
@@ -52,9 +52,11 @@ protected List createTasks() throws Exception {
tasks.add(generateDefaultProvider());
tasks.addAll(generateInterceptors());
if (shouldGenerateEndpointTests()) {
- tasks.add(generateClientTests());
tasks.add(generateProviderTests());
}
+ if (shouldGenerateEndpointTests() && shouldGenerateClientEndpointTests()) {
+ tasks.add(generateClientTests());
+ }
if (hasClientContextParams()) {
tasks.add(generateClientContextParams());
}
@@ -118,6 +120,13 @@ private boolean shouldGenerateEndpointTests() {
!generatorTaskParams.getModel().getEndpointTestSuiteModel().getTestCases().isEmpty();
}
+ private boolean shouldGenerateClientEndpointTests() {
+ CustomizationConfig customizationConfig = generatorTaskParams.getModel().getCustomizationConfig();
+ boolean noTestCasesHaveOperationInputs = model.getEndpointTestSuiteModel().getTestCases().stream()
+ .noneMatch(t -> t.getOperationInputs() != null);
+ return noTestCasesHaveOperationInputs && Boolean.TRUE.equals(customizationConfig.isGenerateEndpointClientTests());
+ }
+
private boolean hasClientContextParams() {
Map clientContextParams = model.getClientContextParams();
return clientContextParams != null && !clientContextParams.isEmpty();
diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/model/config/customization/CustomizationConfig.java b/codegen/src/main/java/software/amazon/awssdk/codegen/model/config/customization/CustomizationConfig.java
index d5aff3419b33..6913bc6e83f8 100644
--- a/codegen/src/main/java/software/amazon/awssdk/codegen/model/config/customization/CustomizationConfig.java
+++ b/codegen/src/main/java/software/amazon/awssdk/codegen/model/config/customization/CustomizationConfig.java
@@ -217,6 +217,11 @@ public class CustomizationConfig {
*/
private boolean skipEndpointTestGeneration;
+ /**
+ * Whether to generate client-level endpoint tests; overrides test case criteria such as operation inputs.
+ */
+ private boolean generateEndpointClientTests;
+
/**
* A mapping from the skipped test's description to the reason why it's being skipped.
*/
@@ -577,6 +582,14 @@ public void setSkipEndpointTestGeneration(boolean skipEndpointTestGeneration) {
this.skipEndpointTestGeneration = skipEndpointTestGeneration;
}
+ public boolean isGenerateEndpointClientTests() {
+ return generateEndpointClientTests;
+ }
+
+ public void setGenerateEndpointClientTests(boolean generateEndpointClientTests) {
+ this.generateEndpointClientTests = generateEndpointClientTests;
+ }
+
public boolean useGlobalEndpoint() {
return useGlobalEndpoint;
}
diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/builder/AsyncClientBuilderClass.java b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/builder/AsyncClientBuilderClass.java
index f8731d7dad07..bd0e6023d53e 100644
--- a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/builder/AsyncClientBuilderClass.java
+++ b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/builder/AsyncClientBuilderClass.java
@@ -18,8 +18,12 @@
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterizedTypeName;
+import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
import javax.lang.model.element.Modifier;
import software.amazon.awssdk.annotations.SdkInternalApi;
import software.amazon.awssdk.auth.token.credentials.SdkTokenProvider;
@@ -32,6 +36,9 @@
import software.amazon.awssdk.codegen.utils.AuthUtils;
import software.amazon.awssdk.core.client.config.SdkClientConfiguration;
import software.amazon.awssdk.core.client.config.SdkClientOption;
+import software.amazon.awssdk.core.interceptor.ExecutionInterceptor;
+import software.amazon.awssdk.protocols.query.interceptor.QueryParametersToBodyInterceptor;
+import software.amazon.awssdk.utils.CollectionUtils;
public class AsyncClientBuilderClass implements ClassSpec {
private final IntermediateModel model;
@@ -119,26 +126,53 @@ private MethodSpec endpointProviderMethod() {
}
private MethodSpec buildClientMethod() {
- return MethodSpec.methodBuilder("buildClient")
- .addAnnotation(Override.class)
- .addModifiers(Modifier.PROTECTED, Modifier.FINAL)
- .returns(clientInterfaceName)
- .addStatement("$T clientConfiguration = super.asyncClientConfiguration()", SdkClientConfiguration.class)
- .addStatement("this.validateClientOptions(clientConfiguration)")
- .addStatement("$T endpointOverride = null", URI.class)
- .addCode("if (clientConfiguration.option($T.ENDPOINT_OVERRIDDEN) != null"
- + "&& $T.TRUE.equals(clientConfiguration.option($T.ENDPOINT_OVERRIDDEN))) {"
- + "endpointOverride = clientConfiguration.option($T.ENDPOINT);"
- + "}",
- SdkClientOption.class, Boolean.class, SdkClientOption.class, SdkClientOption.class)
- .addStatement("$T serviceClientConfiguration = $T.builder()"
- + ".overrideConfiguration(overrideConfiguration())"
- + ".region(clientConfiguration.option($T.AWS_REGION))"
- + ".endpointOverride(endpointOverride)"
- + ".build()",
- serviceConfigClassName, serviceConfigClassName, AwsClientOption.class)
- .addStatement("return new $T(serviceClientConfiguration, clientConfiguration)", clientClassName)
- .build();
+ MethodSpec.Builder b = MethodSpec.methodBuilder("buildClient")
+ .addAnnotation(Override.class)
+ .addModifiers(Modifier.PROTECTED, Modifier.FINAL)
+ .returns(clientInterfaceName)
+ .addStatement("$T clientConfiguration = super.asyncClientConfiguration()",
+ SdkClientConfiguration.class);
+
+ addQueryProtocolInterceptors(b);
+
+ return b.addStatement("this.validateClientOptions(clientConfiguration)")
+ .addStatement("$T endpointOverride = null", URI.class)
+ .addCode("if (clientConfiguration.option($T.ENDPOINT_OVERRIDDEN) != null"
+ + "&& $T.TRUE.equals(clientConfiguration.option($T.ENDPOINT_OVERRIDDEN))) {"
+ + "endpointOverride = clientConfiguration.option($T.ENDPOINT);"
+ + "}",
+ SdkClientOption.class, Boolean.class, SdkClientOption.class, SdkClientOption.class)
+ .addStatement("$T serviceClientConfiguration = $T.builder()"
+ + ".overrideConfiguration(overrideConfiguration())"
+ + ".region(clientConfiguration.option($T.AWS_REGION))"
+ + ".endpointOverride(endpointOverride)"
+ + ".build()",
+ serviceConfigClassName, serviceConfigClassName, AwsClientOption.class)
+ .addStatement("return new $T(serviceClientConfiguration, clientConfiguration)", clientClassName)
+ .build();
+ }
+
+ private MethodSpec.Builder addQueryProtocolInterceptors(MethodSpec.Builder b) {
+ if (!model.getMetadata().isQueryProtocol()) {
+ return b;
+ }
+
+ TypeName listType = ParameterizedTypeName.get(List.class, ExecutionInterceptor.class);
+
+ b.addStatement("$T interceptors = clientConfiguration.option($T.EXECUTION_INTERCEPTORS)",
+ listType, SdkClientOption.class)
+ .addStatement("$T queryParamsToBodyInterceptor = $T.singletonList(new $T())",
+ listType, Collections.class, QueryParametersToBodyInterceptor.class)
+ .addStatement("$T customizationInterceptors = new $T<>()", listType, ArrayList.class);
+
+ List customInterceptors = model.getCustomizationConfig().getInterceptors();
+ customInterceptors.forEach(i -> b.addStatement("customizationInterceptors.add(new $T())", ClassName.bestGuess(i)));
+
+ b.addStatement("interceptors = $T.mergeLists(queryParamsToBodyInterceptor, interceptors)", CollectionUtils.class)
+ .addStatement("interceptors = $T.mergeLists(customizationInterceptors, interceptors)", CollectionUtils.class);
+
+ return b.addStatement("clientConfiguration = clientConfiguration.toBuilder().option($T.EXECUTION_INTERCEPTORS, "
+ + "interceptors).build()", SdkClientOption.class);
}
private MethodSpec bearerTokenProviderMethod() {
diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/builder/BaseClientBuilderClass.java b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/builder/BaseClientBuilderClass.java
index 1be4d730040e..72d534d5ab99 100644
--- a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/builder/BaseClientBuilderClass.java
+++ b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/builder/BaseClientBuilderClass.java
@@ -28,7 +28,6 @@
import com.squareup.javapoet.TypeSpec;
import com.squareup.javapoet.TypeVariableName;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -59,7 +58,6 @@
import software.amazon.awssdk.core.signer.Signer;
import software.amazon.awssdk.http.Protocol;
import software.amazon.awssdk.http.SdkHttpConfigurationOption;
-import software.amazon.awssdk.protocols.query.interceptor.QueryParametersToBodyInterceptor;
import software.amazon.awssdk.utils.AttributeMap;
import software.amazon.awssdk.utils.CollectionUtils;
import software.amazon.awssdk.utils.StringUtils;
@@ -262,8 +260,10 @@ private MethodSpec finalizeServiceConfigurationMethod() {
builtInInterceptors.add(endpointRulesSpecUtils.authSchemesInterceptorName());
builtInInterceptors.add(endpointRulesSpecUtils.requestModifierInterceptorName());
- for (String interceptor : model.getCustomizationConfig().getInterceptors()) {
- builtInInterceptors.add(ClassName.bestGuess(interceptor));
+ if (!model.getMetadata().isQueryProtocol()) {
+ for (String interceptor : model.getCustomizationConfig().getInterceptors()) {
+ builtInInterceptors.add(ClassName.bestGuess(interceptor));
+ }
}
for (ClassName interceptor : builtInInterceptors) {
@@ -288,16 +288,6 @@ private MethodSpec finalizeServiceConfigurationMethod() {
builder.addCode("interceptors = $T.mergeLists(interceptors, config.option($T.EXECUTION_INTERCEPTORS));\n",
CollectionUtils.class, SdkClientOption.class);
- if (model.getMetadata().isQueryProtocol()) {
- TypeName listType = ParameterizedTypeName.get(List.class, ExecutionInterceptor.class);
- builder.addStatement("$T protocolInterceptors = $T.singletonList(new $T())",
- listType,
- Collections.class,
- QueryParametersToBodyInterceptor.class);
- builder.addStatement("interceptors = $T.mergeLists(interceptors, protocolInterceptors)",
- CollectionUtils.class);
- }
-
if (model.getEndpointOperation().isPresent()) {
builder.beginControlFlow("if (!endpointDiscoveryEnabled)")
.addStatement("$1T chain = new $1T(config)", DefaultEndpointDiscoveryProviderChain.class)
diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/builder/SyncClientBuilderClass.java b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/builder/SyncClientBuilderClass.java
index 036589de04e8..8b330e76ce1b 100644
--- a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/builder/SyncClientBuilderClass.java
+++ b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/builder/SyncClientBuilderClass.java
@@ -18,8 +18,12 @@
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterizedTypeName;
+import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
import javax.lang.model.element.Modifier;
import software.amazon.awssdk.annotations.SdkInternalApi;
import software.amazon.awssdk.auth.token.credentials.SdkTokenProvider;
@@ -32,6 +36,9 @@
import software.amazon.awssdk.codegen.utils.AuthUtils;
import software.amazon.awssdk.core.client.config.SdkClientConfiguration;
import software.amazon.awssdk.core.client.config.SdkClientOption;
+import software.amazon.awssdk.core.interceptor.ExecutionInterceptor;
+import software.amazon.awssdk.protocols.query.interceptor.QueryParametersToBodyInterceptor;
+import software.amazon.awssdk.utils.CollectionUtils;
public class SyncClientBuilderClass implements ClassSpec {
private final IntermediateModel model;
@@ -119,26 +126,53 @@ private MethodSpec endpointProviderMethod() {
private MethodSpec buildClientMethod() {
- return MethodSpec.methodBuilder("buildClient")
- .addAnnotation(Override.class)
- .addModifiers(Modifier.PROTECTED, Modifier.FINAL)
- .returns(clientInterfaceName)
- .addStatement("$T clientConfiguration = super.syncClientConfiguration()", SdkClientConfiguration.class)
- .addStatement("this.validateClientOptions(clientConfiguration)")
- .addStatement("$T endpointOverride = null", URI.class)
- .addCode("if (clientConfiguration.option($T.ENDPOINT_OVERRIDDEN) != null"
- + "&& $T.TRUE.equals(clientConfiguration.option($T.ENDPOINT_OVERRIDDEN))) {"
- + "endpointOverride = clientConfiguration.option($T.ENDPOINT);"
- + "}",
- SdkClientOption.class, Boolean.class, SdkClientOption.class, SdkClientOption.class)
- .addStatement("$T serviceClientConfiguration = $T.builder()"
- + ".overrideConfiguration(overrideConfiguration())"
- + ".region(clientConfiguration.option($T.AWS_REGION))"
- + ".endpointOverride(endpointOverride)"
- + ".build()",
- serviceConfigClassName, serviceConfigClassName, AwsClientOption.class)
- .addStatement("return new $T(serviceClientConfiguration, clientConfiguration)", clientClassName)
- .build();
+ MethodSpec.Builder b = MethodSpec.methodBuilder("buildClient")
+ .addAnnotation(Override.class)
+ .addModifiers(Modifier.PROTECTED, Modifier.FINAL)
+ .returns(clientInterfaceName)
+ .addStatement("$T clientConfiguration = super.syncClientConfiguration()",
+ SdkClientConfiguration.class);
+
+ addQueryProtocolInterceptors(b);
+
+ return b.addStatement("this.validateClientOptions(clientConfiguration)")
+ .addStatement("$T endpointOverride = null", URI.class)
+ .addCode("if (clientConfiguration.option($T.ENDPOINT_OVERRIDDEN) != null"
+ + "&& $T.TRUE.equals(clientConfiguration.option($T.ENDPOINT_OVERRIDDEN))) {"
+ + "endpointOverride = clientConfiguration.option($T.ENDPOINT);"
+ + "}",
+ SdkClientOption.class, Boolean.class, SdkClientOption.class, SdkClientOption.class)
+ .addStatement("$T serviceClientConfiguration = $T.builder()"
+ + ".overrideConfiguration(overrideConfiguration())"
+ + ".region(clientConfiguration.option($T.AWS_REGION))"
+ + ".endpointOverride(endpointOverride)"
+ + ".build()",
+ serviceConfigClassName, serviceConfigClassName, AwsClientOption.class)
+ .addStatement("return new $T(serviceClientConfiguration, clientConfiguration)", clientClassName)
+ .build();
+ }
+
+ private MethodSpec.Builder addQueryProtocolInterceptors(MethodSpec.Builder b) {
+ if (!model.getMetadata().isQueryProtocol()) {
+ return b;
+ }
+
+ TypeName listType = ParameterizedTypeName.get(List.class, ExecutionInterceptor.class);
+
+ b.addStatement("$T interceptors = clientConfiguration.option($T.EXECUTION_INTERCEPTORS)",
+ listType, SdkClientOption.class)
+ .addStatement("$T queryParamsToBodyInterceptor = $T.singletonList(new $T())",
+ listType, Collections.class, QueryParametersToBodyInterceptor.class)
+ .addStatement("$T customizationInterceptors = new $T<>()", listType, ArrayList.class);
+
+ List customInterceptors = model.getCustomizationConfig().getInterceptors();
+ customInterceptors.forEach(i -> b.addStatement("customizationInterceptors.add(new $T())", ClassName.bestGuess(i)));
+
+ b.addStatement("interceptors = $T.mergeLists(queryParamsToBodyInterceptor, interceptors)", CollectionUtils.class)
+ .addStatement("interceptors = $T.mergeLists(customizationInterceptors, interceptors)", CollectionUtils.class);
+
+ return b.addStatement("clientConfiguration = clientConfiguration.toBuilder().option($T.EXECUTION_INTERCEPTORS, "
+ + "interceptors).build()", SdkClientOption.class);
}
private MethodSpec tokenProviderMethodImpl() {
diff --git a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/rules/EndpointRulesClientTestSpec.java b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/rules/EndpointRulesClientTestSpec.java
index bdea3ebb52ff..4a0c69f04d71 100644
--- a/codegen/src/main/java/software/amazon/awssdk/codegen/poet/rules/EndpointRulesClientTestSpec.java
+++ b/codegen/src/main/java/software/amazon/awssdk/codegen/poet/rules/EndpointRulesClientTestSpec.java
@@ -111,6 +111,10 @@ public TypeSpec poetSpec() {
b.addField(s3RegionEndpointSystemPropertySaveValueField());
}
+ if (serviceHasNoMatchingTestCases()) {
+ return b.build();
+ }
+
b.addMethod(methodSetupMethod());
b.addMethod(teardownMethod());
@@ -215,16 +219,22 @@ private MethodSpec syncTestsSourceMethod() {
.addModifiers(Modifier.PRIVATE, Modifier.STATIC)
.returns(ParameterizedTypeName.get(List.class, SyncTestCase.class));
+
+
+
b.addCode("return $T.asList(", Arrays.class);
EndpointTestSuiteModel endpointTestSuiteModel = model.getEndpointTestSuiteModel();
Iterator testIter = endpointTestSuiteModel.getTestCases().iterator();
-
+ boolean isFirst = true;
while (testIter.hasNext()) {
EndpointTestModel test = testIter.next();
-
- if (test.getOperationInputs() != null) {
+ if (testCaseHasOperationInputs(test)) {
Iterator operationInputsIter = test.getOperationInputs().iterator();
+ if (!isFirst) {
+ b.addCode(", ");
+ }
+ isFirst = false;
while (operationInputsIter.hasNext()) {
OperationInput opInput = operationInputsIter.next();
OperationModel opModel = model.getOperation(opInput.getOperationName());
@@ -240,7 +250,11 @@ private MethodSpec syncTestsSourceMethod() {
b.addCode(",");
}
}
- } else {
+ } else if (shouldGenerateClientTestsOverride()) {
+ if (!isFirst) {
+ b.addCode(", ");
+ }
+ isFirst = false;
b.addCode("new $T($S, $L, $L$L)",
SyncTestCase.class,
test.getDocumentation(),
@@ -248,10 +262,6 @@ private MethodSpec syncTestsSourceMethod() {
TestGeneratorUtils.createExpect(test.getExpect(), defaultOpModel, null),
getSkipReasonBlock(test.getDocumentation()));
}
-
- if (testIter.hasNext()) {
- b.addCode(",");
- }
}
b.addStatement(")");
@@ -365,12 +375,16 @@ private MethodSpec asyncTestsSourceMethod() {
EndpointTestSuiteModel endpointTestSuiteModel = model.getEndpointTestSuiteModel();
Iterator testIter = endpointTestSuiteModel.getTestCases().iterator();
-
+ boolean isFirst = true;
while (testIter.hasNext()) {
EndpointTestModel test = testIter.next();
- if (test.getOperationInputs() != null) {
+ if (testCaseHasOperationInputs(test)) {
Iterator operationInputsIter = test.getOperationInputs().iterator();
+ if (!isFirst) {
+ b.addCode(", ");
+ }
+ isFirst = false;
while (operationInputsIter.hasNext()) {
OperationInput opInput = operationInputsIter.next();
OperationModel opModel = model.getOperation(opInput.getOperationName());
@@ -386,7 +400,11 @@ private MethodSpec asyncTestsSourceMethod() {
b.addCode(",");
}
}
- } else {
+ } else if (shouldGenerateClientTestsOverride()) {
+ if (!isFirst) {
+ b.addCode(", ");
+ }
+ isFirst = false;
b.addCode("new $T($S, $L, $L$L)",
AsyncTestCase.class,
test.getDocumentation(),
@@ -394,10 +412,6 @@ private MethodSpec asyncTestsSourceMethod() {
TestGeneratorUtils.createExpect(test.getExpect(), defaultOpModel, null),
getSkipReasonBlock(test.getDocumentation()));
}
-
- if (testIter.hasNext()) {
- b.addCode(",");
- }
}
b.addStatement(")");
@@ -649,6 +663,27 @@ private Map getSkippedTests() {
return skippedTests;
}
+ private boolean serviceHasNoMatchingTestCases() {
+ boolean noTestCasesHaveOperationInputs = model.getEndpointTestSuiteModel().getTestCases().stream()
+ .noneMatch(EndpointRulesClientTestSpec::testCaseHasOperationInputs);
+ return noTestCasesHaveOperationInputs && !shouldGenerateClientTestsOverride();
+ }
+
+ /**
+ * Always generate client endpoint tests if the test case has operation inputs
+ */
+ private static boolean testCaseHasOperationInputs(EndpointTestModel test) {
+ return test.getOperationInputs() != null;
+ }
+
+ /**
+ * Some services can run tests without operation inputs if there are other conditions that allow
+ * codegen to create a functioning test case
+ */
+ private boolean shouldGenerateClientTestsOverride() {
+ return model.getCustomizationConfig().isGenerateEndpointClientTests();
+ }
+
private CodeBlock getSkipReasonBlock(String testName) {
if (getSkippedTests().containsKey(testName)) {
Validate.notNull(getSkippedTests().get(testName), "Test %s must have a reason for skipping", testName);
diff --git a/codegen/src/main/resources/software/amazon/awssdk/codegen/rules/AwsEndpointProviderUtils.java.resource b/codegen/src/main/resources/software/amazon/awssdk/codegen/rules/AwsEndpointProviderUtils.java.resource
index ee11d604d2a2..db40f3f672e7 100644
--- a/codegen/src/main/resources/software/amazon/awssdk/codegen/rules/AwsEndpointProviderUtils.java.resource
+++ b/codegen/src/main/resources/software/amazon/awssdk/codegen/rules/AwsEndpointProviderUtils.java.resource
@@ -150,19 +150,33 @@ public final class AwsEndpointProviderUtils {
String clientEndpointPath = clientEndpoint.getRawPath();
// [client endpoint path]/[request path]
- String requestPath = request.getUri().getRawPath();
+ String requestPath = request.encodedPath();
// [client endpoint path]/[additional path added by resolver]
String resolvedUriPath = resolvedUri.getRawPath();
- // our goal is to construct [client endpoint path]/[additional path added by resolver]/[request path], so we
- // just need to strip the client endpoint path from the marshalled request path to isolate just the part added
- // by the marshaller
- String requestPathWithClientPathRemoved = StringUtils.replaceOnce(requestPath, clientEndpointPath, "");
- String finalPath = SdkHttpUtils.appendUri(resolvedUriPath, requestPathWithClientPathRemoved);
+ String finalPath = requestPath;
+
+ // If there is an additional path added by resolver, i.e., [additional path added by resolver] not null,
+ // we need to combine the path
+ if (!resolvedUriPath.equals(clientEndpointPath)) {
+ finalPath = combinePath(clientEndpointPath, requestPath, resolvedUriPath);
+ }
return request.toBuilder().protocol(resolvedUri.getScheme()).host(resolvedUri.getHost()).port(resolvedUri.getPort())
- .encodedPath(finalPath).build();
+ .encodedPath(finalPath).build();
+ }
+
+ /**
+ * Our goal is to construct [client endpoint path]/[additional path added by resolver]/[request path], so we just need to
+ * strip the client endpoint path from the marshalled request path to isolate just the part added by the marshaller. Trailing
+ * slash is removed from client endpoint path before stripping because it could cause the leading slash to be removed from the
+ * request path: e.g., StringUtils.replaceOnce("/", "//test", "") generates "/test" and the expected result is "//test"
+ */
+ private static String combinePath(String clientEndpointPath, String requestPath, String resolvedUriPath) {
+ String requestPathWithClientPathRemoved = StringUtils.replaceOnce(requestPath, clientEndpointPath, "");
+ String finalPath = SdkHttpUtils.appendUri(resolvedUriPath, requestPathWithClientPathRemoved);
+ return finalPath;
}
public static AwsRequest addHeaders(AwsRequest request, Map> headers) {
diff --git a/codegen/src/main/resources/software/amazon/awssdk/codegen/rules/partitions.json.resource b/codegen/src/main/resources/software/amazon/awssdk/codegen/rules/partitions.json.resource
index 5d5d39a6c9d5..2018b804f3d7 100644
--- a/codegen/src/main/resources/software/amazon/awssdk/codegen/rules/partitions.json.resource
+++ b/codegen/src/main/resources/software/amazon/awssdk/codegen/rules/partitions.json.resource
@@ -187,6 +187,17 @@
},
"regionRegex" : "^eu\\-isoe\\-\\w+\\-\\d+$",
"regions" : { }
+ }, {
+ "id" : "aws-iso-f",
+ "outputs" : {
+ "dnsSuffix" : "csp.hci.ic.gov",
+ "dualStackDnsSuffix" : "csp.hci.ic.gov",
+ "name" : "aws-iso-f",
+ "supportsDualStack" : false,
+ "supportsFIPS" : true
+ },
+ "regionRegex" : "^us\\-isof\\-\\w+\\-\\d+$",
+ "regions" : { }
} ],
"version" : "1.1"
}
\ No newline at end of file
diff --git a/codegen/src/test/java/software/amazon/awssdk/codegen/internal/QueryProtocolCustomTestInterceptor.java b/codegen/src/test/java/software/amazon/awssdk/codegen/internal/QueryProtocolCustomTestInterceptor.java
new file mode 100644
index 000000000000..6bd5206d9b11
--- /dev/null
+++ b/codegen/src/test/java/software/amazon/awssdk/codegen/internal/QueryProtocolCustomTestInterceptor.java
@@ -0,0 +1,12 @@
+package software.amazon.awssdk.codegen.internal;
+
+import software.amazon.awssdk.annotations.SdkInternalApi;
+import software.amazon.awssdk.codegen.poet.builder.BuilderClassTest;
+
+/**
+ * Empty no-op test interceptor for query protocols to view generated code in test-query-sync-client-builder-class.java and
+ * test-query-async-client-builder-class.java and validate in {@link BuilderClassTest}.
+ */
+@SdkInternalApi
+public class QueryProtocolCustomTestInterceptor {
+}
diff --git a/codegen/src/test/java/software/amazon/awssdk/codegen/poet/builder/BuilderClassTest.java b/codegen/src/test/java/software/amazon/awssdk/codegen/poet/builder/BuilderClassTest.java
index b111e47bf3c0..3edafd55dab3 100644
--- a/codegen/src/test/java/software/amazon/awssdk/codegen/poet/builder/BuilderClassTest.java
+++ b/codegen/src/test/java/software/amazon/awssdk/codegen/poet/builder/BuilderClassTest.java
@@ -58,6 +58,16 @@ public void baseQueryClientBuilderClass() throws Exception {
validateQueryGeneration(BaseClientBuilderClass::new, "test-query-client-builder-class.java");
}
+ @Test
+ public void syncQueryClientBuilderClass() throws Exception {
+ validateQueryGeneration(SyncClientBuilderClass::new, "test-query-sync-client-builder-class.java");
+ }
+
+ @Test
+ public void asyncQueryClientBuilderClass() throws Exception {
+ validateQueryGeneration(AsyncClientBuilderClass::new, "test-query-async-client-builder-class.java");
+ }
+
@Test
public void syncClientBuilderInterface() throws Exception {
validateGeneration(SyncClientBuilderInterface::new, "test-sync-client-builder-interface.java");
diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-query-async-client-builder-class.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-query-async-client-builder-class.java
new file mode 100644
index 000000000000..f71429db299c
--- /dev/null
+++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-query-async-client-builder-class.java
@@ -0,0 +1,61 @@
+package software.amazon.awssdk.services.query;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import software.amazon.awssdk.annotations.Generated;
+import software.amazon.awssdk.annotations.SdkInternalApi;
+import software.amazon.awssdk.auth.token.credentials.SdkTokenProvider;
+import software.amazon.awssdk.awscore.client.config.AwsClientOption;
+import software.amazon.awssdk.codegen.internal.QueryProtocolCustomTestInterceptor;
+import software.amazon.awssdk.core.client.config.SdkClientConfiguration;
+import software.amazon.awssdk.core.client.config.SdkClientOption;
+import software.amazon.awssdk.core.interceptor.ExecutionInterceptor;
+import software.amazon.awssdk.protocols.query.interceptor.QueryParametersToBodyInterceptor;
+import software.amazon.awssdk.services.query.endpoints.QueryEndpointProvider;
+import software.amazon.awssdk.utils.CollectionUtils;
+
+/**
+ * Internal implementation of {@link QueryAsyncClientBuilder}.
+ */
+@Generated("software.amazon.awssdk:codegen")
+@SdkInternalApi
+final class DefaultQueryAsyncClientBuilder extends DefaultQueryBaseClientBuilder
+ implements QueryAsyncClientBuilder {
+ @Override
+ public DefaultQueryAsyncClientBuilder endpointProvider(QueryEndpointProvider endpointProvider) {
+ clientConfiguration.option(SdkClientOption.ENDPOINT_PROVIDER, endpointProvider);
+ return this;
+ }
+
+ @Override
+ public DefaultQueryAsyncClientBuilder tokenProvider(SdkTokenProvider tokenProvider) {
+ clientConfiguration.option(AwsClientOption.TOKEN_PROVIDER, tokenProvider);
+ return this;
+ }
+
+ @Override
+ protected final QueryAsyncClient buildClient() {
+ SdkClientConfiguration clientConfiguration = super.asyncClientConfiguration();
+ List interceptors = clientConfiguration.option(SdkClientOption.EXECUTION_INTERCEPTORS);
+ List queryParamsToBodyInterceptor = Collections
+ .singletonList(new QueryParametersToBodyInterceptor());
+ List customizationInterceptors = new ArrayList<>();
+ customizationInterceptors.add(new QueryProtocolCustomTestInterceptor());
+ interceptors = CollectionUtils.mergeLists(queryParamsToBodyInterceptor, interceptors);
+ interceptors = CollectionUtils.mergeLists(customizationInterceptors, interceptors);
+ clientConfiguration = clientConfiguration.toBuilder().option(SdkClientOption.EXECUTION_INTERCEPTORS, interceptors)
+ .build();
+ this.validateClientOptions(clientConfiguration);
+ URI endpointOverride = null;
+ if (clientConfiguration.option(SdkClientOption.ENDPOINT_OVERRIDDEN) != null
+ && Boolean.TRUE.equals(clientConfiguration.option(SdkClientOption.ENDPOINT_OVERRIDDEN))) {
+ endpointOverride = clientConfiguration.option(SdkClientOption.ENDPOINT);
+ }
+ QueryServiceClientConfiguration serviceClientConfiguration = QueryServiceClientConfiguration.builder()
+ .overrideConfiguration(overrideConfiguration()).region(clientConfiguration.option(AwsClientOption.AWS_REGION))
+ .endpointOverride(endpointOverride).build();
+ return new DefaultQueryAsyncClient(serviceClientConfiguration, clientConfiguration);
+ }
+}
diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-query-client-builder-class.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-query-client-builder-class.java
index 141b27f6cfe0..e1b5cf7bf055 100644
--- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-query-client-builder-class.java
+++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-query-client-builder-class.java
@@ -1,7 +1,6 @@
package software.amazon.awssdk.services.query;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import software.amazon.awssdk.annotations.Generated;
import software.amazon.awssdk.annotations.SdkInternalApi;
@@ -17,7 +16,6 @@
import software.amazon.awssdk.core.interceptor.ClasspathInterceptorChainFactory;
import software.amazon.awssdk.core.interceptor.ExecutionInterceptor;
import software.amazon.awssdk.core.signer.Signer;
-import software.amazon.awssdk.protocols.query.interceptor.QueryParametersToBodyInterceptor;
import software.amazon.awssdk.services.query.endpoints.QueryClientContextParams;
import software.amazon.awssdk.services.query.endpoints.QueryEndpointProvider;
import software.amazon.awssdk.services.query.endpoints.internal.QueryEndpointAuthSchemeInterceptor;
@@ -64,8 +62,6 @@ protected final SdkClientConfiguration finalizeServiceConfiguration(SdkClientCon
interceptors = CollectionUtils.mergeLists(endpointInterceptors, interceptors);
interceptors = CollectionUtils.mergeLists(interceptors, additionalInterceptors);
interceptors = CollectionUtils.mergeLists(interceptors, config.option(SdkClientOption.EXECUTION_INTERCEPTORS));
- List protocolInterceptors = Collections.singletonList(new QueryParametersToBodyInterceptor());
- interceptors = CollectionUtils.mergeLists(interceptors, protocolInterceptors);
return config.toBuilder().option(SdkClientOption.EXECUTION_INTERCEPTORS, interceptors)
.option(SdkClientOption.CLIENT_CONTEXT_PARAMS, clientContextParams.build()).build();
}
diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-query-sync-client-builder-class.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-query-sync-client-builder-class.java
new file mode 100644
index 000000000000..56b94d1d3189
--- /dev/null
+++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-query-sync-client-builder-class.java
@@ -0,0 +1,61 @@
+package software.amazon.awssdk.services.query;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import software.amazon.awssdk.annotations.Generated;
+import software.amazon.awssdk.annotations.SdkInternalApi;
+import software.amazon.awssdk.auth.token.credentials.SdkTokenProvider;
+import software.amazon.awssdk.awscore.client.config.AwsClientOption;
+import software.amazon.awssdk.codegen.internal.QueryProtocolCustomTestInterceptor;
+import software.amazon.awssdk.core.client.config.SdkClientConfiguration;
+import software.amazon.awssdk.core.client.config.SdkClientOption;
+import software.amazon.awssdk.core.interceptor.ExecutionInterceptor;
+import software.amazon.awssdk.protocols.query.interceptor.QueryParametersToBodyInterceptor;
+import software.amazon.awssdk.services.query.endpoints.QueryEndpointProvider;
+import software.amazon.awssdk.utils.CollectionUtils;
+
+/**
+ * Internal implementation of {@link QueryClientBuilder}.
+ */
+@Generated("software.amazon.awssdk:codegen")
+@SdkInternalApi
+final class DefaultQueryClientBuilder extends DefaultQueryBaseClientBuilder implements
+ QueryClientBuilder {
+ @Override
+ public DefaultQueryClientBuilder endpointProvider(QueryEndpointProvider endpointProvider) {
+ clientConfiguration.option(SdkClientOption.ENDPOINT_PROVIDER, endpointProvider);
+ return this;
+ }
+
+ @Override
+ public DefaultQueryClientBuilder tokenProvider(SdkTokenProvider tokenProvider) {
+ clientConfiguration.option(AwsClientOption.TOKEN_PROVIDER, tokenProvider);
+ return this;
+ }
+
+ @Override
+ protected final QueryClient buildClient() {
+ SdkClientConfiguration clientConfiguration = super.syncClientConfiguration();
+ List interceptors = clientConfiguration.option(SdkClientOption.EXECUTION_INTERCEPTORS);
+ List queryParamsToBodyInterceptor = Collections
+ .singletonList(new QueryParametersToBodyInterceptor());
+ List customizationInterceptors = new ArrayList<>();
+ customizationInterceptors.add(new QueryProtocolCustomTestInterceptor());
+ interceptors = CollectionUtils.mergeLists(queryParamsToBodyInterceptor, interceptors);
+ interceptors = CollectionUtils.mergeLists(customizationInterceptors, interceptors);
+ clientConfiguration = clientConfiguration.toBuilder().option(SdkClientOption.EXECUTION_INTERCEPTORS, interceptors)
+ .build();
+ this.validateClientOptions(clientConfiguration);
+ URI endpointOverride = null;
+ if (clientConfiguration.option(SdkClientOption.ENDPOINT_OVERRIDDEN) != null
+ && Boolean.TRUE.equals(clientConfiguration.option(SdkClientOption.ENDPOINT_OVERRIDDEN))) {
+ endpointOverride = clientConfiguration.option(SdkClientOption.ENDPOINT);
+ }
+ QueryServiceClientConfiguration serviceClientConfiguration = QueryServiceClientConfiguration.builder()
+ .overrideConfiguration(overrideConfiguration()).region(clientConfiguration.option(AwsClientOption.AWS_REGION))
+ .endpointOverride(endpointOverride).build();
+ return new DefaultQueryClient(serviceClientConfiguration, clientConfiguration);
+ }
+}
diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/c2j/query/customization.config b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/c2j/query/customization.config
index c95b6d2e5f63..18824fa00a30 100644
--- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/c2j/query/customization.config
+++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/c2j/query/customization.config
@@ -2,7 +2,10 @@
"authPolicyActions" : {
"skip" : true
},
- "skipEndpointTests": {
+ "skipEndpointTests": {
"test case 4": "Does not work"
- }
+ },
+ "interceptors": [
+ "software.amazon.awssdk.codegen.internal.QueryProtocolCustomTestInterceptor"
+ ]
}
\ No newline at end of file
diff --git a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/rules/endpoint-rules-test-class.java b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/rules/endpoint-rules-test-class.java
index fdcaad2a56a0..8872858320db 100644
--- a/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/rules/endpoint-rules-test-class.java
+++ b/codegen/src/test/resources/software/amazon/awssdk/codegen/poet/rules/endpoint-rules-test-class.java
@@ -18,7 +18,6 @@
import software.amazon.awssdk.services.query.QueryAsyncClientBuilder;
import software.amazon.awssdk.services.query.QueryClient;
import software.amazon.awssdk.services.query.QueryClientBuilder;
-import software.amazon.awssdk.services.query.model.APostOperationRequest;
import software.amazon.awssdk.services.query.model.ChecksumStructure;
import software.amazon.awssdk.services.query.model.OperationWithContextParamRequest;
@@ -47,26 +46,6 @@ public void asyncClient_usesCorrectEndpoint(AsyncTestCase tc) {
private static List syncTestCases() {
return Arrays.asList(
- new SyncTestCase("test case 1", () -> {
- QueryClientBuilder builder = QueryClient.builder();
- builder.credentialsProvider(BaseRuleSetClientTest.CREDENTIALS_PROVIDER);
- builder.tokenProvider(BaseRuleSetClientTest.TOKEN_PROVIDER);
- builder.httpClient(getSyncHttpClient());
- builder.region(Region.of("us-east-1"));
- APostOperationRequest request = APostOperationRequest.builder().build();
- builder.build().aPostOperation(request);
- }, Expect.builder().endpoint(Endpoint.builder().url(URI.create("https://foo-myservice.aws")).build()).build()),
- new SyncTestCase("test case 2", () -> {
- QueryClientBuilder builder = QueryClient.builder();
- builder.credentialsProvider(BaseRuleSetClientTest.CREDENTIALS_PROVIDER);
- builder.tokenProvider(BaseRuleSetClientTest.TOKEN_PROVIDER);
- builder.httpClient(getSyncHttpClient());
- builder.region(Region.of("us-east-1"));
- builder.booleanContextParam(true);
- builder.stringContextParam("this is a test");
- APostOperationRequest request = APostOperationRequest.builder().build();
- builder.build().aPostOperation(request);
- }, Expect.builder().endpoint(Endpoint.builder().url(URI.create("https://foo-myservice.aws")).build()).build()),
new SyncTestCase("test case 3", () -> {
QueryClientBuilder builder = QueryClient.builder();
builder.credentialsProvider(BaseRuleSetClientTest.CREDENTIALS_PROVIDER);
@@ -88,14 +67,6 @@ private static List syncTestCases() {
builder.build().operationWithContextParam(request);
}, Expect.builder().endpoint(Endpoint.builder().url(URI.create("https://myservice.aws")).build()).build(),
"Does not work"),
- new SyncTestCase("For region us-iso-west-1 with FIPS enabled and DualStack enabled", () -> {
- QueryClientBuilder builder = QueryClient.builder();
- builder.credentialsProvider(BaseRuleSetClientTest.CREDENTIALS_PROVIDER);
- builder.tokenProvider(BaseRuleSetClientTest.TOKEN_PROVIDER);
- builder.httpClient(getSyncHttpClient());
- APostOperationRequest request = APostOperationRequest.builder().build();
- builder.build().aPostOperation(request);
- }, Expect.builder().error("Should have been skipped!").build(), "Client builder does the validation"),
new SyncTestCase("Has complex operation input", () -> {
QueryClientBuilder builder = QueryClient.builder();
builder.credentialsProvider(BaseRuleSetClientTest.CREDENTIALS_PROVIDER);
@@ -104,39 +75,11 @@ private static List syncTestCases() {
OperationWithContextParamRequest request = OperationWithContextParamRequest.builder()
.nestedMember(ChecksumStructure.builder().checksumMode("foo").build()).build();
builder.build().operationWithContextParam(request);
- }, Expect.builder().error("Missing info").build()), new SyncTestCase("Has has undeclared input parameter",
- () -> {
- QueryClientBuilder builder = QueryClient.builder();
- builder.credentialsProvider(BaseRuleSetClientTest.CREDENTIALS_PROVIDER);
- builder.tokenProvider(BaseRuleSetClientTest.TOKEN_PROVIDER);
- builder.httpClient(getSyncHttpClient());
- APostOperationRequest request = APostOperationRequest.builder().build();
- builder.build().aPostOperation(request);
- }, Expect.builder().error("Missing info").build()));
+ }, Expect.builder().error("Missing info").build()));
}
private static List asyncTestCases() {
return Arrays.asList(
- new AsyncTestCase("test case 1", () -> {
- QueryAsyncClientBuilder builder = QueryAsyncClient.builder();
- builder.credentialsProvider(BaseRuleSetClientTest.CREDENTIALS_PROVIDER);
- builder.tokenProvider(BaseRuleSetClientTest.TOKEN_PROVIDER);
- builder.httpClient(getAsyncHttpClient());
- builder.region(Region.of("us-east-1"));
- APostOperationRequest request = APostOperationRequest.builder().build();
- return builder.build().aPostOperation(request);
- }, Expect.builder().endpoint(Endpoint.builder().url(URI.create("https://foo-myservice.aws")).build()).build()),
- new AsyncTestCase("test case 2", () -> {
- QueryAsyncClientBuilder builder = QueryAsyncClient.builder();
- builder.credentialsProvider(BaseRuleSetClientTest.CREDENTIALS_PROVIDER);
- builder.tokenProvider(BaseRuleSetClientTest.TOKEN_PROVIDER);
- builder.httpClient(getAsyncHttpClient());
- builder.region(Region.of("us-east-1"));
- builder.booleanContextParam(true);
- builder.stringContextParam("this is a test");
- APostOperationRequest request = APostOperationRequest.builder().build();
- return builder.build().aPostOperation(request);
- }, Expect.builder().endpoint(Endpoint.builder().url(URI.create("https://foo-myservice.aws")).build()).build()),
new AsyncTestCase("test case 3", () -> {
QueryAsyncClientBuilder builder = QueryAsyncClient.builder();
builder.credentialsProvider(BaseRuleSetClientTest.CREDENTIALS_PROVIDER);
@@ -158,14 +101,6 @@ private static List asyncTestCases() {
return builder.build().operationWithContextParam(request);
}, Expect.builder().endpoint(Endpoint.builder().url(URI.create("https://myservice.aws")).build()).build(),
"Does not work"),
- new AsyncTestCase("For region us-iso-west-1 with FIPS enabled and DualStack enabled", () -> {
- QueryAsyncClientBuilder builder = QueryAsyncClient.builder();
- builder.credentialsProvider(BaseRuleSetClientTest.CREDENTIALS_PROVIDER);
- builder.tokenProvider(BaseRuleSetClientTest.TOKEN_PROVIDER);
- builder.httpClient(getAsyncHttpClient());
- APostOperationRequest request = APostOperationRequest.builder().build();
- return builder.build().aPostOperation(request);
- }, Expect.builder().error("Should have been skipped!").build(), "Client builder does the validation"),
new AsyncTestCase("Has complex operation input", () -> {
QueryAsyncClientBuilder builder = QueryAsyncClient.builder();
builder.credentialsProvider(BaseRuleSetClientTest.CREDENTIALS_PROVIDER);
@@ -174,14 +109,6 @@ private static List asyncTestCases() {
OperationWithContextParamRequest request = OperationWithContextParamRequest.builder()
.nestedMember(ChecksumStructure.builder().checksumMode("foo").build()).build();
return builder.build().operationWithContextParam(request);
- }, Expect.builder().error("Missing info").build()), new AsyncTestCase("Has has undeclared input parameter",
- () -> {
- QueryAsyncClientBuilder builder = QueryAsyncClient.builder();
- builder.credentialsProvider(BaseRuleSetClientTest.CREDENTIALS_PROVIDER);
- builder.tokenProvider(BaseRuleSetClientTest.TOKEN_PROVIDER);
- builder.httpClient(getAsyncHttpClient());
- APostOperationRequest request = APostOperationRequest.builder().build();
- return builder.build().aPostOperation(request);
- }, Expect.builder().error("Missing info").build()));
+ }, Expect.builder().error("Missing info").build()));
}
}
diff --git a/core/annotations/pom.xml b/core/annotations/pom.xml
index 0511bf3ce9f9..f0ceb1412bd8 100644
--- a/core/annotations/pom.xml
+++ b/core/annotations/pom.xml
@@ -20,7 +20,7 @@
core
software.amazon.awssdk
- 2.20.68-SNAPSHOT
+ 2.20.93-SNAPSHOT
4.0.0
diff --git a/core/arns/pom.xml b/core/arns/pom.xml
index 81fd186432fe..c9fe1f92c5df 100644
--- a/core/arns/pom.xml
+++ b/core/arns/pom.xml
@@ -20,7 +20,7 @@
core
software.amazon.awssdk
- 2.20.68-SNAPSHOT
+ 2.20.93-SNAPSHOT
4.0.0
diff --git a/core/auth-crt/pom.xml b/core/auth-crt/pom.xml
index 94ee56fe165c..bc9fd1f4c9bd 100644
--- a/core/auth-crt/pom.xml
+++ b/core/auth-crt/pom.xml
@@ -22,7 +22,7 @@
software.amazon.awssdk
core
- 2.20.68-SNAPSHOT
+ 2.20.93-SNAPSHOT
auth-crt
diff --git a/core/auth/pom.xml b/core/auth/pom.xml
index 96d131db7f10..4407ae40b2b5 100644
--- a/core/auth/pom.xml
+++ b/core/auth/pom.xml
@@ -22,7 +22,7 @@
software.amazon.awssdk
core
- 2.20.68-SNAPSHOT
+ 2.20.93-SNAPSHOT
auth
diff --git a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/AwsSessionCredentials.java b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/AwsSessionCredentials.java
index 8acd9efd02b9..de90e393708f 100644
--- a/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/AwsSessionCredentials.java
+++ b/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/AwsSessionCredentials.java
@@ -15,7 +15,9 @@
package software.amazon.awssdk.auth.credentials;
+import java.time.Instant;
import java.util.Objects;
+import java.util.Optional;
import software.amazon.awssdk.annotations.Immutable;
import software.amazon.awssdk.annotations.SdkPublicApi;
import software.amazon.awssdk.utils.ToString;
@@ -34,10 +36,20 @@ public final class AwsSessionCredentials implements AwsCredentials {
private final String secretAccessKey;
private final String sessionToken;
- private AwsSessionCredentials(String accessKey, String secretKey, String sessionToken) {
- this.accessKeyId = Validate.paramNotNull(accessKey, "accessKey");
- this.secretAccessKey = Validate.paramNotNull(secretKey, "secretKey");
- this.sessionToken = Validate.paramNotNull(sessionToken, "sessionToken");
+ private final Instant expirationTime;
+
+ private AwsSessionCredentials(Builder builder) {
+ this.accessKeyId = Validate.paramNotNull(builder.accessKeyId, "accessKey");
+ this.secretAccessKey = Validate.paramNotNull(builder.secretAccessKey, "secretKey");
+ this.sessionToken = Validate.paramNotNull(builder.sessionToken, "sessionToken");
+ this.expirationTime = builder.expirationTime;
+ }
+
+ /**
+ * Returns a builder for this object.
+ */
+ public static Builder builder() {
+ return new Builder();
}
/**
@@ -49,7 +61,7 @@ private AwsSessionCredentials(String accessKey, String secretKey, String session
* received temporary permission to access some resource.
*/
public static AwsSessionCredentials create(String accessKey, String secretKey, String sessionToken) {
- return new AwsSessionCredentials(accessKey, secretKey, sessionToken);
+ return builder().accessKeyId(accessKey).secretAccessKey(secretKey).sessionToken(sessionToken).build();
}
/**
@@ -68,6 +80,13 @@ public String secretAccessKey() {
return secretAccessKey;
}
+ /**
+ * Retrieve the expiration time of these credentials, if it exists.
+ */
+ public Optional expirationTime() {
+ return Optional.ofNullable(expirationTime);
+ }
+
/**
* Retrieve the AWS session token. This token is retrieved from an AWS token service, and is used for authenticating that this
* user has received temporary permission to access some resource.
@@ -95,7 +114,8 @@ public boolean equals(Object o) {
AwsSessionCredentials that = (AwsSessionCredentials) o;
return Objects.equals(accessKeyId, that.accessKeyId) &&
Objects.equals(secretAccessKey, that.secretAccessKey) &&
- Objects.equals(sessionToken, that.sessionToken);
+ Objects.equals(sessionToken, that.sessionToken) &&
+ Objects.equals(expirationTime, that.expirationTime().orElse(null));
}
@Override
@@ -104,6 +124,57 @@ public int hashCode() {
hashCode = 31 * hashCode + Objects.hashCode(accessKeyId());
hashCode = 31 * hashCode + Objects.hashCode(secretAccessKey());
hashCode = 31 * hashCode + Objects.hashCode(sessionToken());
+ hashCode = 31 * hashCode + Objects.hashCode(expirationTime);
return hashCode;
}
+
+ /**
+ * A builder for creating an instance of {@link AwsSessionCredentials}. This can be created with the static
+ * {@link #builder()} method.
+ */
+ public static final class Builder {
+ private String accessKeyId;
+ private String secretAccessKey;
+ private String sessionToken;
+ private Instant expirationTime;
+
+ /**
+ * The AWS access key, used to identify the user interacting with services. Required.
+ */
+ public Builder accessKeyId(String accessKeyId) {
+ this.accessKeyId = accessKeyId;
+ return this;
+ }
+
+ /**
+ * The AWS secret access key, used to authenticate the user interacting with services. Required
+ */
+ public Builder secretAccessKey(String secretAccessKey) {
+ this.secretAccessKey = secretAccessKey;
+ return this;
+ }
+
+ /**
+ * The AWS session token, retrieved from an AWS token service, used for authenticating that this user has
+ * received temporary permission to access some resource. Required
+ */
+ public Builder sessionToken(String sessionToken) {
+ this.sessionToken = sessionToken;
+ return this;
+ }
+
+ /**
+ * The time after which this identity will no longer be valid. If this is empty,
+ * an expiration time is not known (but the identity may still expire at some
+ * time in the future).
+ */
+ public Builder expirationTime(Instant expirationTime) {
+ this.expirationTime = expirationTime;
+ return this;
+ }
+
+ public AwsSessionCredentials build() {
+ return new AwsSessionCredentials(this);
+ }
+ }
}
diff --git a/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/internal/AwsSessionCredentialsTest.java b/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/internal/AwsSessionCredentialsTest.java
index 8e9c6a9ee975..e0ccc19c5954 100644
--- a/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/internal/AwsSessionCredentialsTest.java
+++ b/core/auth/src/test/java/software/amazon/awssdk/auth/credentials/internal/AwsSessionCredentialsTest.java
@@ -15,24 +15,64 @@
package software.amazon.awssdk.auth.credentials.internal;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.assertj.core.api.Assertions.assertThat;
-
+import nl.jqno.equalsverifier.EqualsVerifier;
import org.junit.jupiter.api.Test;
import software.amazon.awssdk.auth.credentials.AwsSessionCredentials;
public class AwsSessionCredentialsTest {
+ private static final String ACCESS_KEY_ID = "accessKeyId";
+ private static final String SECRET_ACCESS_KEY = "secretAccessKey";
+ private static final String SESSION_TOKEN = "sessionToken";
+
+ public void equalsHashcode() {
+ EqualsVerifier.forClass(AwsSessionCredentials.class)
+ .verify();
+ }
+
+ @Test
+ public void emptyBuilder_ThrowsException() {
+ assertThrows(NullPointerException.class, () -> AwsSessionCredentials.builder().build());
+ }
+
+ @Test
+ public void builderMissingSessionToken_ThrowsException() {
+ assertThrows(NullPointerException.class, () -> AwsSessionCredentials.builder()
+ .accessKeyId(ACCESS_KEY_ID)
+ .secretAccessKey(SECRET_ACCESS_KEY)
+ .build());
+ }
@Test
- public void equalsHashCode() {
- AwsSessionCredentials credentials =
- AwsSessionCredentials.create("test", "key", "sessionToken");
-
- AwsSessionCredentials anotherCredentials =
- AwsSessionCredentials.create("test", "key", "sessionToken");
- assertThat(credentials).isEqualTo(anotherCredentials);
- assertThat(credentials.hashCode()).isEqualTo(anotherCredentials.hashCode());
+ public void builderMissingAccessKeyId_ThrowsException() {
+ assertThrows(NullPointerException.class, () -> AwsSessionCredentials.builder()
+ .secretAccessKey(SECRET_ACCESS_KEY)
+ .sessionToken(SESSION_TOKEN)
+ .build());
}
+ @Test
+ public void create_isSuccessful() {
+ AwsSessionCredentials identity = AwsSessionCredentials.create(ACCESS_KEY_ID,
+ SECRET_ACCESS_KEY,
+ SESSION_TOKEN);
+ assertEquals(ACCESS_KEY_ID, identity.accessKeyId());
+ assertEquals(SECRET_ACCESS_KEY, identity.secretAccessKey());
+ assertEquals(SESSION_TOKEN, identity.sessionToken());
+ }
+
+ @Test
+ public void build_isSuccessful() {
+ AwsSessionCredentials identity = AwsSessionCredentials.builder()
+ .accessKeyId(ACCESS_KEY_ID)
+ .secretAccessKey(SECRET_ACCESS_KEY)
+ .sessionToken(SESSION_TOKEN)
+ .build();
+ assertEquals(ACCESS_KEY_ID, identity.accessKeyId());
+ assertEquals(SECRET_ACCESS_KEY, identity.secretAccessKey());
+ assertEquals(SESSION_TOKEN, identity.sessionToken());
+ }
}
diff --git a/core/aws-core/pom.xml b/core/aws-core/pom.xml
index 2501a9197b12..8669fb99527d 100644
--- a/core/aws-core/pom.xml
+++ b/core/aws-core/pom.xml
@@ -22,7 +22,7 @@
software.amazon.awssdk
core
- 2.20.68-SNAPSHOT
+ 2.20.93-SNAPSHOT
aws-core
diff --git a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/interceptor/TraceIdExecutionInterceptor.java b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/interceptor/TraceIdExecutionInterceptor.java
index 00301a89b2f8..f8091c3b4e94 100644
--- a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/interceptor/TraceIdExecutionInterceptor.java
+++ b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/interceptor/TraceIdExecutionInterceptor.java
@@ -17,6 +17,7 @@
import java.util.Optional;
import software.amazon.awssdk.annotations.SdkInternalApi;
+import software.amazon.awssdk.awscore.internal.interceptor.TracingSystemSetting;
import software.amazon.awssdk.core.interceptor.Context;
import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
import software.amazon.awssdk.core.interceptor.ExecutionInterceptor;
@@ -24,13 +25,12 @@
import software.amazon.awssdk.utils.SystemSetting;
/**
- * The {@code TraceIdExecutionInterceptor} copies the {@link #TRACE_ID_ENVIRONMENT_VARIABLE} value to the
- * {@link #TRACE_ID_HEADER} header, assuming we seem to be running in a lambda environment.
+ * The {@code TraceIdExecutionInterceptor} copies the trace details to the {@link #TRACE_ID_HEADER} header, assuming we seem to
+ * be running in a lambda environment.
*/
@SdkInternalApi
public class TraceIdExecutionInterceptor implements ExecutionInterceptor {
private static final String TRACE_ID_HEADER = "X-Amzn-Trace-Id";
- private static final String TRACE_ID_ENVIRONMENT_VARIABLE = "_X_AMZN_TRACE_ID";
private static final String LAMBDA_FUNCTION_NAME_ENVIRONMENT_VARIABLE = "AWS_LAMBDA_FUNCTION_NAME";
@Override
@@ -38,7 +38,7 @@ public SdkHttpRequest modifyHttpRequest(Context.ModifyHttpRequest context, Execu
Optional traceIdHeader = traceIdHeader(context);
if (!traceIdHeader.isPresent()) {
Optional lambdafunctionName = lambdaFunctionNameEnvironmentVariable();
- Optional traceId = traceIdEnvironmentVariable();
+ Optional traceId = traceId();
if (lambdafunctionName.isPresent() && traceId.isPresent()) {
return context.httpRequest().copy(r -> r.putHeader(TRACE_ID_HEADER, traceId.get()));
@@ -52,10 +52,8 @@ private Optional traceIdHeader(Context.ModifyHttpRequest context) {
return context.httpRequest().firstMatchingHeader(TRACE_ID_HEADER);
}
- private Optional traceIdEnvironmentVariable() {
- // CHECKSTYLE:OFF - This is not configured by the customer, so it should not be configurable by system property
- return SystemSetting.getStringValueFromEnvironmentVariable(TRACE_ID_ENVIRONMENT_VARIABLE);
- // CHECKSTYLE:ON
+ private Optional traceId() {
+ return TracingSystemSetting._X_AMZN_TRACE_ID.getStringValue();
}
private Optional lambdaFunctionNameEnvironmentVariable() {
diff --git a/core/aws-core/src/main/java/software/amazon/awssdk/awscore/internal/interceptor/TracingSystemSetting.java b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/internal/interceptor/TracingSystemSetting.java
new file mode 100644
index 000000000000..6f412e9a83a5
--- /dev/null
+++ b/core/aws-core/src/main/java/software/amazon/awssdk/awscore/internal/interceptor/TracingSystemSetting.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 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 software.amazon.awssdk.awscore.internal.interceptor;
+
+import software.amazon.awssdk.annotations.SdkInternalApi;
+import software.amazon.awssdk.utils.SystemSetting;
+
+/**
+ * Tracing specific System Setting.
+ */
+@SdkInternalApi
+public enum TracingSystemSetting implements SystemSetting {
+ // See: https://github.com/aws/aws-xray-sdk-java/issues/251
+ _X_AMZN_TRACE_ID("com.amazonaws.xray.traceHeader", null);
+
+ private final String systemProperty;
+ private final String defaultValue;
+
+ TracingSystemSetting(String systemProperty, String defaultValue) {
+ this.systemProperty = systemProperty;
+ this.defaultValue = defaultValue;
+ }
+
+ @Override
+ public String property() {
+ return systemProperty;
+ }
+
+ @Override
+ public String environmentVariable() {
+ return name();
+ }
+
+ @Override
+ public String defaultValue() {
+ return defaultValue;
+ }
+}
diff --git a/core/aws-core/src/test/java/software/amazon/awssdk/awscore/interceptor/TraceIdExecutionInterceptorTest.java b/core/aws-core/src/test/java/software/amazon/awssdk/awscore/interceptor/TraceIdExecutionInterceptorTest.java
index 408108ab22b6..b3f965a490fc 100644
--- a/core/aws-core/src/test/java/software/amazon/awssdk/awscore/interceptor/TraceIdExecutionInterceptorTest.java
+++ b/core/aws-core/src/test/java/software/amazon/awssdk/awscore/interceptor/TraceIdExecutionInterceptorTest.java
@@ -18,6 +18,7 @@
import static org.assertj.core.api.Assertions.assertThat;
import java.net.URI;
+import java.util.Properties;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import software.amazon.awssdk.core.SdkRequest;
@@ -49,6 +50,32 @@ public void headerAddedWithEnvSettings() {
});
}
+
+ @Test
+ public void headerAddedWithSysPropWhenNoEnvSettings() {
+ EnvironmentVariableHelper.run(env -> {
+ resetRelevantEnvVars(env);
+ env.set("AWS_LAMBDA_FUNCTION_NAME", "foo");
+ Properties props = System.getProperties();
+ props.setProperty("com.amazonaws.xray.traceHeader", "sys-prop");
+ Context.ModifyHttpRequest context = context();
+ assertThat(modifyHttpRequest(context).firstMatchingHeader("X-Amzn-Trace-Id")).hasValue("sys-prop");
+ });
+ }
+
+ @Test
+ public void headerAddedWithEnvVariableValueWhenBothEnvAndSysPropAreSet() {
+ EnvironmentVariableHelper.run(env -> {
+ resetRelevantEnvVars(env);
+ env.set("AWS_LAMBDA_FUNCTION_NAME", "foo");
+ env.set("_X_AMZN_TRACE_ID", "bar");
+ Properties props = System.getProperties();
+ props.setProperty("com.amazonaws.xray.traceHeader", "sys-prop");
+ Context.ModifyHttpRequest context = context();
+ assertThat(modifyHttpRequest(context).firstMatchingHeader("X-Amzn-Trace-Id")).hasValue("sys-prop");
+ });
+ }
+
@Test
public void headerNotAddedIfHeaderAlreadyExists() {
EnvironmentVariableHelper.run(env -> {
diff --git a/core/crt-core/pom.xml b/core/crt-core/pom.xml
index ca16db949f29..9789a22ba291 100644
--- a/core/crt-core/pom.xml
+++ b/core/crt-core/pom.xml
@@ -21,7 +21,7 @@
software.amazon.awssdk
core
- 2.20.68-SNAPSHOT
+ 2.20.93-SNAPSHOT
crt-core
diff --git a/core/endpoints-spi/pom.xml b/core/endpoints-spi/pom.xml
index 7f634278913e..691b6e750104 100644
--- a/core/endpoints-spi/pom.xml
+++ b/core/endpoints-spi/pom.xml
@@ -20,7 +20,7 @@
core
software.amazon.awssdk
- 2.20.68-SNAPSHOT
+ 2.20.93-SNAPSHOT
4.0.0
diff --git a/core/imds/pom.xml b/core/imds/pom.xml
index cc7593a38c21..7184e284e49c 100644
--- a/core/imds/pom.xml
+++ b/core/imds/pom.xml
@@ -20,7 +20,7 @@
core
software.amazon.awssdk
- 2.20.68-SNAPSHOT
+ 2.20.93-SNAPSHOT
4.0.0
imds
diff --git a/core/json-utils/pom.xml b/core/json-utils/pom.xml
index d8cc787f39f6..e40e2197b535 100644
--- a/core/json-utils/pom.xml
+++ b/core/json-utils/pom.xml
@@ -20,7 +20,7 @@
core
software.amazon.awssdk
- 2.20.68-SNAPSHOT
+ 2.20.93-SNAPSHOT
4.0.0
diff --git a/core/metrics-spi/pom.xml b/core/metrics-spi/pom.xml
index 08a64f3a754b..3c8981505c06 100644
--- a/core/metrics-spi/pom.xml
+++ b/core/metrics-spi/pom.xml
@@ -5,7 +5,7 @@
core
software.amazon.awssdk
- 2.20.68-SNAPSHOT
+ 2.20.93-SNAPSHOT
4.0.0
diff --git a/core/pom.xml b/core/pom.xml
index 65368acaf0b2..a1f005cca382 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -21,7 +21,7 @@
aws-sdk-java-pom
software.amazon.awssdk
- 2.20.68-SNAPSHOT
+ 2.20.93-SNAPSHOT
core
diff --git a/core/profiles/pom.xml b/core/profiles/pom.xml
index 5e6b7c1f0bbf..6c24b03da2bd 100644
--- a/core/profiles/pom.xml
+++ b/core/profiles/pom.xml
@@ -22,7 +22,7 @@
software.amazon.awssdk
core
- 2.20.68-SNAPSHOT
+ 2.20.93-SNAPSHOT
profiles
diff --git a/core/profiles/src/test/java/software/amazon/awssdk/profiles/ProfileFileSupplierTest.java b/core/profiles/src/test/java/software/amazon/awssdk/profiles/ProfileFileSupplierTest.java
index 50e4c80ecc58..6fa45ecbe4fb 100644
--- a/core/profiles/src/test/java/software/amazon/awssdk/profiles/ProfileFileSupplierTest.java
+++ b/core/profiles/src/test/java/software/amazon/awssdk/profiles/ProfileFileSupplierTest.java
@@ -433,8 +433,9 @@ void aggregate_duplicateOptionsGivenFixedProfileFirst_preservesPrecedence() {
}
@Test
- void aggregate_duplicateOptionsGivenReloadingProfileFirst_preservesPrecedence() {
- AdjustableClock clock = new AdjustableClock();
+ void aggregate_duplicateOptionsGivenReloadingProfileFirst_preservesPrecedence() throws IOException {
+ Instant startTime = Instant.now();
+ AdjustableClock clock = new AdjustableClock(startTime);
ProfileFile configFile1 = configFile("profile default", Pair.of("aws_access_key_id", "config-key"));
Path credentialsFilePath = generateTestCredentialsFile("defaultAccessKey", "defaultSecretAccessKey");
@@ -452,7 +453,14 @@ void aggregate_duplicateOptionsGivenReloadingProfileFirst_preservesPrecedence()
generateTestCredentialsFile("defaultAccessKey2", "defaultSecretAccessKey2");
- clock.tickForward(Duration.ofMillis(1_000));
+ Duration tick = Duration.ofMillis(1_000);
+
+ // The refresh logic uses the last modified attribute of the profile file to determine if it's changed and should be
+ // reloaded; unfortunately that means that if things happen quickly enough, the last modified time of the first version
+ // of the file, and the new version will be the same. Ensure that there is a change in the last modified time for the
+ // test file.
+ Files.setLastModifiedTime(getTestCredentialsFilePath(), FileTime.from(startTime.plus(tick)));
+ clock.tickForward(tick);
profileFile = supplier.get();
accessKeyId = profileFile.profile("default").get().property("aws_access_key_id").get();
@@ -505,10 +513,10 @@ void get_givenOnLoadAction_callsActionOncePerNewProfileFile() {
assertThat(blockCount.get()).isEqualTo(actualProfilesCount);
}
- private Path generateTestFile(String contents, String filename) {
+ private Path writeTestFile(String contents, Path path) {
try {
Files.createDirectories(testDirectory);
- return Files.write(testDirectory.resolve(filename), contents.getBytes(StandardCharsets.UTF_8));
+ return Files.write(path, contents.getBytes(StandardCharsets.UTF_8));
} catch (IOException e) {
throw new RuntimeException(e);
}
@@ -517,7 +525,11 @@ private Path generateTestFile(String contents, String filename) {
private Path generateTestCredentialsFile(String accessKeyId, String secretAccessKey) {
String contents = String.format("[default]\naws_access_key_id = %s\naws_secret_access_key = %s\n",
accessKeyId, secretAccessKey);
- return generateTestFile(contents, "credentials.txt");
+ return writeTestFile(contents, getTestCredentialsFilePath());
+ }
+
+ private Path getTestCredentialsFilePath() {
+ return testDirectory.resolve("credentials.txt");
}
private Path generateTestConfigFile(Pair... pairs) {
@@ -526,7 +538,7 @@ private Path generateTestConfigFile(Pair... pairs) {
.collect(Collectors.joining(System.lineSeparator()));
String contents = String.format("[default]\n%s", values);
- return generateTestFile(contents, "config.txt");
+ return writeTestFile(contents, testDirectory.resolve("config.txt"));
}
private void updateModificationTime(Path path, Instant instant) {
@@ -597,6 +609,10 @@ private AdjustableClock() {
this.time = Instant.now();
}
+ private AdjustableClock(Instant time) {
+ this.time = time;
+ }
+
@Override
public ZoneId getZone() {
return ZoneOffset.UTC;
diff --git a/core/protocols/aws-cbor-protocol/pom.xml b/core/protocols/aws-cbor-protocol/pom.xml
index 89e619340fb2..b2eda66c5b05 100644
--- a/core/protocols/aws-cbor-protocol/pom.xml
+++ b/core/protocols/aws-cbor-protocol/pom.xml
@@ -20,7 +20,7 @@
protocols
software.amazon.awssdk
- 2.20.68-SNAPSHOT
+ 2.20.93-SNAPSHOT
4.0.0
diff --git a/core/protocols/aws-json-protocol/pom.xml b/core/protocols/aws-json-protocol/pom.xml
index fa63621c7438..35ad37a367df 100644
--- a/core/protocols/aws-json-protocol/pom.xml
+++ b/core/protocols/aws-json-protocol/pom.xml
@@ -20,7 +20,7 @@
protocols
software.amazon.awssdk
- 2.20.68-SNAPSHOT
+ 2.20.93-SNAPSHOT
4.0.0
diff --git a/core/protocols/aws-query-protocol/pom.xml b/core/protocols/aws-query-protocol/pom.xml
index 88b86b99b78e..17752c401a58 100644
--- a/core/protocols/aws-query-protocol/pom.xml
+++ b/core/protocols/aws-query-protocol/pom.xml
@@ -20,7 +20,7 @@
protocols
software.amazon.awssdk
- 2.20.68-SNAPSHOT
+ 2.20.93-SNAPSHOT
4.0.0
diff --git a/core/protocols/aws-xml-protocol/pom.xml b/core/protocols/aws-xml-protocol/pom.xml
index efd5c1b5cb5b..958b33ba6c55 100644
--- a/core/protocols/aws-xml-protocol/pom.xml
+++ b/core/protocols/aws-xml-protocol/pom.xml
@@ -20,7 +20,7 @@
protocols
software.amazon.awssdk
- 2.20.68-SNAPSHOT
+ 2.20.93-SNAPSHOT
4.0.0
diff --git a/core/protocols/pom.xml b/core/protocols/pom.xml
index 71006e19ac0c..2e842618d8d1 100644
--- a/core/protocols/pom.xml
+++ b/core/protocols/pom.xml
@@ -20,7 +20,7 @@
core
software.amazon.awssdk
- 2.20.68-SNAPSHOT
+ 2.20.93-SNAPSHOT
4.0.0
diff --git a/core/protocols/protocol-core/pom.xml b/core/protocols/protocol-core/pom.xml
index e9277738ae7e..54e6811ed212 100644
--- a/core/protocols/protocol-core/pom.xml
+++ b/core/protocols/protocol-core/pom.xml
@@ -20,7 +20,7 @@
protocols
software.amazon.awssdk
- 2.20.68-SNAPSHOT
+ 2.20.93-SNAPSHOT
4.0.0
diff --git a/core/regions/pom.xml b/core/regions/pom.xml
index aebfb9c53004..8b6030ece3ea 100644
--- a/core/regions/pom.xml
+++ b/core/regions/pom.xml
@@ -22,7 +22,7 @@
software.amazon.awssdk
core
- 2.20.68-SNAPSHOT
+ 2.20.93-SNAPSHOT
regions
diff --git a/core/regions/src/main/resources/software/amazon/awssdk/regions/internal/region/endpoints.json b/core/regions/src/main/resources/software/amazon/awssdk/regions/internal/region/endpoints.json
index 6b9002532d26..344555c7d052 100644
--- a/core/regions/src/main/resources/software/amazon/awssdk/regions/internal/region/endpoints.json
+++ b/core/regions/src/main/resources/software/amazon/awssdk/regions/internal/region/endpoints.json
@@ -1076,6 +1076,7 @@
"ap-southeast-1" : { },
"ap-southeast-2" : { },
"ap-southeast-3" : { },
+ "ap-southeast-4" : { },
"ca-central-1" : { },
"eu-central-1" : { },
"eu-central-2" : { },
@@ -2005,6 +2006,9 @@
"variants" : [ {
"hostname" : "athena-fips.us-east-1.amazonaws.com",
"tags" : [ "fips" ]
+ }, {
+ "hostname" : "athena-fips.us-east-1.api.aws",
+ "tags" : [ "dualstack", "fips" ]
}, {
"hostname" : "athena.us-east-1.api.aws",
"tags" : [ "dualstack" ]
@@ -2014,6 +2018,9 @@
"variants" : [ {
"hostname" : "athena-fips.us-east-2.amazonaws.com",
"tags" : [ "fips" ]
+ }, {
+ "hostname" : "athena-fips.us-east-2.api.aws",
+ "tags" : [ "dualstack", "fips" ]
}, {
"hostname" : "athena.us-east-2.api.aws",
"tags" : [ "dualstack" ]
@@ -2023,6 +2030,9 @@
"variants" : [ {
"hostname" : "athena-fips.us-west-1.amazonaws.com",
"tags" : [ "fips" ]
+ }, {
+ "hostname" : "athena-fips.us-west-1.api.aws",
+ "tags" : [ "dualstack", "fips" ]
}, {
"hostname" : "athena.us-west-1.api.aws",
"tags" : [ "dualstack" ]
@@ -2032,6 +2042,9 @@
"variants" : [ {
"hostname" : "athena-fips.us-west-2.amazonaws.com",
"tags" : [ "fips" ]
+ }, {
+ "hostname" : "athena-fips.us-west-2.api.aws",
+ "tags" : [ "dualstack", "fips" ]
}, {
"hostname" : "athena.us-west-2.api.aws",
"tags" : [ "dualstack" ]
@@ -2200,6 +2213,7 @@
"ap-southeast-1" : { },
"ap-southeast-2" : { },
"ap-southeast-3" : { },
+ "ap-southeast-4" : { },
"ca-central-1" : { },
"eu-central-1" : { },
"eu-central-2" : { },
@@ -2824,6 +2838,7 @@
"ap-southeast-1" : { },
"ap-southeast-2" : { },
"ap-southeast-3" : { },
+ "ap-southeast-4" : { },
"ca-central-1" : { },
"eu-central-1" : { },
"eu-central-2" : { },
@@ -3199,6 +3214,7 @@
"ca-central-1" : { },
"eu-central-1" : { },
"eu-north-1" : { },
+ "eu-south-1" : { },
"eu-west-1" : { },
"eu-west-2" : { },
"eu-west-3" : { },
@@ -3259,6 +3275,13 @@
"deprecated" : true,
"hostname" : "cognito-identity-fips.us-east-2.amazonaws.com"
},
+ "fips-us-west-1" : {
+ "credentialScope" : {
+ "region" : "us-west-1"
+ },
+ "deprecated" : true,
+ "hostname" : "cognito-identity-fips.us-west-1.amazonaws.com"
+ },
"fips-us-west-2" : {
"credentialScope" : {
"region" : "us-west-2"
@@ -3280,7 +3303,12 @@
"tags" : [ "fips" ]
} ]
},
- "us-west-1" : { },
+ "us-west-1" : {
+ "variants" : [ {
+ "hostname" : "cognito-identity-fips.us-west-1.amazonaws.com",
+ "tags" : [ "fips" ]
+ } ]
+ },
"us-west-2" : {
"variants" : [ {
"hostname" : "cognito-identity-fips.us-west-2.amazonaws.com",
@@ -4238,6 +4266,7 @@
"ap-southeast-1" : { },
"ap-southeast-2" : { },
"ap-southeast-3" : { },
+ "ap-southeast-4" : { },
"ca-central-1" : {
"variants" : [ {
"hostname" : "datasync-fips.ca-central-1.amazonaws.com",
@@ -4348,12 +4377,24 @@
"ap-south-1" : { },
"ap-southeast-1" : { },
"ap-southeast-2" : { },
- "ca-central-1" : { },
+ "ca-central-1" : {
+ "variants" : [ {
+ "hostname" : "devops-guru-fips.ca-central-1.amazonaws.com",
+ "tags" : [ "fips" ]
+ } ]
+ },
"eu-central-1" : { },
"eu-north-1" : { },
"eu-west-1" : { },
"eu-west-2" : { },
"eu-west-3" : { },
+ "fips-ca-central-1" : {
+ "credentialScope" : {
+ "region" : "ca-central-1"
+ },
+ "deprecated" : true,
+ "hostname" : "devops-guru-fips.ca-central-1.amazonaws.com"
+ },
"fips-us-east-1" : {
"credentialScope" : {
"region" : "us-east-1"
@@ -4368,6 +4409,13 @@
"deprecated" : true,
"hostname" : "devops-guru-fips.us-east-2.amazonaws.com"
},
+ "fips-us-west-1" : {
+ "credentialScope" : {
+ "region" : "us-west-1"
+ },
+ "deprecated" : true,
+ "hostname" : "devops-guru-fips.us-west-1.amazonaws.com"
+ },
"fips-us-west-2" : {
"credentialScope" : {
"region" : "us-west-2"
@@ -4388,7 +4436,12 @@
"tags" : [ "fips" ]
} ]
},
- "us-west-1" : { },
+ "us-west-1" : {
+ "variants" : [ {
+ "hostname" : "devops-guru-fips.us-west-1.amazonaws.com",
+ "tags" : [ "fips" ]
+ } ]
+ },
"us-west-2" : {
"variants" : [ {
"hostname" : "devops-guru-fips.us-west-2.amazonaws.com",
@@ -6576,6 +6629,7 @@
} ]
},
"ap-southeast-3" : { },
+ "ap-southeast-4" : { },
"ca-central-1" : {
"variants" : [ {
"hostname" : "fms-fips.ca-central-1.amazonaws.com",
@@ -6909,6 +6963,7 @@
"ap-southeast-1" : { },
"ap-southeast-2" : { },
"ap-southeast-3" : { },
+ "ap-southeast-4" : { },
"ca-central-1" : {
"variants" : [ {
"hostname" : "fsx-fips.ca-central-1.amazonaws.com",
@@ -7919,11 +7974,6 @@
}
},
"iot" : {
- "defaults" : {
- "credentialScope" : {
- "service" : "execute-api"
- }
- },
"endpoints" : {
"ap-east-1" : { },
"ap-northeast-1" : { },
@@ -7943,37 +7993,22 @@
"eu-west-2" : { },
"eu-west-3" : { },
"fips-ca-central-1" : {
- "credentialScope" : {
- "service" : "execute-api"
- },
"deprecated" : true,
"hostname" : "iot-fips.ca-central-1.amazonaws.com"
},
"fips-us-east-1" : {
- "credentialScope" : {
- "service" : "execute-api"
- },
"deprecated" : true,
"hostname" : "iot-fips.us-east-1.amazonaws.com"
},
"fips-us-east-2" : {
- "credentialScope" : {
- "service" : "execute-api"
- },
"deprecated" : true,
"hostname" : "iot-fips.us-east-2.amazonaws.com"
},
"fips-us-west-1" : {
- "credentialScope" : {
- "service" : "execute-api"
- },
"deprecated" : true,
"hostname" : "iot-fips.us-west-1.amazonaws.com"
},
"fips-us-west-2" : {
- "credentialScope" : {
- "service" : "execute-api"
- },
"deprecated" : true,
"hostname" : "iot-fips.us-west-2.amazonaws.com"
},
@@ -8493,6 +8528,7 @@
"ap-southeast-1" : { },
"ap-southeast-2" : { },
"ap-southeast-3" : { },
+ "ap-southeast-4" : { },
"ca-central-1" : {
"variants" : [ {
"hostname" : "kafka-fips.ca-central-1.amazonaws.com",
@@ -8599,6 +8635,7 @@
"ap-southeast-2" : { },
"ca-central-1" : { },
"eu-west-1" : { },
+ "eu-west-2" : { },
"fips-us-east-1" : {
"credentialScope" : {
"region" : "us-east-1"
@@ -8684,7 +8721,11 @@
"hostname" : "kendra-ranking.ap-southeast-4.api.aws"
},
"ca-central-1" : {
- "hostname" : "kendra-ranking.ca-central-1.api.aws"
+ "hostname" : "kendra-ranking.ca-central-1.api.aws",
+ "variants" : [ {
+ "hostname" : "kendra-ranking-fips.ca-central-1.api.aws",
+ "tags" : [ "fips" ]
+ } ]
},
"eu-central-2" : {
"hostname" : "kendra-ranking.eu-central-2.api.aws"
@@ -8714,16 +8755,28 @@
"hostname" : "kendra-ranking.sa-east-1.api.aws"
},
"us-east-1" : {
- "hostname" : "kendra-ranking.us-east-1.api.aws"
+ "hostname" : "kendra-ranking.us-east-1.api.aws",
+ "variants" : [ {
+ "hostname" : "kendra-ranking-fips.us-east-1.api.aws",
+ "tags" : [ "fips" ]
+ } ]
},
"us-east-2" : {
- "hostname" : "kendra-ranking.us-east-2.api.aws"
+ "hostname" : "kendra-ranking.us-east-2.api.aws",
+ "variants" : [ {
+ "hostname" : "kendra-ranking-fips.us-east-2.api.aws",
+ "tags" : [ "fips" ]
+ } ]
},
"us-west-1" : {
"hostname" : "kendra-ranking.us-west-1.api.aws"
},
"us-west-2" : {
- "hostname" : "kendra-ranking.us-west-2.api.aws"
+ "hostname" : "kendra-ranking.us-west-2.api.aws",
+ "variants" : [ {
+ "hostname" : "kendra-ranking-fips.us-west-2.api.aws",
+ "tags" : [ "fips" ]
+ } ]
}
}
},
@@ -10265,6 +10318,25 @@
"us-west-2" : { }
}
},
+ "mediapackagev2" : {
+ "endpoints" : {
+ "ap-northeast-1" : { },
+ "ap-northeast-2" : { },
+ "ap-south-1" : { },
+ "ap-southeast-1" : { },
+ "ap-southeast-2" : { },
+ "eu-central-1" : { },
+ "eu-north-1" : { },
+ "eu-west-1" : { },
+ "eu-west-2" : { },
+ "eu-west-3" : { },
+ "sa-east-1" : { },
+ "us-east-1" : { },
+ "us-east-2" : { },
+ "us-west-1" : { },
+ "us-west-2" : { }
+ }
+ },
"mediastore" : {
"endpoints" : {
"ap-northeast-1" : { },
@@ -10413,6 +10485,7 @@
"eu-south-2" : { },
"eu-west-1" : { },
"eu-west-2" : { },
+ "eu-west-3" : { },
"me-central-1" : { },
"me-south-1" : { },
"sa-east-1" : { },
@@ -10441,13 +10514,17 @@
"ap-northeast-2" : { },
"ap-northeast-3" : { },
"ap-south-1" : { },
+ "ap-south-2" : { },
"ap-southeast-1" : { },
"ap-southeast-2" : { },
"ap-southeast-3" : { },
+ "ap-southeast-4" : { },
"ca-central-1" : { },
"eu-central-1" : { },
+ "eu-central-2" : { },
"eu-north-1" : { },
"eu-south-1" : { },
+ "eu-south-2" : { },
"eu-west-1" : { },
"eu-west-2" : { },
"eu-west-3" : { },
@@ -10685,13 +10762,17 @@
"ap-northeast-2" : { },
"ap-northeast-3" : { },
"ap-south-1" : { },
+ "ap-south-2" : { },
"ap-southeast-1" : { },
"ap-southeast-2" : { },
"ap-southeast-3" : { },
+ "ap-southeast-4" : { },
"ca-central-1" : { },
"eu-central-1" : { },
+ "eu-central-2" : { },
"eu-north-1" : { },
"eu-south-1" : { },
+ "eu-south-2" : { },
"eu-west-1" : { },
"eu-west-2" : { },
"eu-west-3" : { },
@@ -10975,10 +11056,15 @@
"nimble" : {
"endpoints" : {
"ap-northeast-1" : { },
+ "ap-southeast-1" : { },
"ap-southeast-2" : { },
"ca-central-1" : { },
+ "eu-central-1" : { },
+ "eu-north-1" : { },
+ "eu-west-1" : { },
"eu-west-2" : { },
"us-east-1" : { },
+ "us-east-2" : { },
"us-west-2" : { }
}
},
@@ -13001,6 +13087,7 @@
"ap-southeast-1" : { },
"ap-southeast-2" : { },
"ap-southeast-3" : { },
+ "ap-southeast-4" : { },
"ca-central-1" : { },
"eu-central-1" : { },
"eu-central-2" : { },
@@ -13868,6 +13955,7 @@
"ap-southeast-1" : { },
"ap-southeast-2" : { },
"ap-southeast-3" : { },
+ "ap-southeast-4" : { },
"ca-central-1" : { },
"eu-central-1" : { },
"eu-central-2" : { },
@@ -13937,6 +14025,8 @@
"securitylake" : {
"endpoints" : {
"ap-northeast-1" : { },
+ "ap-northeast-2" : { },
+ "ap-south-1" : { },
"ap-southeast-1" : { },
"ap-southeast-2" : { },
"eu-central-1" : { },
@@ -13945,6 +14035,7 @@
"sa-east-1" : { },
"us-east-1" : { },
"us-east-2" : { },
+ "us-west-1" : { },
"us-west-2" : { }
}
},
@@ -16016,8 +16107,11 @@
},
"transcribestreaming" : {
"endpoints" : {
+ "af-south-1" : { },
"ap-northeast-1" : { },
"ap-northeast-2" : { },
+ "ap-south-1" : { },
+ "ap-southeast-1" : { },
"ap-southeast-2" : { },
"ca-central-1" : { },
"eu-central-1" : { },
@@ -16105,6 +16199,7 @@
"ap-northeast-2" : { },
"ap-northeast-3" : { },
"ap-south-1" : { },
+ "ap-south-2" : { },
"ap-southeast-1" : { },
"ap-southeast-2" : { },
"ap-southeast-3" : { },
@@ -16115,8 +16210,10 @@
} ]
},
"eu-central-1" : { },
+ "eu-central-2" : { },
"eu-north-1" : { },
"eu-south-1" : { },
+ "eu-south-2" : { },
"eu-west-1" : { },
"eu-west-2" : { },
"eu-west-3" : { },
@@ -16243,6 +16340,37 @@
}
}
},
+ "verifiedpermissions" : {
+ "endpoints" : {
+ "af-south-1" : { },
+ "ap-east-1" : { },
+ "ap-northeast-1" : { },
+ "ap-northeast-2" : { },
+ "ap-northeast-3" : { },
+ "ap-south-1" : { },
+ "ap-south-2" : { },
+ "ap-southeast-1" : { },
+ "ap-southeast-2" : { },
+ "ap-southeast-3" : { },
+ "ap-southeast-4" : { },
+ "ca-central-1" : { },
+ "eu-central-1" : { },
+ "eu-central-2" : { },
+ "eu-north-1" : { },
+ "eu-south-1" : { },
+ "eu-south-2" : { },
+ "eu-west-1" : { },
+ "eu-west-2" : { },
+ "eu-west-3" : { },
+ "me-central-1" : { },
+ "me-south-1" : { },
+ "sa-east-1" : { },
+ "us-east-1" : { },
+ "us-east-2" : { },
+ "us-west-1" : { },
+ "us-west-2" : { }
+ }
+ },
"voice-chime" : {
"endpoints" : {
"ap-northeast-1" : { },
@@ -17616,6 +17744,12 @@
"cn-northwest-1" : { }
}
},
+ "airflow" : {
+ "endpoints" : {
+ "cn-north-1" : { },
+ "cn-northwest-1" : { }
+ }
+ },
"api.ecr" : {
"endpoints" : {
"cn-north-1" : {
@@ -18188,11 +18322,6 @@
}
},
"iot" : {
- "defaults" : {
- "credentialScope" : {
- "service" : "execute-api"
- }
- },
"endpoints" : {
"cn-north-1" : { },
"cn-northwest-1" : { }
@@ -19316,6 +19445,9 @@
"variants" : [ {
"hostname" : "athena-fips.us-gov-east-1.amazonaws.com",
"tags" : [ "fips" ]
+ }, {
+ "hostname" : "athena-fips.us-gov-east-1.api.aws",
+ "tags" : [ "dualstack", "fips" ]
}, {
"hostname" : "athena.us-gov-east-1.api.aws",
"tags" : [ "dualstack" ]
@@ -19325,6 +19457,9 @@
"variants" : [ {
"hostname" : "athena-fips.us-gov-west-1.amazonaws.com",
"tags" : [ "fips" ]
+ }, {
+ "hostname" : "athena-fips.us-gov-west-1.api.aws",
+ "tags" : [ "dualstack", "fips" ]
}, {
"hostname" : "athena.us-gov-west-1.api.aws",
"tags" : [ "dualstack" ]
@@ -20976,23 +21111,12 @@
}
},
"iot" : {
- "defaults" : {
- "credentialScope" : {
- "service" : "execute-api"
- }
- },
"endpoints" : {
"fips-us-gov-east-1" : {
- "credentialScope" : {
- "service" : "execute-api"
- },
"deprecated" : true,
"hostname" : "iot-fips.us-gov-east-1.amazonaws.com"
},
"fips-us-gov-west-1" : {
- "credentialScope" : {
- "service" : "execute-api"
- },
"deprecated" : true,
"hostname" : "iot-fips.us-gov-west-1.amazonaws.com"
},
@@ -21467,6 +21591,36 @@
"us-gov-west-1" : { }
}
},
+ "mgn" : {
+ "endpoints" : {
+ "fips-us-gov-east-1" : {
+ "credentialScope" : {
+ "region" : "us-gov-east-1"
+ },
+ "deprecated" : true,
+ "hostname" : "mgn-fips.us-gov-east-1.amazonaws.com"
+ },
+ "fips-us-gov-west-1" : {
+ "credentialScope" : {
+ "region" : "us-gov-west-1"
+ },
+ "deprecated" : true,
+ "hostname" : "mgn-fips.us-gov-west-1.amazonaws.com"
+ },
+ "us-gov-east-1" : {
+ "variants" : [ {
+ "hostname" : "mgn-fips.us-gov-east-1.amazonaws.com",
+ "tags" : [ "fips" ]
+ } ]
+ },
+ "us-gov-west-1" : {
+ "variants" : [ {
+ "hostname" : "mgn-fips.us-gov-west-1.amazonaws.com",
+ "tags" : [ "fips" ]
+ } ]
+ }
+ }
+ },
"models.lex" : {
"defaults" : {
"credentialScope" : {
@@ -22464,6 +22618,12 @@
}
}
},
+ "simspaceweaver" : {
+ "endpoints" : {
+ "us-gov-east-1" : { },
+ "us-gov-west-1" : { }
+ }
+ },
"sms" : {
"endpoints" : {
"fips-us-gov-east-1" : {
@@ -23099,6 +23259,13 @@
},
"workspaces" : {
"endpoints" : {
+ "fips-us-gov-east-1" : {
+ "credentialScope" : {
+ "region" : "us-gov-east-1"
+ },
+ "deprecated" : true,
+ "hostname" : "workspaces-fips.us-gov-east-1.amazonaws.com"
+ },
"fips-us-gov-west-1" : {
"credentialScope" : {
"region" : "us-gov-west-1"
@@ -23106,7 +23273,12 @@
"deprecated" : true,
"hostname" : "workspaces-fips.us-gov-west-1.amazonaws.com"
},
- "us-gov-east-1" : { },
+ "us-gov-east-1" : {
+ "variants" : [ {
+ "hostname" : "workspaces-fips.us-gov-east-1.amazonaws.com",
+ "tags" : [ "fips" ]
+ } ]
+ },
"us-gov-west-1" : {
"variants" : [ {
"hostname" : "workspaces-fips.us-gov-west-1.amazonaws.com",
@@ -23230,6 +23402,12 @@
"us-iso-west-1" : { }
}
},
+ "cloudcontrolapi" : {
+ "endpoints" : {
+ "us-iso-east-1" : { },
+ "us-iso-west-1" : { }
+ }
+ },
"cloudformation" : {
"endpoints" : {
"us-iso-east-1" : { },
@@ -23273,6 +23451,12 @@
"us-iso-west-1" : { }
}
},
+ "dlm" : {
+ "endpoints" : {
+ "us-iso-east-1" : { },
+ "us-iso-west-1" : { }
+ }
+ },
"dms" : {
"defaults" : {
"variants" : [ {
@@ -23601,7 +23785,8 @@
},
"route53resolver" : {
"endpoints" : {
- "us-iso-east-1" : { }
+ "us-iso-east-1" : { },
+ "us-iso-west-1" : { }
}
},
"runtime.sagemaker" : {
@@ -23702,7 +23887,8 @@
},
"tagging" : {
"endpoints" : {
- "us-iso-east-1" : { }
+ "us-iso-east-1" : { },
+ "us-iso-west-1" : { }
}
},
"transcribe" : {
@@ -24194,6 +24380,23 @@
"regionRegex" : "^eu\\-isoe\\-\\w+\\-\\d+$",
"regions" : { },
"services" : { }
+ }, {
+ "defaults" : {
+ "hostname" : "{service}.{region}.{dnsSuffix}",
+ "protocols" : [ "https" ],
+ "signatureVersions" : [ "v4" ],
+ "variants" : [ {
+ "dnsSuffix" : "csp.hci.ic.gov",
+ "hostname" : "{service}-fips.{region}.{dnsSuffix}",
+ "tags" : [ "fips" ]
+ } ]
+ },
+ "dnsSuffix" : "csp.hci.ic.gov",
+ "partition" : "aws-iso-f",
+ "partitionName" : "AWS ISOF",
+ "regionRegex" : "^us\\-isof\\-\\w+\\-\\d+$",
+ "regions" : { },
+ "services" : { }
} ],
"version" : 3
}
\ No newline at end of file
diff --git a/core/sdk-core/pom.xml b/core/sdk-core/pom.xml
index 0ec62964aee1..f44b4399b61a 100644
--- a/core/sdk-core/pom.xml
+++ b/core/sdk-core/pom.xml
@@ -21,7 +21,7 @@
software.amazon.awssdk
core
- 2.20.68-SNAPSHOT
+ 2.20.93-SNAPSHOT
sdk-core
AWS Java SDK :: SDK Core
diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/async/AsyncRequestBody.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/async/AsyncRequestBody.java
index 7a1738f51d97..07dea1568089 100644
--- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/async/AsyncRequestBody.java
+++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/async/AsyncRequestBody.java
@@ -22,37 +22,38 @@
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
+import java.util.Arrays;
import java.util.Optional;
import java.util.concurrent.ExecutorService;
import org.reactivestreams.Publisher;
import org.reactivestreams.Subscriber;
import software.amazon.awssdk.annotations.SdkPublicApi;
-import software.amazon.awssdk.core.internal.async.ByteArrayAsyncRequestBody;
+import software.amazon.awssdk.core.internal.async.ByteBuffersAsyncRequestBody;
import software.amazon.awssdk.core.internal.async.FileAsyncRequestBody;
import software.amazon.awssdk.core.internal.async.InputStreamWithExecutorAsyncRequestBody;
import software.amazon.awssdk.core.internal.util.Mimetype;
import software.amazon.awssdk.utils.BinaryUtils;
/**
- * Interface to allow non-blocking streaming of request content. This follows the reactive streams pattern where
- * this interface is the {@link Publisher} of data (specifically {@link ByteBuffer} chunks) and the HTTP client is the Subscriber
- * of the data (i.e. to write that data on the wire).
+ * Interface to allow non-blocking streaming of request content. This follows the reactive streams pattern where this interface is
+ * the {@link Publisher} of data (specifically {@link ByteBuffer} chunks) and the HTTP client is the Subscriber of the data (i.e.
+ * to write that data on the wire).
*
*
* {@link #subscribe(Subscriber)} should be implemented to tie this publisher to a subscriber. Ideally each call to subscribe
- * should reproduce the content (i.e if you are reading from a file each subscribe call should produce a {@link
- * org.reactivestreams.Subscription} that reads the file fully). This allows for automatic retries to be performed in the SDK. If
- * the content is not reproducible, an exception may be thrown from any subsequent {@link #subscribe(Subscriber)} calls.
+ * should reproduce the content (i.e if you are reading from a file each subscribe call should produce a
+ * {@link org.reactivestreams.Subscription} that reads the file fully). This allows for automatic retries to be performed in the
+ * SDK. If the content is not reproducible, an exception may be thrown from any subsequent {@link #subscribe(Subscriber)} calls.
*
*
*
- * It is important to only send the number of chunks that the subscriber requests to avoid out of memory situations.
- * The subscriber does it's own buffering so it's usually not needed to buffer in the publisher. Additional permits
- * for chunks will be notified via the {@link org.reactivestreams.Subscription#request(long)} method.
+ * It is important to only send the number of chunks that the subscriber requests to avoid out of memory situations. The
+ * subscriber does it's own buffering so it's usually not needed to buffer in the publisher. Additional permits for chunks will be
+ * notified via the {@link org.reactivestreams.Subscription#request(long)} method.
*
*
* @see FileAsyncRequestBody
- * @see ByteArrayAsyncRequestBody
+ * @see ByteBuffersAsyncRequestBody
*/
@SdkPublicApi
public interface AsyncRequestBody extends SdkPublisher {
@@ -70,8 +71,8 @@ default String contentType() {
}
/**
- * Creates an {@link AsyncRequestBody} the produces data from the input ByteBuffer publisher.
- * The data is delivered when the publisher publishes the data.
+ * Creates an {@link AsyncRequestBody} the produces data from the input ByteBuffer publisher. The data is delivered when the
+ * publisher publishes the data.
*
* @param publisher Publisher of source data
* @return Implementation of {@link AsyncRequestBody} that produces data send by the publisher
@@ -124,11 +125,11 @@ static AsyncRequestBody fromFile(File file) {
* @param string The string to provide.
* @param cs The {@link Charset} to use.
* @return Implementation of {@link AsyncRequestBody} that uses the specified string.
- * @see ByteArrayAsyncRequestBody
+ * @see ByteBuffersAsyncRequestBody
*/
static AsyncRequestBody fromString(String string, Charset cs) {
- return new ByteArrayAsyncRequestBody(string.getBytes(cs),
- Mimetype.MIMETYPE_TEXT_PLAIN + "; charset=" + cs.name());
+ return ByteBuffersAsyncRequestBody.from(Mimetype.MIMETYPE_TEXT_PLAIN + "; charset=" + cs.name(),
+ string.getBytes(cs));
}
/**
@@ -143,29 +144,181 @@ static AsyncRequestBody fromString(String string) {
}
/**
- * Creates a {@link AsyncRequestBody} from a byte array. The contents of the byte array are copied so modifications to the
- * original byte array are not reflected in the {@link AsyncRequestBody}.
+ * Creates an {@link AsyncRequestBody} from a byte array. This will copy the contents of the byte array to prevent
+ * modifications to the provided byte array from being reflected in the {@link AsyncRequestBody}.
*
* @param bytes The bytes to send to the service.
* @return AsyncRequestBody instance.
*/
static AsyncRequestBody fromBytes(byte[] bytes) {
- return new ByteArrayAsyncRequestBody(bytes, Mimetype.MIMETYPE_OCTET_STREAM);
+ byte[] clonedBytes = bytes.clone();
+ return ByteBuffersAsyncRequestBody.from(clonedBytes);
}
/**
- * Creates a {@link AsyncRequestBody} from a {@link ByteBuffer}. Buffer contents are copied so any modifications
- * made to the original {@link ByteBuffer} are not reflected in the {@link AsyncRequestBody}.
+ * Creates an {@link AsyncRequestBody} from a byte array without copying the contents of the byte array. This
+ * introduces concurrency risks, allowing: (1) the caller to modify the byte array stored in this {@code AsyncRequestBody}
+ * implementation AND (2) any users of {@link #fromBytesUnsafe(byte[])} to modify the byte array passed into this
+ * {@code AsyncRequestBody} implementation.
+ *
+ * As the method name implies, this is unsafe. Use {@link #fromBytes(byte[])} unless you're sure you know the risks.
+ *
+ * @param bytes The bytes to send to the service.
+ * @return AsyncRequestBody instance.
+ */
+ static AsyncRequestBody fromBytesUnsafe(byte[] bytes) {
+ return ByteBuffersAsyncRequestBody.from(bytes);
+ }
+
+ /**
+ * Creates an {@link AsyncRequestBody} from a {@link ByteBuffer}. This will copy the contents of the {@link ByteBuffer} to
+ * prevent modifications to the provided {@link ByteBuffer} from being reflected in the {@link AsyncRequestBody}.
+ *
+ * NOTE: This method ignores the current read position. Use {@link #fromRemainingByteBuffer(ByteBuffer)} if you need
+ * it to copy only the remaining readable bytes.
*
* @param byteBuffer ByteBuffer to send to the service.
* @return AsyncRequestBody instance.
*/
static AsyncRequestBody fromByteBuffer(ByteBuffer byteBuffer) {
- return fromBytes(BinaryUtils.copyAllBytesFrom(byteBuffer));
+ ByteBuffer immutableCopy = BinaryUtils.immutableCopyOf(byteBuffer);
+ immutableCopy.rewind();
+ return ByteBuffersAsyncRequestBody.of((long) immutableCopy.remaining(), immutableCopy);
+ }
+
+ /**
+ * Creates an {@link AsyncRequestBody} from the remaining readable bytes from a {@link ByteBuffer}. This will copy the
+ * remaining contents of the {@link ByteBuffer} to prevent modifications to the provided {@link ByteBuffer} from being
+ * reflected in the {@link AsyncRequestBody}.
+ *
Unlike {@link #fromByteBuffer(ByteBuffer)}, this method respects the current read position of the buffer and reads
+ * only the remaining bytes.
+ *
+ * @param byteBuffer ByteBuffer to send to the service.
+ * @return AsyncRequestBody instance.
+ */
+ static AsyncRequestBody fromRemainingByteBuffer(ByteBuffer byteBuffer) {
+ ByteBuffer immutableCopy = BinaryUtils.immutableCopyOfRemaining(byteBuffer);
+ return ByteBuffersAsyncRequestBody.of((long) immutableCopy.remaining(), immutableCopy);
+ }
+
+ /**
+ * Creates an {@link AsyncRequestBody} from a {@link ByteBuffer} without copying the contents of the
+ * {@link ByteBuffer}. This introduces concurrency risks, allowing the caller to modify the {@link ByteBuffer} stored in this
+ * {@code AsyncRequestBody} implementation.
+ *
+ * NOTE: This method ignores the current read position. Use {@link #fromRemainingByteBufferUnsafe(ByteBuffer)} if you
+ * need it to copy only the remaining readable bytes.
+ *
+ *
As the method name implies, this is unsafe. Use {@link #fromByteBuffer(ByteBuffer)}} unless you're sure you know the
+ * risks.
+ *
+ * @param byteBuffer ByteBuffer to send to the service.
+ * @return AsyncRequestBody instance.
+ */
+ static AsyncRequestBody fromByteBufferUnsafe(ByteBuffer byteBuffer) {
+ ByteBuffer readOnlyBuffer = byteBuffer.asReadOnlyBuffer();
+ readOnlyBuffer.rewind();
+ return ByteBuffersAsyncRequestBody.of((long) readOnlyBuffer.remaining(), readOnlyBuffer);
+ }
+
+ /**
+ * Creates an {@link AsyncRequestBody} from a {@link ByteBuffer} without copying the contents of the
+ * {@link ByteBuffer}. This introduces concurrency risks, allowing the caller to modify the {@link ByteBuffer} stored in this
+ * {@code AsyncRequestBody} implementation.
+ *
Unlike {@link #fromByteBufferUnsafe(ByteBuffer)}, this method respects the current read position of
+ * the buffer and reads only the remaining bytes.
+ *
+ *
As the method name implies, this is unsafe. Use {@link #fromByteBuffer(ByteBuffer)}} unless you're sure you know the
+ * risks.
+ *
+ * @param byteBuffer ByteBuffer to send to the service.
+ * @return AsyncRequestBody instance.
+ */
+ static AsyncRequestBody fromRemainingByteBufferUnsafe(ByteBuffer byteBuffer) {
+ ByteBuffer readOnlyBuffer = byteBuffer.asReadOnlyBuffer();
+ return ByteBuffersAsyncRequestBody.of((long) readOnlyBuffer.remaining(), readOnlyBuffer);
+ }
+
+ /**
+ * Creates an {@link AsyncRequestBody} from a {@link ByteBuffer} array. This will copy the contents of each {@link ByteBuffer}
+ * to prevent modifications to any provided {@link ByteBuffer} from being reflected in the {@link AsyncRequestBody}.
+ *
+ * NOTE: This method ignores the current read position of each {@link ByteBuffer}. Use
+ * {@link #fromRemainingByteBuffers(ByteBuffer...)} if you need it to copy only the remaining readable bytes.
+ *
+ * @param byteBuffers ByteBuffer array to send to the service.
+ * @return AsyncRequestBody instance.
+ */
+ static AsyncRequestBody fromByteBuffers(ByteBuffer... byteBuffers) {
+ ByteBuffer[] immutableCopy = Arrays.stream(byteBuffers)
+ .map(BinaryUtils::immutableCopyOf)
+ .peek(ByteBuffer::rewind)
+ .toArray(ByteBuffer[]::new);
+ return ByteBuffersAsyncRequestBody.of(immutableCopy);
+ }
+
+ /**
+ * Creates an {@link AsyncRequestBody} from a {@link ByteBuffer} array. This will copy the remaining contents of each
+ * {@link ByteBuffer} to prevent modifications to any provided {@link ByteBuffer} from being reflected in the
+ * {@link AsyncRequestBody}.
+ *
Unlike {@link #fromByteBufferUnsafe(ByteBuffer)},
+ * this method respects the current read position of each buffer and reads only the remaining bytes.
+ *
+ * @param byteBuffers ByteBuffer array to send to the service.
+ * @return AsyncRequestBody instance.
+ */
+ static AsyncRequestBody fromRemainingByteBuffers(ByteBuffer... byteBuffers) {
+ ByteBuffer[] immutableCopy = Arrays.stream(byteBuffers)
+ .map(BinaryUtils::immutableCopyOfRemaining)
+ .peek(ByteBuffer::rewind)
+ .toArray(ByteBuffer[]::new);
+ return ByteBuffersAsyncRequestBody.of(immutableCopy);
+ }
+
+ /**
+ * Creates an {@link AsyncRequestBody} from a {@link ByteBuffer} array without copying the contents of each
+ * {@link ByteBuffer}. This introduces concurrency risks, allowing the caller to modify any {@link ByteBuffer} stored in this
+ * {@code AsyncRequestBody} implementation.
+ *
+ * NOTE: This method ignores the current read position of each {@link ByteBuffer}. Use
+ * {@link #fromRemainingByteBuffers(ByteBuffer...)} if you need it to copy only the remaining readable bytes.
+ *
+ *
As the method name implies, this is unsafe. Use {@link #fromByteBuffers(ByteBuffer...)} unless you're sure you know the
+ * risks.
+ *
+ * @param byteBuffers ByteBuffer array to send to the service.
+ * @return AsyncRequestBody instance.
+ */
+ static AsyncRequestBody fromByteBuffersUnsafe(ByteBuffer... byteBuffers) {
+ ByteBuffer[] readOnlyBuffers = Arrays.stream(byteBuffers)
+ .map(ByteBuffer::asReadOnlyBuffer)
+ .peek(ByteBuffer::rewind)
+ .toArray(ByteBuffer[]::new);
+ return ByteBuffersAsyncRequestBody.of(readOnlyBuffers);
+ }
+
+ /**
+ * Creates an {@link AsyncRequestBody} from a {@link ByteBuffer} array without copying the contents of each
+ * {@link ByteBuffer}. This introduces concurrency risks, allowing the caller to modify any {@link ByteBuffer} stored in this
+ * {@code AsyncRequestBody} implementation.
+ *
Unlike {@link #fromByteBuffersUnsafe(ByteBuffer...)},
+ * this method respects the current read position of each buffer and reads only the remaining bytes.
+ *
+ *
As the method name implies, this is unsafe. Use {@link #fromByteBuffers(ByteBuffer...)} unless you're sure you know the
+ * risks.
+ *
+ * @param byteBuffers ByteBuffer array to send to the service.
+ * @return AsyncRequestBody instance.
+ */
+ static AsyncRequestBody fromRemainingByteBuffersUnsafe(ByteBuffer... byteBuffers) {
+ ByteBuffer[] readOnlyBuffers = Arrays.stream(byteBuffers)
+ .map(ByteBuffer::asReadOnlyBuffer)
+ .toArray(ByteBuffer[]::new);
+ return ByteBuffersAsyncRequestBody.of(readOnlyBuffers);
}
/**
- * Creates a {@link AsyncRequestBody} from a {@link InputStream}.
+ * Creates an {@link AsyncRequestBody} from an {@link InputStream}.
*
*
An {@link ExecutorService} is required in order to perform the blocking data reads, to prevent blocking the
* non-blocking event loop threads owned by the SDK.
@@ -239,7 +392,7 @@ static BlockingOutputStreamAsyncRequestBody forBlockingOutputStream(Long content
}
/**
- * Creates a {@link AsyncRequestBody} with no content.
+ * Creates an {@link AsyncRequestBody} with no content.
*
* @return AsyncRequestBody instance.
*/
diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/client/builder/SdkDefaultClientBuilder.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/client/builder/SdkDefaultClientBuilder.java
index c91ad39ad1a3..18fcc1e52f2e 100644
--- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/client/builder/SdkDefaultClientBuilder.java
+++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/client/builder/SdkDefaultClientBuilder.java
@@ -91,6 +91,7 @@
import software.amazon.awssdk.profiles.ProfileFileSystemSetting;
import software.amazon.awssdk.utils.AttributeMap;
import software.amazon.awssdk.utils.Either;
+import software.amazon.awssdk.utils.ScheduledExecutorUtils;
import software.amazon.awssdk.utils.ThreadFactoryBuilder;
import software.amazon.awssdk.utils.Validate;
@@ -222,6 +223,7 @@ private SdkClientConfiguration setOverrides(SdkClientConfiguration configuration
SdkClientConfiguration.Builder builder = configuration.toBuilder();
+ builder.option(SCHEDULED_EXECUTOR_SERVICE, clientOverrideConfiguration.scheduledExecutorService().orElse(null));
builder.option(EXECUTION_INTERCEPTORS, clientOverrideConfiguration.executionInterceptors());
builder.option(RETRY_POLICY, clientOverrideConfiguration.retryPolicy().orElse(null));
builder.option(ADDITIONAL_HTTP_HEADERS, clientOverrideConfiguration.headers());
@@ -313,7 +315,7 @@ private SdkClientConfiguration finalizeAsyncConfiguration(SdkClientConfiguration
private SdkClientConfiguration finalizeConfiguration(SdkClientConfiguration config) {
RetryPolicy retryPolicy = resolveRetryPolicy(config);
return config.toBuilder()
- .option(SCHEDULED_EXECUTOR_SERVICE, resolveScheduledExecutorService())
+ .option(SCHEDULED_EXECUTOR_SERVICE, resolveScheduledExecutorService(config))
.option(EXECUTION_INTERCEPTORS, resolveExecutionInterceptors(config))
.option(RETRY_POLICY, retryPolicy)
.option(CLIENT_USER_AGENT, resolveClientUserAgent(config, retryPolicy))
@@ -410,9 +412,17 @@ private Executor resolveAsyncFutureCompletionExecutor(SdkClientConfiguration con
* Finalize the internal SDK scheduled executor service that is used for scheduling tasks such
* as async retry attempts and timeout task.
*/
- private ScheduledExecutorService resolveScheduledExecutorService() {
- return Executors.newScheduledThreadPool(5, new ThreadFactoryBuilder()
- .threadNamePrefix("sdk-ScheduledExecutor").build());
+ private ScheduledExecutorService resolveScheduledExecutorService(SdkClientConfiguration config) {
+ Supplier defaultScheduledExecutor = () -> {
+ ScheduledExecutorService executor = Executors.newScheduledThreadPool(5, new ThreadFactoryBuilder()
+ .threadNamePrefix("sdk-ScheduledExecutor").build());
+
+ return executor;
+ };
+
+ return Optional.ofNullable(config.option(SCHEDULED_EXECUTOR_SERVICE))
+ .map(ScheduledExecutorUtils::unmanagedScheduledExecutor)
+ .orElseGet(defaultScheduledExecutor);
}
/**
diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/client/config/ClientOverrideConfiguration.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/client/config/ClientOverrideConfiguration.java
index 4ba034413b90..83cf2317038d 100644
--- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/client/config/ClientOverrideConfiguration.java
+++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/client/config/ClientOverrideConfiguration.java
@@ -23,6 +23,7 @@
import java.util.Map;
import java.util.Optional;
import java.util.TreeMap;
+import java.util.concurrent.ScheduledExecutorService;
import java.util.function.Consumer;
import software.amazon.awssdk.annotations.SdkPublicApi;
import software.amazon.awssdk.annotations.ToBuilderIgnoreField;
@@ -62,6 +63,7 @@ public final class ClientOverrideConfiguration
private final String defaultProfileName;
private final List metricPublishers;
private final ExecutionAttributes executionAttributes;
+ private final ScheduledExecutorService scheduledExecutorService;
/**
* Initialize this configuration. Private to require use of {@link #builder()}.
@@ -77,6 +79,7 @@ private ClientOverrideConfiguration(Builder builder) {
this.defaultProfileName = builder.defaultProfileName();
this.metricPublishers = Collections.unmodifiableList(new ArrayList<>(builder.metricPublishers()));
this.executionAttributes = ExecutionAttributes.unmodifiableExecutionAttributes(builder.executionAttributes());
+ this.scheduledExecutorService = builder.scheduledExecutorService();
}
@Override
@@ -92,7 +95,8 @@ public Builder toBuilder() {
.defaultProfileFile(defaultProfileFile)
.defaultProfileName(defaultProfileName)
.executionAttributes(executionAttributes)
- .metricPublishers(metricPublishers);
+ .metricPublishers(metricPublishers)
+ .scheduledExecutorService(scheduledExecutorService);
}
/**
@@ -141,6 +145,17 @@ public List executionInterceptors() {
return executionInterceptors;
}
+ /**
+ * The optional scheduled executor service that should be used for scheduling tasks such as async retry attempts
+ * and timeout task.
+ *
+ * The SDK will not automatically close the executor when the client is closed. It is the responsibility of the
+ * user to manually close the executor once all clients utilizing it have been closed.
+ */
+ public Optional scheduledExecutorService() {
+ return Optional.ofNullable(scheduledExecutorService);
+ }
+
/**
* The amount of time 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 requests including retries,
@@ -226,6 +241,7 @@ public String toString() {
.add("advancedOptions", advancedOptions)
.add("profileFile", defaultProfileFile)
.add("profileName", defaultProfileName)
+ .add("scheduledExecutorService", scheduledExecutorService)
.build();
}
@@ -338,6 +354,20 @@ default Builder retryPolicy(RetryMode retryMode) {
List executionInterceptors();
+ /**
+ * Configure the scheduled executor service that should be used for scheduling tasks such as async retry attempts
+ * and timeout task.
+ *
+ *
+ * The SDK will not automatically close the executor when the client is closed. It is the responsibility of the
+ * user to manually close the executor once all clients utilizing it have been closed.
+ *
+ * @see ClientOverrideConfiguration#scheduledExecutorService()
+ */
+ Builder scheduledExecutorService(ScheduledExecutorService scheduledExecutorService);
+
+ ScheduledExecutorService scheduledExecutorService();
+
/**
* Configure an advanced override option. These values are used very rarely, and the majority of SDK customers can ignore
* them.
@@ -499,6 +529,7 @@ private static final class DefaultClientOverrideConfigurationBuilder implements
private String defaultProfileName;
private List metricPublishers = new ArrayList<>();
private ExecutionAttributes.Builder executionAttributes = ExecutionAttributes.builder();
+ private ScheduledExecutorService scheduledExecutorService;
@Override
public Builder headers(Map> headers) {
@@ -561,6 +592,18 @@ public List executionInterceptors() {
return Collections.unmodifiableList(executionInterceptors);
}
+ @Override
+ public ScheduledExecutorService scheduledExecutorService()
+ {
+ return scheduledExecutorService;
+ }
+
+ @Override
+ public Builder scheduledExecutorService(ScheduledExecutorService scheduledExecutorService) {
+ this.scheduledExecutorService = scheduledExecutorService;
+ return this;
+ }
+
@Override
public Builder putAdvancedOption(SdkAdvancedClientOption option, T value) {
this.advancedOptions.put(option, value);
diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/interceptor/SdkExecutionAttribute.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/interceptor/SdkExecutionAttribute.java
index 94f78e64a0df..6e71448dc98f 100644
--- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/interceptor/SdkExecutionAttribute.java
+++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/interceptor/SdkExecutionAttribute.java
@@ -67,7 +67,8 @@ public class SdkExecutionAttribute {
public static final ExecutionAttribute ENDPOINT_OVERRIDDEN = new ExecutionAttribute<>("EndpointOverridden");
/**
- * This is the endpointOverride (if {@link #ENDPOINT_OVERRIDDEN} is true), otherwise null.
+ * This is the endpointOverride (if {@link #ENDPOINT_OVERRIDDEN} is true), otherwise the endpoint generated from regional
+ * metadata.
*/
public static final ExecutionAttribute CLIENT_ENDPOINT = new ExecutionAttribute<>("EndpointOverride");
diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/async/ByteArrayAsyncRequestBody.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/async/ByteArrayAsyncRequestBody.java
deleted file mode 100644
index 29205479b798..000000000000
--- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/async/ByteArrayAsyncRequestBody.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright 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 software.amazon.awssdk.core.internal.async;
-
-import java.nio.ByteBuffer;
-import java.util.Optional;
-import org.reactivestreams.Subscriber;
-import org.reactivestreams.Subscription;
-import software.amazon.awssdk.annotations.SdkInternalApi;
-import software.amazon.awssdk.core.async.AsyncRequestBody;
-import software.amazon.awssdk.utils.Logger;
-
-/**
- * An implementation of {@link AsyncRequestBody} for providing data from memory. This is created using static
- * methods on {@link AsyncRequestBody}
- *
- * @see AsyncRequestBody#fromBytes(byte[])
- * @see AsyncRequestBody#fromByteBuffer(ByteBuffer)
- * @see AsyncRequestBody#fromString(String)
- */
-@SdkInternalApi
-public final class ByteArrayAsyncRequestBody implements AsyncRequestBody {
- private static final Logger log = Logger.loggerFor(ByteArrayAsyncRequestBody.class);
-
- private final byte[] bytes;
-
- private final String mimetype;
-
- public ByteArrayAsyncRequestBody(byte[] bytes, String mimetype) {
- this.bytes = bytes.clone();
- this.mimetype = mimetype;
- }
-
- @Override
- public Optional contentLength() {
- return Optional.of((long) bytes.length);
- }
-
- @Override
- public String contentType() {
- return mimetype;
- }
-
- @Override
- public void subscribe(Subscriber super ByteBuffer> s) {
- // As per rule 1.9 we must throw NullPointerException if the subscriber parameter is null
- if (s == null) {
- throw new NullPointerException("Subscription MUST NOT be null.");
- }
-
- // As per 2.13, this method must return normally (i.e. not throw).
- try {
- s.onSubscribe(
- new Subscription() {
- private boolean done = false;
-
- @Override
- public void request(long n) {
- if (done) {
- return;
- }
- if (n > 0) {
- done = true;
- s.onNext(ByteBuffer.wrap(bytes));
- s.onComplete();
- } else {
- s.onError(new IllegalArgumentException("§3.9: non-positive requests are not allowed!"));
- }
- }
-
- @Override
- public void cancel() {
- synchronized (this) {
- if (!done) {
- done = true;
- }
- }
- }
- }
- );
- } catch (Throwable ex) {
- log.error(() -> s + " violated the Reactive Streams rule 2.13 by throwing an exception from onSubscribe.", ex);
- }
- }
-}
diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/async/ByteBuffersAsyncRequestBody.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/async/ByteBuffersAsyncRequestBody.java
new file mode 100644
index 000000000000..e7e9d00dd0e5
--- /dev/null
+++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/async/ByteBuffersAsyncRequestBody.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright 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 software.amazon.awssdk.core.internal.async;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.Optional;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.reactivestreams.Subscriber;
+import org.reactivestreams.Subscription;
+import software.amazon.awssdk.annotations.SdkInternalApi;
+import software.amazon.awssdk.core.async.AsyncRequestBody;
+import software.amazon.awssdk.core.internal.util.Mimetype;
+import software.amazon.awssdk.utils.BinaryUtils;
+import software.amazon.awssdk.utils.Logger;
+
+/**
+ * An implementation of {@link AsyncRequestBody} for providing data from the supplied {@link ByteBuffer} array. This is created
+ * using static methods on {@link AsyncRequestBody}
+ *
+ * @see AsyncRequestBody#fromBytes(byte[])
+ * @see AsyncRequestBody#fromBytesUnsafe(byte[])
+ * @see AsyncRequestBody#fromByteBuffer(ByteBuffer)
+ * @see AsyncRequestBody#fromByteBufferUnsafe(ByteBuffer)
+ * @see AsyncRequestBody#fromByteBuffers(ByteBuffer...)
+ * @see AsyncRequestBody#fromByteBuffersUnsafe(ByteBuffer...)
+ * @see AsyncRequestBody#fromString(String)
+ */
+@SdkInternalApi
+public final class ByteBuffersAsyncRequestBody implements AsyncRequestBody {
+ private static final Logger log = Logger.loggerFor(ByteBuffersAsyncRequestBody.class);
+
+ private final String mimetype;
+ private final Long length;
+ private final ByteBuffer[] buffers;
+
+ private ByteBuffersAsyncRequestBody(String mimetype, Long length, ByteBuffer... buffers) {
+ this.mimetype = mimetype;
+ this.length = length;
+ this.buffers = buffers;
+ }
+
+ @Override
+ public Optional contentLength() {
+ return Optional.ofNullable(length);
+ }
+
+ @Override
+ public String contentType() {
+ return mimetype;
+ }
+
+ @Override
+ public void subscribe(Subscriber super ByteBuffer> s) {
+ // As per rule 1.9 we must throw NullPointerException if the subscriber parameter is null
+ if (s == null) {
+ throw new NullPointerException("Subscription MUST NOT be null.");
+ }
+
+ // As per 2.13, this method must return normally (i.e. not throw).
+ try {
+ s.onSubscribe(
+ new Subscription() {
+ private final AtomicInteger index = new AtomicInteger(0);
+ private final AtomicBoolean completed = new AtomicBoolean(false);
+
+ @Override
+ public void request(long n) {
+ if (completed.get()) {
+ return;
+ }
+
+ if (n > 0) {
+ int i = index.getAndIncrement();
+
+ if (i >= buffers.length) {
+ return;
+ }
+
+ long remaining = n;
+
+ do {
+ ByteBuffer buffer = buffers[i];
+
+ // Pending discussions on https://github.com/aws/aws-sdk-java-v2/issues/3928
+ if (buffer.isDirect()) {
+ buffer = BinaryUtils.toNonDirectBuffer(buffer);
+ }
+
+ s.onNext(buffer.asReadOnlyBuffer());
+ remaining--;
+ } while (remaining > 0 && (i = index.getAndIncrement()) < buffers.length);
+
+ if (i >= buffers.length - 1 && completed.compareAndSet(false, true)) {
+ s.onComplete();
+ }
+ } else {
+ s.onError(new IllegalArgumentException("§3.9: non-positive requests are not allowed!"));
+ }
+ }
+
+ @Override
+ public void cancel() {
+ completed.set(true);
+ }
+ }
+ );
+ } catch (Throwable ex) {
+ log.error(() -> s + " violated the Reactive Streams rule 2.13 by throwing an exception from onSubscribe.", ex);
+ }
+ }
+
+ public static ByteBuffersAsyncRequestBody of(ByteBuffer... buffers) {
+ long length = Arrays.stream(buffers)
+ .mapToLong(ByteBuffer::remaining)
+ .sum();
+ return new ByteBuffersAsyncRequestBody(Mimetype.MIMETYPE_OCTET_STREAM, length, buffers);
+ }
+
+ public static ByteBuffersAsyncRequestBody of(Long length, ByteBuffer... buffers) {
+ return new ByteBuffersAsyncRequestBody(Mimetype.MIMETYPE_OCTET_STREAM, length, buffers);
+ }
+
+ public static ByteBuffersAsyncRequestBody of(String mimetype, ByteBuffer... buffers) {
+ long length = Arrays.stream(buffers)
+ .mapToLong(ByteBuffer::remaining)
+ .sum();
+ return new ByteBuffersAsyncRequestBody(mimetype, length, buffers);
+ }
+
+ public static ByteBuffersAsyncRequestBody of(String mimetype, Long length, ByteBuffer... buffers) {
+ return new ByteBuffersAsyncRequestBody(mimetype, length, buffers);
+ }
+
+ public static ByteBuffersAsyncRequestBody from(byte[] bytes) {
+ return new ByteBuffersAsyncRequestBody(Mimetype.MIMETYPE_OCTET_STREAM, (long) bytes.length,
+ ByteBuffer.wrap(bytes));
+ }
+
+ public static ByteBuffersAsyncRequestBody from(String mimetype, byte[] bytes) {
+ return new ByteBuffersAsyncRequestBody(mimetype, (long) bytes.length, ByteBuffer.wrap(bytes));
+ }
+}
diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/async/ChunkBuffer.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/async/ChunkBuffer.java
index 8fd7f0260b76..93d6d09578a6 100644
--- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/async/ChunkBuffer.java
+++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/async/ChunkBuffer.java
@@ -22,6 +22,7 @@
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
import software.amazon.awssdk.annotations.SdkInternalApi;
+import software.amazon.awssdk.utils.BinaryUtils;
import software.amazon.awssdk.utils.Validate;
import software.amazon.awssdk.utils.builder.SdkBuilder;
@@ -58,10 +59,11 @@ public synchronized Iterable bufferAndCreateChunks(ByteBuffer buffer
int availableToRead = bufferSize - bufferedBytes;
int bytesToMove = Math.min(availableToRead, currentBytesRead - startPosition);
+ byte[] bytes = BinaryUtils.copyAllBytesFrom(buffer);
if (bufferedBytes == 0) {
- currentBuffer.put(buffer.array(), startPosition, bytesToMove);
+ currentBuffer.put(bytes, startPosition, bytesToMove);
} else {
- currentBuffer.put(buffer.array(), 0, bytesToMove);
+ currentBuffer.put(bytes, 0, bytesToMove);
}
startPosition = startPosition + bytesToMove;
diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/AfterTransmissionExecutionInterceptorsStage.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/AfterTransmissionExecutionInterceptorsStage.java
index 7521219a5030..a7cada02b06c 100644
--- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/AfterTransmissionExecutionInterceptorsStage.java
+++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/AfterTransmissionExecutionInterceptorsStage.java
@@ -31,7 +31,7 @@ public class AfterTransmissionExecutionInterceptorsStage
@Override
public Pair execute(Pair input,
RequestExecutionContext context) throws Exception {
- InterruptMonitor.checkInterrupted();
+ InterruptMonitor.checkInterrupted(input.right());
// Update interceptor context
InterceptorContext interceptorContext =
context.executionContext().interceptorContext().copy(b -> b.httpResponse(input.right())
diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/ApiCallAttemptMetricCollectionStage.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/ApiCallAttemptMetricCollectionStage.java
index 329b302ccba2..bfd10e742469 100644
--- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/ApiCallAttemptMetricCollectionStage.java
+++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/ApiCallAttemptMetricCollectionStage.java
@@ -25,6 +25,7 @@
import software.amazon.awssdk.core.internal.http.pipeline.RequestPipeline;
import software.amazon.awssdk.core.internal.http.pipeline.RequestToResponsePipeline;
import software.amazon.awssdk.core.internal.http.pipeline.stages.utils.RetryableStageHelper;
+import software.amazon.awssdk.core.internal.metrics.SdkErrorType;
import software.amazon.awssdk.core.metrics.CoreMetric;
import software.amazon.awssdk.http.SdkHttpFullRequest;
import software.amazon.awssdk.metrics.MetricCollector;
@@ -47,11 +48,18 @@ public Response execute(SdkHttpFullRequest input, RequestExecutionConte
context.attemptMetricCollector(apiCallAttemptMetrics);
reportBackoffDelay(context);
- Response response = wrapped.execute(input, context);
+ try {
+ Response response = wrapped.execute(input, context);
+ collectHttpMetrics(apiCallAttemptMetrics, response.httpResponse());
- collectHttpMetrics(apiCallAttemptMetrics, response.httpResponse());
-
- return response;
+ if (!Boolean.TRUE.equals(response.isSuccess()) && response.exception() != null) {
+ reportErrorType(context, response.exception());
+ }
+ return response;
+ } catch (Exception e) {
+ reportErrorType(context, e);
+ throw e;
+ }
}
private void reportBackoffDelay(RequestExecutionContext context) {
@@ -60,4 +68,8 @@ private void reportBackoffDelay(RequestExecutionContext context) {
context.attemptMetricCollector().reportMetric(CoreMetric.BACKOFF_DELAY_DURATION, lastBackoffDelay);
}
}
+
+ private void reportErrorType(RequestExecutionContext context, Exception e) {
+ context.attemptMetricCollector().reportMetric(CoreMetric.ERROR_TYPE, SdkErrorType.fromException(e).toString());
+ }
}
diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/AsyncApiCallAttemptMetricCollectionStage.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/AsyncApiCallAttemptMetricCollectionStage.java
index c576da162fba..d4777674452c 100644
--- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/AsyncApiCallAttemptMetricCollectionStage.java
+++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/AsyncApiCallAttemptMetricCollectionStage.java
@@ -25,6 +25,7 @@
import software.amazon.awssdk.core.internal.http.RequestExecutionContext;
import software.amazon.awssdk.core.internal.http.pipeline.RequestPipeline;
import software.amazon.awssdk.core.internal.http.pipeline.stages.utils.RetryableStageHelper;
+import software.amazon.awssdk.core.internal.metrics.SdkErrorType;
import software.amazon.awssdk.core.metrics.CoreMetric;
import software.amazon.awssdk.http.SdkHttpFullRequest;
import software.amazon.awssdk.metrics.MetricCollector;
@@ -57,6 +58,12 @@ public CompletableFuture> execute(SdkHttpFullRequest input,
if (t == null) {
collectHttpMetrics(apiCallAttemptMetrics, r.httpResponse());
}
+
+ if (t != null) {
+ reportErrorType(context, t.getCause());
+ } else if (!Boolean.TRUE.equals(r.isSuccess()) && r.exception() != null) {
+ reportErrorType(context, r.exception());
+ }
});
CompletableFutureUtils.forwardExceptionTo(metricsCollectedFuture, executeFuture);
@@ -69,4 +76,8 @@ private void reportBackoffDelay(RequestExecutionContext context) {
context.attemptMetricCollector().reportMetric(CoreMetric.BACKOFF_DELAY_DURATION, lastBackoffDelay);
}
}
+
+ private void reportErrorType(RequestExecutionContext context, Throwable t) {
+ context.attemptMetricCollector().reportMetric(CoreMetric.ERROR_TYPE, SdkErrorType.fromException(t).toString());
+ }
}
diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/MakeAsyncHttpRequestStage.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/MakeAsyncHttpRequestStage.java
index 063d2a572e96..a40fdeb90b72 100644
--- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/MakeAsyncHttpRequestStage.java
+++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/MakeAsyncHttpRequestStage.java
@@ -156,15 +156,29 @@ private CompletableFuture> executeHttpRequest(SdkHttpFullReque
}
});
- // Offload the completion of the future returned from this stage onto
- // the future completion executor
- responseHandlerFuture.whenCompleteAsync((r, t) -> {
- if (t == null) {
- responseFuture.complete(r);
- } else {
- responseFuture.completeExceptionally(t);
+ // Attempt to offload the completion of the future returned from this
+ // stage onto the future completion executor
+ CompletableFuture asyncComplete =
+ responseHandlerFuture.handleAsync((r, t) -> {
+ completeResponseFuture(responseFuture, r, t);
+ return null;
+ },
+ futureCompletionExecutor);
+
+ // It's possible the async execution above fails. If so, log a warning,
+ // and just complete it synchronously.
+ asyncComplete.whenComplete((ignored, asyncCompleteError) -> {
+ if (asyncCompleteError != null) {
+ log.debug(() -> String.format("Could not complete the service call future on the provided "
+ + "FUTURE_COMPLETION_EXECUTOR. The future will be completed synchronously by thread"
+ + " %s. This may be an indication that the executor is being overwhelmed by too"
+ + " many requests, and it may negatively affect performance. Consider changing "
+ + "the configuration of the executor to accommodate the load through the client.",
+ Thread.currentThread().getName()),
+ asyncCompleteError);
+ responseHandlerFuture.whenComplete((r, t) -> completeResponseFuture(responseFuture, r, t));
}
- }, futureCompletionExecutor);
+ });
return responseFuture;
}
@@ -219,6 +233,14 @@ private TimeoutTracker setupAttemptTimer(CompletableFuture> ex
timeoutMillis);
}
+ private void completeResponseFuture(CompletableFuture> responseFuture, Response r, Throwable t) {
+ if (t == null) {
+ responseFuture.complete(r);
+ } else {
+ responseFuture.completeExceptionally(t);
+ }
+ }
+
/**
* When an operation has a streaming input, the customer must supply an {@link AsyncRequestBody} to
* provide the request content in a non-blocking manner. This adapts that interface to the
diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/interceptor/HttpChecksumInHeaderInterceptor.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/interceptor/HttpChecksumInHeaderInterceptor.java
index 0ddf70959cae..f3c92a254bec 100644
--- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/interceptor/HttpChecksumInHeaderInterceptor.java
+++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/interceptor/HttpChecksumInHeaderInterceptor.java
@@ -15,7 +15,6 @@
package software.amazon.awssdk.core.internal.interceptor;
-import static software.amazon.awssdk.core.HttpChecksumConstant.HTTP_CHECKSUM_VALUE;
import static software.amazon.awssdk.core.HttpChecksumConstant.SIGNING_METHOD;
import static software.amazon.awssdk.core.internal.util.HttpChecksumResolver.getResolvedChecksumSpecs;
@@ -23,7 +22,6 @@
import java.io.UncheckedIOException;
import java.util.Optional;
import software.amazon.awssdk.annotations.SdkInternalApi;
-import software.amazon.awssdk.core.checksums.Algorithm;
import software.amazon.awssdk.core.checksums.ChecksumSpecs;
import software.amazon.awssdk.core.interceptor.Context;
import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
@@ -47,49 +45,27 @@
@SdkInternalApi
public class HttpChecksumInHeaderInterceptor implements ExecutionInterceptor {
- @Override
- public void afterMarshalling(Context.AfterMarshalling context, ExecutionAttributes executionAttributes) {
- ChecksumSpecs headerChecksumSpecs = HttpChecksumUtils.checksumSpecWithRequestAlgorithm(executionAttributes).orElse(null);
-
- if (shouldSkipHttpChecksumInHeader(context, executionAttributes, headerChecksumSpecs)) {
- return;
- }
- Optional syncContent = context.requestBody();
- syncContent.ifPresent(
- requestBody -> saveContentChecksum(requestBody, executionAttributes, headerChecksumSpecs.algorithm()));
- }
-
- @Override
- public SdkHttpRequest modifyHttpRequest(Context.ModifyHttpRequest context, ExecutionAttributes executionAttributes) {
- ChecksumSpecs checksumSpecs = getResolvedChecksumSpecs(executionAttributes);
-
- if (shouldSkipHttpChecksumInHeader(context, executionAttributes, checksumSpecs)) {
- return context.httpRequest();
- }
-
- String httpChecksumValue = executionAttributes.getAttribute(HTTP_CHECKSUM_VALUE);
- if (httpChecksumValue != null) {
- return context.httpRequest().copy(r -> r.putHeader(checksumSpecs.headerName(), httpChecksumValue));
- }
- return context.httpRequest();
-
- }
-
/**
- * Calculates the checksumSpecs of the provided request (and base64 encodes it), storing the result in
- * executionAttribute "HttpChecksumValue".
+ * Calculates the checksum of the provided request (and base64 encodes it), and adds the header to the request.
*
* Note: This assumes that the content stream provider can create multiple new streams. If it only supports one (e.g. with
* an input stream that doesn't support mark/reset), we could consider buffering the content in memory here and updating the
* request body to use that buffered content. We obviously don't want to do that for giant streams, so we haven't opted to do
* that yet.
*/
- private static void saveContentChecksum(RequestBody requestBody, ExecutionAttributes executionAttributes,
- Algorithm algorithm) {
+ @Override
+ public SdkHttpRequest modifyHttpRequest(Context.ModifyHttpRequest context, ExecutionAttributes executionAttributes) {
+ ChecksumSpecs checksumSpecs = getResolvedChecksumSpecs(executionAttributes);
+ Optional syncContent = context.requestBody();
+
+ if (shouldSkipHttpChecksumInHeader(context, executionAttributes, checksumSpecs) || !syncContent.isPresent()) {
+ return context.httpRequest();
+ }
+
try {
String payloadChecksum = BinaryUtils.toBase64(HttpChecksumUtils.computeChecksum(
- requestBody.contentStreamProvider().newStream(), algorithm));
- executionAttributes.putAttribute(HTTP_CHECKSUM_VALUE, payloadChecksum);
+ syncContent.get().contentStreamProvider().newStream(), checksumSpecs.algorithm()));
+ return context.httpRequest().copy(r -> r.putHeader(checksumSpecs.headerName(), payloadChecksum));
} catch (IOException e) {
throw new UncheckedIOException(e);
}
diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/interceptor/HttpChecksumRequiredInterceptor.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/interceptor/HttpChecksumRequiredInterceptor.java
index c98cde397f0c..9729cd2076d7 100644
--- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/interceptor/HttpChecksumRequiredInterceptor.java
+++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/interceptor/HttpChecksumRequiredInterceptor.java
@@ -21,7 +21,6 @@
import software.amazon.awssdk.annotations.SdkInternalApi;
import software.amazon.awssdk.core.async.AsyncRequestBody;
import software.amazon.awssdk.core.interceptor.Context;
-import software.amazon.awssdk.core.interceptor.ExecutionAttribute;
import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
import software.amazon.awssdk.core.interceptor.ExecutionInterceptor;
import software.amazon.awssdk.core.interceptor.SdkInternalExecutionAttribute;
@@ -41,10 +40,17 @@
*/
@SdkInternalApi
public class HttpChecksumRequiredInterceptor implements ExecutionInterceptor {
- private static final ExecutionAttribute CONTENT_MD5_VALUE = new ExecutionAttribute<>("ContentMd5");
+ /**
+ * Calculates the MD5 checksum of the provided request (and base64 encodes it), and adds the header to the request.
+ *
+ * Note: This assumes that the content stream provider can create multiple new streams. If it only supports one (e.g. with
+ * an input stream that doesn't support mark/reset), we could consider buffering the content in memory here and updating the
+ * request body to use that buffered content. We obviously don't want to do that for giant streams, so we haven't opted to do
+ * that yet.
+ */
@Override
- public void afterMarshalling(Context.AfterMarshalling context, ExecutionAttributes executionAttributes) {
+ public SdkHttpRequest modifyHttpRequest(Context.ModifyHttpRequest context, ExecutionAttributes executionAttributes) {
boolean isHttpChecksumRequired = isHttpChecksumRequired(executionAttributes);
boolean requestAlreadyHasMd5 = context.httpRequest().firstMatchingHeader(Header.CONTENT_MD5).isPresent();
@@ -52,7 +58,7 @@ public void afterMarshalling(Context.AfterMarshalling context, ExecutionAttribut
Optional asyncContent = context.asyncRequestBody();
if (!isHttpChecksumRequired || requestAlreadyHasMd5) {
- return;
+ return context.httpRequest();
}
if (asyncContent.isPresent()) {
@@ -60,14 +66,13 @@ public void afterMarshalling(Context.AfterMarshalling context, ExecutionAttribut
+ "for non-blocking content.");
}
- syncContent.ifPresent(requestBody -> saveContentMd5(requestBody, executionAttributes));
- }
-
- @Override
- public SdkHttpRequest modifyHttpRequest(Context.ModifyHttpRequest context, ExecutionAttributes executionAttributes) {
- String contentMd5 = executionAttributes.getAttribute(CONTENT_MD5_VALUE);
- if (contentMd5 != null) {
- return context.httpRequest().copy(r -> r.putHeader(Header.CONTENT_MD5, contentMd5));
+ if (syncContent.isPresent()) {
+ try {
+ String payloadMd5 = Md5Utils.md5AsBase64(syncContent.get().contentStreamProvider().newStream());
+ return context.httpRequest().copy(r -> r.putHeader(Header.CONTENT_MD5, payloadMd5));
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
}
return context.httpRequest();
}
@@ -76,22 +81,4 @@ private boolean isHttpChecksumRequired(ExecutionAttributes executionAttributes)
return executionAttributes.getAttribute(SdkInternalExecutionAttribute.HTTP_CHECKSUM_REQUIRED) != null
|| HttpChecksumUtils.isMd5ChecksumRequired(executionAttributes);
}
-
- /**
- * Calculates the MD5 checksum of the provided request (and base64 encodes it), storing the result in
- * {@link #CONTENT_MD5_VALUE}.
- *
- * Note: This assumes that the content stream provider can create multiple new streams. If it only supports one (e.g. with
- * an input stream that doesn't support mark/reset), we could consider buffering the content in memory here and updating the
- * request body to use that buffered content. We obviously don't want to do that for giant streams, so we haven't opted to do
- * that yet.
- */
- private void saveContentMd5(RequestBody requestBody, ExecutionAttributes executionAttributes) {
- try {
- String payloadMd5 = Md5Utils.md5AsBase64(requestBody.contentStreamProvider().newStream());
- executionAttributes.putAttribute(CONTENT_MD5_VALUE, payloadMd5);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
}
diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/metrics/SdkErrorType.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/metrics/SdkErrorType.java
new file mode 100644
index 000000000000..cbc293cdb73b
--- /dev/null
+++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/metrics/SdkErrorType.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 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 software.amazon.awssdk.core.internal.metrics;
+
+import java.io.IOException;
+import software.amazon.awssdk.annotations.SdkInternalApi;
+import software.amazon.awssdk.core.exception.ApiCallAttemptTimeoutException;
+import software.amazon.awssdk.core.exception.ApiCallTimeoutException;
+import software.amazon.awssdk.core.exception.SdkException;
+import software.amazon.awssdk.core.exception.SdkServiceException;
+import software.amazon.awssdk.core.retry.RetryUtils;
+
+/**
+ * General categories of errors that can be encountered when making an API call attempt.
+ *
+ * This class is NOT intended to fully distinguish the details of every error that is possible to encounter when making
+ * an API call attempt; for example, it is not a replacement for detailed logs. Instead, the categories are intentionally
+ * broad to make it easy at-a-glance what is causing issues with requests, and to help direct further investigation.
+ */
+@SdkInternalApi
+public enum SdkErrorType {
+ /**
+ * The service responded with a throttling error.
+ */
+ THROTTLING("Throttling"),
+
+ /**
+ * The service responded with an error other than {@link #THROTTLING}.
+ */
+ SERVER_ERROR("ServerError"),
+
+ /**
+ * A clientside timeout occurred, either an attempt level timeout, or API call level.
+ */
+ CONFIGURED_TIMEOUT("ConfiguredTimeout"),
+
+ /**
+ * An I/O error.
+ */
+ IO("IO"),
+
+ /**
+ * Catch-all type for errors that don't fit into the other categories.
+ */
+ OTHER("Other"),
+
+ ;
+
+ private final String name;
+
+ SdkErrorType(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+
+ public static SdkErrorType fromException(Throwable e) {
+ if (e instanceof IOException) {
+ return IO;
+ }
+
+ if (e instanceof SdkException) {
+ SdkException sdkError = (SdkException) e;
+ if (sdkError instanceof ApiCallTimeoutException || sdkError instanceof ApiCallAttemptTimeoutException) {
+ return CONFIGURED_TIMEOUT;
+ }
+
+ if (RetryUtils.isThrottlingException(sdkError)) {
+ return THROTTLING;
+ }
+
+ if (e instanceof SdkServiceException) {
+ return SERVER_ERROR;
+ }
+ }
+
+ return OTHER;
+ }
+}
diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/util/ClassLoaderHelper.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/util/ClassLoaderHelper.java
index 3b5b50f7e9a0..2894b2bd8dc4 100644
--- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/util/ClassLoaderHelper.java
+++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/util/ClassLoaderHelper.java
@@ -69,8 +69,7 @@ private static Class> loadClassViaContext(String fqcn) {
* @throws ClassNotFoundException
* if failed to load the class
*/
- public static Class> loadClass(String fqcn, Class>... classes)
- throws ClassNotFoundException {
+ public static Class> loadClass(String fqcn, Class>... classes) throws ClassNotFoundException {
return loadClass(fqcn, true, classes);
}
diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/waiters/WaiterExecutor.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/waiters/WaiterExecutor.java
index 8610c32e49f3..5377e0f04e59 100644
--- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/waiters/WaiterExecutor.java
+++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/waiters/WaiterExecutor.java
@@ -16,14 +16,12 @@
package software.amazon.awssdk.core.internal.waiters;
import java.util.List;
-import java.util.Optional;
import java.util.function.Supplier;
import software.amazon.awssdk.annotations.SdkInternalApi;
import software.amazon.awssdk.annotations.ThreadSafe;
import software.amazon.awssdk.core.exception.SdkClientException;
import software.amazon.awssdk.core.waiters.WaiterAcceptor;
import software.amazon.awssdk.core.waiters.WaiterResponse;
-import software.amazon.awssdk.core.waiters.WaiterState;
import software.amazon.awssdk.utils.Either;
import software.amazon.awssdk.utils.Validate;
@@ -45,45 +43,42 @@ public WaiterExecutor(WaiterConfiguration configuration,
}
WaiterResponse execute(Supplier pollingFunction) {
- return doExecute(pollingFunction, 0, System.currentTimeMillis());
- }
-
- WaiterResponse doExecute(Supplier pollingFunction, int attemptNumber, long startTime) {
- attemptNumber++;
- T response;
- try {
- response = pollingFunction.get();
- } catch (Exception exception) {
- return evaluate(pollingFunction, Either.right(exception), attemptNumber, startTime);
- }
-
- return evaluate(pollingFunction, Either.left(response), attemptNumber, startTime);
- }
+ int attemptNumber = 0;
+ long startTime = System.currentTimeMillis();
- private WaiterResponse evaluate(Supplier pollingFunction,
- Either responseOrException,
- int attemptNumber,
- long startTime) {
- Optional> waiterAcceptor = executorHelper.firstWaiterAcceptorIfMatched(responseOrException);
+ while (true) {
+ attemptNumber++;
- if (waiterAcceptor.isPresent()) {
- WaiterState state = waiterAcceptor.get().waiterState();
- switch (state) {
+ Either polledResponse = pollResponse(pollingFunction);
+ WaiterAcceptor super T> waiterAcceptor = firstWaiterAcceptor(polledResponse);
+ switch (waiterAcceptor.waiterState()) {
case SUCCESS:
- return executorHelper.createWaiterResponse(responseOrException, attemptNumber);
+ return executorHelper.createWaiterResponse(polledResponse, attemptNumber);
case RETRY:
- return maybeRetry(pollingFunction, attemptNumber, startTime);
+ waitToRetry(attemptNumber, startTime);
+ break;
case FAILURE:
- throw executorHelper.waiterFailureException(waiterAcceptor.get());
+ throw executorHelper.waiterFailureException(waiterAcceptor);
default:
throw new UnsupportedOperationException();
}
}
+ }
+
+ private Either pollResponse(Supplier pollingFunction) {
+ try {
+ return Either.left(pollingFunction.get());
+ } catch (Exception exception) {
+ return Either.right(exception);
+ }
+ }
- throw executorHelper.noneMatchException(responseOrException);
+ private WaiterAcceptor super T> firstWaiterAcceptor(Either responseOrException) {
+ return executorHelper.firstWaiterAcceptorIfMatched(responseOrException)
+ .orElseThrow(() -> executorHelper.noneMatchException(responseOrException));
}
- private WaiterResponse maybeRetry(Supplier pollingFunction, int attemptNumber, long startTime) {
+ private void waitToRetry(int attemptNumber, long startTime) {
Either nextDelayOrUnretryableException =
executorHelper.nextDelayOrUnretryableException(attemptNumber, startTime);
@@ -97,6 +92,5 @@ private WaiterResponse maybeRetry(Supplier pollingFunction, int attemptNum
Thread.currentThread().interrupt();
throw SdkClientException.create("The thread got interrupted", e);
}
- return doExecute(pollingFunction, attemptNumber, startTime);
}
}
diff --git a/core/sdk-core/src/main/java/software/amazon/awssdk/core/metrics/CoreMetric.java b/core/sdk-core/src/main/java/software/amazon/awssdk/core/metrics/CoreMetric.java
index fda6bbc67113..f4529d32c1a0 100644
--- a/core/sdk-core/src/main/java/software/amazon/awssdk/core/metrics/CoreMetric.java
+++ b/core/sdk-core/src/main/java/software/amazon/awssdk/core/metrics/CoreMetric.java
@@ -118,6 +118,22 @@ public final class CoreMetric {
public static final SdkMetric AWS_EXTENDED_REQUEST_ID =
metric("AwsExtendedRequestId", String.class, MetricLevel.INFO);
+ /**
+ * The type of error that occurred for a call attempt.
+ *
+ * The following are possible values:
+ *
+ * Throttling - The service responded with a throttling error.
+ * ServerError - The service responded with an error other than throttling.
+ * ClientTimeout - A client timeout occurred, either at the API call level, or API call attempt level.
+ * IO - An I/O error occurred.
+ * Other - Catch-all for other errors that don't fall into the above categories.
+ *
+ *
+ */
+ public static final SdkMetric ERROR_TYPE =
+ metric("ErrorType", String.class, MetricLevel.INFO);
+
private CoreMetric() {
}
diff --git a/core/sdk-core/src/test/java/software/amazon/awssdk/core/async/AsyncRequestBodyTest.java b/core/sdk-core/src/test/java/software/amazon/awssdk/core/async/AsyncRequestBodyTest.java
index e0252c9ba6d2..aab643cbb6a6 100644
--- a/core/sdk-core/src/test/java/software/amazon/awssdk/core/async/AsyncRequestBodyTest.java
+++ b/core/sdk-core/src/test/java/software/amazon/awssdk/core/async/AsyncRequestBodyTest.java
@@ -15,44 +15,39 @@
package software.amazon.awssdk.core.async;
-import static java.nio.charset.StandardCharsets.UTF_8;
-import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
import com.google.common.jimfs.Configuration;
import com.google.common.jimfs.Jimfs;
import io.reactivex.Flowable;
-import java.io.File;
-import java.io.FileWriter;
import java.io.IOException;
-import java.io.InputStream;
import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.time.Instant;
-import java.util.Collections;
import java.util.List;
-import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Function;
import java.util.stream.Collectors;
import org.assertj.core.util.Lists;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.junit.jupiter.params.provider.ValueSource;
import org.reactivestreams.Publisher;
import org.reactivestreams.Subscriber;
import software.amazon.awssdk.core.internal.util.Mimetype;
import software.amazon.awssdk.http.async.SimpleSubscriber;
import software.amazon.awssdk.utils.BinaryUtils;
-import software.amazon.awssdk.utils.StringInputStream;
-@RunWith(Parameterized.class)
public class AsyncRequestBodyTest {
- private final static String testString = "Hello!";
- private final static Path path;
+
+ private static final String testString = "Hello!";
+ private static final Path path;
static {
FileSystem fs = Jimfs.newFileSystem(Configuration.unix());
@@ -64,27 +59,16 @@ public class AsyncRequestBodyTest {
}
}
- @Parameterized.Parameters
- public static AsyncRequestBody[] data() {
- return new AsyncRequestBody[]{
- AsyncRequestBody.fromString(testString),
- AsyncRequestBody.fromFile(path)
- };
+ @ParameterizedTest
+ @MethodSource("contentIntegrityChecks")
+ void hasCorrectLength(AsyncRequestBody asyncRequestBody) {
+ assertEquals(testString.length(), asyncRequestBody.contentLength().get());
}
- private AsyncRequestBody provider;
-
- public AsyncRequestBodyTest(AsyncRequestBody provider) {
- this.provider = provider;
- }
- @Test
- public void hasCorrectLength() {
- assertThat(provider.contentLength().get()).isEqualTo(testString.length());
- }
-
- @Test
- public void hasCorrectContent() throws InterruptedException {
+ @ParameterizedTest
+ @MethodSource("contentIntegrityChecks")
+ void hasCorrectContent(AsyncRequestBody asyncRequestBody) throws InterruptedException {
StringBuilder sb = new StringBuilder();
CountDownLatch done = new CountDownLatch(1);
@@ -106,75 +90,268 @@ public void onComplete() {
}
};
- provider.subscribe(subscriber);
+ asyncRequestBody.subscribe(subscriber);
done.await();
- assertThat(sb.toString()).isEqualTo(testString);
+ assertEquals(testString, sb.toString());
+ }
+
+ private static AsyncRequestBody[] contentIntegrityChecks() {
+ return new AsyncRequestBody[] {
+ AsyncRequestBody.fromString(testString),
+ AsyncRequestBody.fromFile(path)
+ };
}
@Test
- public void stringConstructorHasCorrectContentType() {
- AsyncRequestBody requestBody = AsyncRequestBody.fromString("hello world");
- assertThat(requestBody.contentType()).isEqualTo("text/plain; charset=UTF-8");
+ void fromBytesCopiesTheProvidedByteArray() {
+ byte[] bytes = testString.getBytes(StandardCharsets.UTF_8);
+ byte[] bytesClone = bytes.clone();
+
+ AsyncRequestBody asyncRequestBody = AsyncRequestBody.fromBytes(bytes);
+
+ for (int i = 0; i < bytes.length; i++) {
+ bytes[i] += 1;
+ }
+
+ AtomicReference publishedBuffer = new AtomicReference<>();
+ Subscriber subscriber = new SimpleSubscriber(publishedBuffer::set);
+
+ asyncRequestBody.subscribe(subscriber);
+
+ byte[] publishedByteArray = BinaryUtils.copyAllBytesFrom(publishedBuffer.get());
+ assertArrayEquals(bytesClone, publishedByteArray);
}
@Test
- public void stringWithEncoding1ConstructorHasCorrectContentType() {
- AsyncRequestBody requestBody = AsyncRequestBody.fromString("hello world", StandardCharsets.ISO_8859_1);
- assertThat(requestBody.contentType()).isEqualTo("text/plain; charset=ISO-8859-1");
+ void fromBytesUnsafeDoesNotCopyTheProvidedByteArray() {
+ byte[] bytes = testString.getBytes(StandardCharsets.UTF_8);
+
+ AsyncRequestBody asyncRequestBody = AsyncRequestBody.fromBytesUnsafe(bytes);
+
+ for (int i = 0; i < bytes.length; i++) {
+ bytes[i] += 1;
+ }
+
+ AtomicReference publishedBuffer = new AtomicReference<>();
+ Subscriber subscriber = new SimpleSubscriber(publishedBuffer::set);
+
+ asyncRequestBody.subscribe(subscriber);
+
+ byte[] publishedByteArray = BinaryUtils.copyAllBytesFrom(publishedBuffer.get());
+ assertArrayEquals(bytes, publishedByteArray);
+ }
+
+ @ParameterizedTest
+ @MethodSource("safeByteBufferBodyBuilders")
+ void safeByteBufferBuildersCopyTheProvidedBuffer(Function bodyBuilder) {
+ byte[] bytes = testString.getBytes(StandardCharsets.UTF_8);
+ byte[] bytesClone = bytes.clone();
+
+ AsyncRequestBody asyncRequestBody = bodyBuilder.apply(ByteBuffer.wrap(bytes));
+
+ for (int i = 0; i < bytes.length; i++) {
+ bytes[i] += 1;
+ }
+
+ AtomicReference publishedBuffer = new AtomicReference<>();
+ Subscriber subscriber = new SimpleSubscriber(publishedBuffer::set);
+
+ asyncRequestBody.subscribe(subscriber);
+
+ byte[] publishedByteArray = BinaryUtils.copyAllBytesFrom(publishedBuffer.get());
+ assertArrayEquals(bytesClone, publishedByteArray);
+ }
+
+ private static Function[] safeByteBufferBodyBuilders() {
+ Function fromByteBuffer = AsyncRequestBody::fromByteBuffer;
+ Function fromRemainingByteBuffer = AsyncRequestBody::fromRemainingByteBuffer;
+ Function fromByteBuffers = AsyncRequestBody::fromByteBuffers;
+ Function fromRemainingByteBuffers = AsyncRequestBody::fromRemainingByteBuffers;
+ return new Function[] {fromByteBuffer, fromRemainingByteBuffer, fromByteBuffers, fromRemainingByteBuffers};
+ }
+
+ @ParameterizedTest
+ @MethodSource("unsafeByteBufferBodyBuilders")
+ void unsafeByteBufferBuildersDoNotCopyTheProvidedBuffer(Function bodyBuilder) {
+ byte[] bytes = testString.getBytes(StandardCharsets.UTF_8);
+
+ AsyncRequestBody asyncRequestBody = bodyBuilder.apply(ByteBuffer.wrap(bytes));
+
+ for (int i = 0; i < bytes.length; i++) {
+ bytes[i] += 1;
+ }
+
+ AtomicReference publishedBuffer = new AtomicReference<>();
+ Subscriber subscriber = new SimpleSubscriber(publishedBuffer::set);
+
+ asyncRequestBody.subscribe(subscriber);
+
+ byte[] publishedByteArray = BinaryUtils.copyAllBytesFrom(publishedBuffer.get());
+ assertArrayEquals(bytes, publishedByteArray);
+ }
+
+ private static Function[] unsafeByteBufferBodyBuilders() {
+ Function fromByteBuffer = AsyncRequestBody::fromByteBufferUnsafe;
+ Function fromRemainingByteBuffer = AsyncRequestBody::fromRemainingByteBufferUnsafe;
+ Function fromByteBuffers = AsyncRequestBody::fromByteBuffersUnsafe;
+ Function fromRemainingByteBuffers = AsyncRequestBody::fromRemainingByteBuffersUnsafe;
+ return new Function[] {fromByteBuffer, fromRemainingByteBuffer, fromByteBuffers, fromRemainingByteBuffers};
+ }
+
+ @ParameterizedTest
+ @MethodSource("nonRewindingByteBufferBodyBuilders")
+ void nonRewindingByteBufferBuildersReadFromTheInputBufferPosition(
+ Function bodyBuilder) {
+ byte[] bytes = testString.getBytes(StandardCharsets.UTF_8);
+ ByteBuffer bb = ByteBuffer.wrap(bytes);
+ int expectedPosition = bytes.length / 2;
+ bb.position(expectedPosition);
+
+ AsyncRequestBody asyncRequestBody = bodyBuilder.apply(bb);
+
+ AtomicReference publishedBuffer = new AtomicReference<>();
+ Subscriber subscriber = new SimpleSubscriber(publishedBuffer::set);
+
+ asyncRequestBody.subscribe(subscriber);
+
+ int remaining = bb.remaining();
+ assertEquals(remaining, publishedBuffer.get().remaining());
+ for (int i = 0; i < remaining; i++) {
+ assertEquals(bb.get(), publishedBuffer.get().get());
+ }
+ }
+
+ private static Function[] nonRewindingByteBufferBodyBuilders() {
+ Function fromRemainingByteBuffer = AsyncRequestBody::fromRemainingByteBuffer;
+ Function fromRemainingByteBufferUnsafe = AsyncRequestBody::fromRemainingByteBufferUnsafe;
+ Function fromRemainingByteBuffers = AsyncRequestBody::fromRemainingByteBuffers;
+ Function fromRemainingByteBuffersUnsafe = AsyncRequestBody::fromRemainingByteBuffersUnsafe;
+ return new Function[] {fromRemainingByteBuffer, fromRemainingByteBufferUnsafe, fromRemainingByteBuffers,
+ fromRemainingByteBuffersUnsafe};
+ }
+
+ @ParameterizedTest
+ @MethodSource("safeNonRewindingByteBufferBodyBuilders")
+ void safeNonRewindingByteBufferBuildersCopyFromTheInputBufferPosition(
+ Function bodyBuilder) {
+ byte[] bytes = testString.getBytes(StandardCharsets.UTF_8);
+ ByteBuffer bb = ByteBuffer.wrap(bytes);
+ int expectedPosition = bytes.length / 2;
+ bb.position(expectedPosition);
+
+ AsyncRequestBody asyncRequestBody = bodyBuilder.apply(bb);
+
+ AtomicReference publishedBuffer = new AtomicReference<>();
+ Subscriber subscriber = new SimpleSubscriber(publishedBuffer::set);
+
+ asyncRequestBody.subscribe(subscriber);
+
+ int remaining = bb.remaining();
+ assertEquals(remaining, publishedBuffer.get().capacity());
+ for (int i = 0; i < remaining; i++) {
+ assertEquals(bb.get(), publishedBuffer.get().get());
+ }
+ }
+
+ private static Function[] safeNonRewindingByteBufferBodyBuilders() {
+ Function fromRemainingByteBuffer = AsyncRequestBody::fromRemainingByteBuffer;
+ Function fromRemainingByteBuffers = AsyncRequestBody::fromRemainingByteBuffers;
+ return new Function[] {fromRemainingByteBuffer, fromRemainingByteBuffers};
+ }
+
+ @ParameterizedTest
+ @MethodSource("rewindingByteBufferBodyBuilders")
+ void rewindingByteBufferBuildersDoNotRewindTheInputBuffer(Function bodyBuilder) {
+ byte[] bytes = testString.getBytes(StandardCharsets.UTF_8);
+ ByteBuffer bb = ByteBuffer.wrap(bytes);
+ int expectedPosition = bytes.length / 2;
+ bb.position(expectedPosition);
+
+ AsyncRequestBody asyncRequestBody = bodyBuilder.apply(bb);
+
+ Subscriber subscriber = new SimpleSubscriber(buffer -> {
+ });
+
+ asyncRequestBody.subscribe(subscriber);
+
+ assertEquals(expectedPosition, bb.position());
+ }
+
+ @ParameterizedTest
+ @MethodSource("rewindingByteBufferBodyBuilders")
+ void rewindingByteBufferBuildersReadTheInputBufferFromTheBeginning(
+ Function bodyBuilder) {
+ byte[] bytes = testString.getBytes(StandardCharsets.UTF_8);
+ ByteBuffer bb = ByteBuffer.wrap(bytes);
+ bb.position(bytes.length / 2);
+
+ AsyncRequestBody asyncRequestBody = bodyBuilder.apply(bb);
+
+ AtomicReference publishedBuffer = new AtomicReference<>();
+ Subscriber subscriber = new SimpleSubscriber(publishedBuffer::set);
+
+ asyncRequestBody.subscribe(subscriber);
+
+ assertEquals(0, publishedBuffer.get().position());
+ publishedBuffer.get().rewind();
+ bb.rewind();
+ assertEquals(bb, publishedBuffer.get());
+ }
+
+ private static Function[] rewindingByteBufferBodyBuilders() {
+ Function fromByteBuffer = AsyncRequestBody::fromByteBuffer;
+ Function fromByteBufferUnsafe = AsyncRequestBody::fromByteBufferUnsafe;
+ Function fromByteBuffers = AsyncRequestBody::fromByteBuffers;
+ Function fromByteBuffersUnsafe = AsyncRequestBody::fromByteBuffersUnsafe;
+ return new Function[] {fromByteBuffer, fromByteBufferUnsafe, fromByteBuffers, fromByteBuffersUnsafe};
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = {"US-ASCII", "ISO-8859-1", "UTF-8", "UTF-16BE", "UTF-16LE", "UTF-16"})
+ void charsetsAreConvertedToTheCorrectContentType(Charset charset) {
+ AsyncRequestBody requestBody = AsyncRequestBody.fromString("hello world", charset);
+ assertEquals("text/plain; charset=" + charset.name(), requestBody.contentType());
}
@Test
- public void stringWithEncoding2ConstructorHasCorrectContentType() {
- AsyncRequestBody requestBody = AsyncRequestBody.fromString("hello world", StandardCharsets.UTF_16BE);
- assertThat(requestBody.contentType()).isEqualTo("text/plain; charset=UTF-16BE");
+ void stringConstructorHasCorrectDefaultContentType() {
+ AsyncRequestBody requestBody = AsyncRequestBody.fromString("hello world");
+ assertEquals("text/plain; charset=UTF-8", requestBody.contentType());
}
@Test
- public void fileConstructorHasCorrectContentType() {
+ void fileConstructorHasCorrectContentType() {
AsyncRequestBody requestBody = AsyncRequestBody.fromFile(path);
- assertThat(requestBody.contentType()).isEqualTo(Mimetype.MIMETYPE_OCTET_STREAM);
+ assertEquals(Mimetype.MIMETYPE_OCTET_STREAM, requestBody.contentType());
}
@Test
- public void bytesArrayConstructorHasCorrectContentType() {
+ void bytesArrayConstructorHasCorrectContentType() {
AsyncRequestBody requestBody = AsyncRequestBody.fromBytes("hello world".getBytes());
- assertThat(requestBody.contentType()).isEqualTo(Mimetype.MIMETYPE_OCTET_STREAM);
+ assertEquals(Mimetype.MIMETYPE_OCTET_STREAM, requestBody.contentType());
}
@Test
- public void bytesBufferConstructorHasCorrectContentType() {
+ void bytesBufferConstructorHasCorrectContentType() {
ByteBuffer byteBuffer = ByteBuffer.wrap("hello world".getBytes());
AsyncRequestBody requestBody = AsyncRequestBody.fromByteBuffer(byteBuffer);
- assertThat(requestBody.contentType()).isEqualTo(Mimetype.MIMETYPE_OCTET_STREAM);
+ assertEquals(Mimetype.MIMETYPE_OCTET_STREAM, requestBody.contentType());
}
@Test
- public void emptyBytesConstructorHasCorrectContentType() {
+ void emptyBytesConstructorHasCorrectContentType() {
AsyncRequestBody requestBody = AsyncRequestBody.empty();
- assertThat(requestBody.contentType()).isEqualTo(Mimetype.MIMETYPE_OCTET_STREAM);
+ assertEquals(Mimetype.MIMETYPE_OCTET_STREAM, requestBody.contentType());
}
@Test
- public void publisherConstructorHasCorrectContentType() {
+ void publisherConstructorHasCorrectContentType() {
List requestBodyStrings = Lists.newArrayList("A", "B", "C");
List bodyBytes = requestBodyStrings.stream()
- .map(s -> ByteBuffer.wrap(s.getBytes(StandardCharsets.UTF_8)))
- .collect(Collectors.toList());
+ .map(s -> ByteBuffer.wrap(s.getBytes(StandardCharsets.UTF_8)))
+ .collect(Collectors.toList());
Publisher bodyPublisher = Flowable.fromIterable(bodyBytes);
AsyncRequestBody requestBody = AsyncRequestBody.fromPublisher(bodyPublisher);
- assertThat(requestBody.contentType()).isEqualTo(Mimetype.MIMETYPE_OCTET_STREAM);
- }
-
- @Test
- public void fromBytes_byteArrayNotNull_createsCopy() {
- byte[] original = {0x1, 0x2, 0x3, 0x4};
- byte[] toModify = new byte[original.length];
- System.arraycopy(original, 0, toModify, 0, original.length);
- AsyncRequestBody body = AsyncRequestBody.fromBytes(toModify);
- for (int i = 0; i < toModify.length; ++i) {
- toModify[i]++;
- }
- ByteBuffer publishedBb = Flowable.fromPublisher(body).toList().blockingGet().get(0);
- assertThat(BinaryUtils.copyAllBytesFrom(publishedBb)).isEqualTo(original);
+ assertEquals(Mimetype.MIMETYPE_OCTET_STREAM, requestBody.contentType());
}
}
diff --git a/core/sdk-core/src/test/java/software/amazon/awssdk/core/client/builder/DefaultClientBuilderTest.java b/core/sdk-core/src/test/java/software/amazon/awssdk/core/client/builder/DefaultClientBuilderTest.java
index ec526330cdc9..bc4a00954ca8 100644
--- a/core/sdk-core/src/test/java/software/amazon/awssdk/core/client/builder/DefaultClientBuilderTest.java
+++ b/core/sdk-core/src/test/java/software/amazon/awssdk/core/client/builder/DefaultClientBuilderTest.java
@@ -38,6 +38,7 @@
import static software.amazon.awssdk.core.client.config.SdkClientOption.PROFILE_FILE_SUPPLIER;
import static software.amazon.awssdk.core.client.config.SdkClientOption.PROFILE_NAME;
import static software.amazon.awssdk.core.client.config.SdkClientOption.RETRY_POLICY;
+import static software.amazon.awssdk.core.client.config.SdkClientOption.SCHEDULED_EXECUTOR_SERVICE;
import static software.amazon.awssdk.core.internal.SdkInternalTestAdvancedClientOption.ENDPOINT_OVERRIDDEN_OVERRIDE;
import java.beans.BeanInfo;
@@ -52,6 +53,8 @@
import java.util.List;
import java.util.Map;
import java.util.Optional;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
import java.util.function.Supplier;
import org.assertj.core.api.Assertions;
import org.junit.Before;
@@ -76,6 +79,7 @@
import software.amazon.awssdk.metrics.MetricPublisher;
import software.amazon.awssdk.profiles.ProfileFile;
import software.amazon.awssdk.utils.AttributeMap;
+import software.amazon.awssdk.utils.ScheduledExecutorUtils.UnmanagedScheduledExecutorService;
import software.amazon.awssdk.utils.StringInputStream;
/**
@@ -132,6 +136,7 @@ public void overrideConfigurationReturnsSetValues() {
.type(ProfileFile.Type.CONFIGURATION)
.build();
String profileName = "name";
+ ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
ClientOverrideConfiguration overrideConfig = ClientOverrideConfiguration.builder()
.executionInterceptors(interceptors)
@@ -148,6 +153,7 @@ public void overrideConfigurationReturnsSetValues() {
.metricPublishers(metricPublishers)
.executionAttributes(executionAttributes)
.putAdvancedOption(ENDPOINT_OVERRIDDEN_OVERRIDE, Boolean.TRUE)
+ .scheduledExecutorService(scheduledExecutorService)
.build();
TestClientBuilder builder = testClientBuilder().overrideConfiguration(overrideConfig);
@@ -166,6 +172,7 @@ public void overrideConfigurationReturnsSetValues() {
assertThat(builderOverrideConfig.metricPublishers()).isEqualTo(metricPublishers);
assertThat(builderOverrideConfig.executionAttributes().getAttributes()).isEqualTo(executionAttributes.getAttributes());
assertThat(builderOverrideConfig.advancedOption(ENDPOINT_OVERRIDDEN_OVERRIDE)).isEqualTo(Optional.of(Boolean.TRUE));
+ assertThat(builderOverrideConfig.scheduledExecutorService().get()).isEqualTo(scheduledExecutorService);
}
@Test
@@ -189,6 +196,7 @@ public void overrideConfigurationOmitsUnsetValues() {
assertThat(builderOverrideConfig.metricPublishers()).isEmpty();
assertThat(builderOverrideConfig.executionAttributes().getAttributes()).isEmpty();
assertThat(builderOverrideConfig.advancedOption(ENDPOINT_OVERRIDDEN_OVERRIDE)).isEmpty();
+ assertThat(builderOverrideConfig.scheduledExecutorService()).isEmpty();
}
@Test
@@ -198,6 +206,7 @@ public void buildIncludesClientOverrides() {
interceptors.add(interceptor);
RetryPolicy retryPolicy = RetryPolicy.builder().build();
+ ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
Map> headers = new HashMap<>();
List headerValues = new ArrayList<>();
@@ -247,6 +256,7 @@ public void close() {
.metricPublishers(metricPublishers)
.executionAttributes(executionAttributes)
.putAdvancedOption(ENDPOINT_OVERRIDDEN_OVERRIDE, Boolean.TRUE)
+ .scheduledExecutorService(scheduledExecutorService)
.build();
SdkClientConfiguration config =
@@ -267,6 +277,9 @@ public void close() {
assertThat(config.option(METRIC_PUBLISHERS)).contains(metricPublisher);
assertThat(config.option(EXECUTION_ATTRIBUTES).getAttribute(execAttribute)).isEqualTo("value");
assertThat(config.option(ENDPOINT_OVERRIDDEN)).isEqualTo(Boolean.TRUE);
+ UnmanagedScheduledExecutorService customScheduledExecutorService =
+ (UnmanagedScheduledExecutorService) config.option(SCHEDULED_EXECUTOR_SERVICE);
+ assertThat(customScheduledExecutorService.scheduledExecutorService()).isEqualTo(scheduledExecutorService);
}
@Test
diff --git a/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/async/ByteArrayAsyncRequestBodyTest.java b/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/async/ByteArrayAsyncRequestBodyTest.java
deleted file mode 100644
index 378fbf2f59c3..000000000000
--- a/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/async/ByteArrayAsyncRequestBodyTest.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright 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 software.amazon.awssdk.core.internal.async;
-
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-import java.nio.ByteBuffer;
-import java.util.concurrent.atomic.AtomicBoolean;
-import org.junit.jupiter.api.Test;
-import org.reactivestreams.Subscriber;
-import org.reactivestreams.Subscription;
-import software.amazon.awssdk.core.internal.util.Mimetype;
-
-public class ByteArrayAsyncRequestBodyTest {
- private class testSubscriber implements Subscriber {
- private Subscription subscription;
- protected AtomicBoolean onCompleteCalled = new AtomicBoolean(false);
-
- @Override
- public void onSubscribe(Subscription s) {
- this.subscription = s;
- s.request(1);
- }
-
- @Override
- public void onNext(ByteBuffer byteBuffer) {
-
- }
-
- @Override
- public void onError(Throwable throwable) {
-
- }
-
- @Override
- public void onComplete() {
- subscription.request(1);
- onCompleteCalled.set(true);
- }
- }
-
- testSubscriber subscriber = new testSubscriber();
-
- @Test
- public void concurrentRequests_shouldCompleteNormally() {
- ByteArrayAsyncRequestBody byteArrayReq = new ByteArrayAsyncRequestBody("Hello World!".getBytes(),
- Mimetype.MIMETYPE_OCTET_STREAM);
- byteArrayReq.subscribe(subscriber);
- assertTrue(subscriber.onCompleteCalled.get());
- }
-
-}
diff --git a/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/async/ByteBuffersAsyncRequestBodyTest.java b/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/async/ByteBuffersAsyncRequestBodyTest.java
new file mode 100644
index 000000000000..b4073247f8b9
--- /dev/null
+++ b/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/async/ByteBuffersAsyncRequestBodyTest.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright 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 software.amazon.awssdk.core.internal.async;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.IntStream;
+import org.junit.jupiter.api.Test;
+import org.reactivestreams.Subscriber;
+import org.reactivestreams.Subscription;
+import software.amazon.awssdk.core.async.AsyncRequestBody;
+import software.amazon.awssdk.utils.BinaryUtils;
+
+class ByteBuffersAsyncRequestBodyTest {
+
+ private static class TestSubscriber implements Subscriber {
+ private Subscription subscription;
+ private boolean onCompleteCalled = false;
+ private int callsToComplete = 0;
+ private final List publishedResults = Collections.synchronizedList(new ArrayList<>());
+
+ public void request(long n) {
+ subscription.request(n);
+ }
+
+ @Override
+ public void onSubscribe(Subscription s) {
+ this.subscription = s;
+ }
+
+ @Override
+ public void onNext(ByteBuffer byteBuffer) {
+ publishedResults.add(byteBuffer);
+ }
+
+ @Override
+ public void onError(Throwable throwable) {
+ throw new IllegalStateException(throwable);
+ }
+
+ @Override
+ public void onComplete() {
+ onCompleteCalled = true;
+ callsToComplete++;
+ }
+ }
+
+ @Test
+ public void subscriberIsMarkedAsCompleted() {
+ AsyncRequestBody requestBody = ByteBuffersAsyncRequestBody.from("Hello World!".getBytes(StandardCharsets.UTF_8));
+
+ TestSubscriber subscriber = new TestSubscriber();
+ requestBody.subscribe(subscriber);
+ subscriber.request(1);
+
+ assertTrue(subscriber.onCompleteCalled);
+ assertEquals(1, subscriber.publishedResults.size());
+ }
+
+ @Test
+ public void subscriberIsMarkedAsCompletedWhenARequestIsMadeForMoreBuffersThanAreAvailable() {
+ AsyncRequestBody requestBody = ByteBuffersAsyncRequestBody.from("Hello World!".getBytes(StandardCharsets.UTF_8));
+
+ TestSubscriber subscriber = new TestSubscriber();
+ requestBody.subscribe(subscriber);
+ subscriber.request(2);
+
+ assertTrue(subscriber.onCompleteCalled);
+ assertEquals(1, subscriber.publishedResults.size());
+ }
+
+ @Test
+ public void subscriberIsThreadSafeAndMarkedAsCompletedExactlyOnce() throws InterruptedException {
+ int numBuffers = 100;
+ AsyncRequestBody requestBody = ByteBuffersAsyncRequestBody.of(IntStream.range(0, numBuffers)
+ .mapToObj(i -> ByteBuffer.wrap(new byte[1]))
+ .toArray(ByteBuffer[]::new));
+
+ TestSubscriber subscriber = new TestSubscriber();
+ requestBody.subscribe(subscriber);
+
+ int parallelism = 8;
+ ExecutorService executorService = Executors.newFixedThreadPool(parallelism);
+ for (int i = 0; i < parallelism; i++) {
+ executorService.submit(() -> {
+ for (int j = 0; j < numBuffers; j++) {
+ subscriber.request(2);
+ }
+ });
+ }
+ executorService.shutdown();
+ executorService.awaitTermination(1, TimeUnit.MINUTES);
+
+ assertTrue(subscriber.onCompleteCalled);
+ assertEquals(1, subscriber.callsToComplete);
+ assertEquals(numBuffers, subscriber.publishedResults.size());
+ }
+
+ @Test
+ public void subscriberIsNotMarkedAsCompletedWhenThereAreRemainingBuffersToPublish() {
+ byte[] helloWorld = "Hello World!".getBytes(StandardCharsets.UTF_8);
+ byte[] goodbyeWorld = "Goodbye World!".getBytes(StandardCharsets.UTF_8);
+ AsyncRequestBody requestBody = ByteBuffersAsyncRequestBody.of((long) (helloWorld.length + goodbyeWorld.length),
+ ByteBuffer.wrap(helloWorld),
+ ByteBuffer.wrap(goodbyeWorld));
+
+ TestSubscriber subscriber = new TestSubscriber();
+ requestBody.subscribe(subscriber);
+ subscriber.request(1);
+
+ assertFalse(subscriber.onCompleteCalled);
+ assertEquals(1, subscriber.publishedResults.size());
+ }
+
+ @Test
+ public void subscriberReceivesAllBuffers() {
+ byte[] helloWorld = "Hello World!".getBytes(StandardCharsets.UTF_8);
+ byte[] goodbyeWorld = "Goodbye World!".getBytes(StandardCharsets.UTF_8);
+
+ AsyncRequestBody requestBody = ByteBuffersAsyncRequestBody.of((long) (helloWorld.length + goodbyeWorld.length),
+ ByteBuffer.wrap(helloWorld),
+ ByteBuffer.wrap(goodbyeWorld));
+
+ TestSubscriber subscriber = new TestSubscriber();
+ requestBody.subscribe(subscriber);
+ subscriber.request(2);
+
+ assertEquals(2, subscriber.publishedResults.size());
+ assertTrue(subscriber.onCompleteCalled);
+ assertArrayEquals(helloWorld, BinaryUtils.copyAllBytesFrom(subscriber.publishedResults.get(0)));
+ assertArrayEquals(goodbyeWorld, BinaryUtils.copyAllBytesFrom(subscriber.publishedResults.get(1)));
+ }
+
+ @Test
+ public void multipleSubscribersReceiveTheSameResults() {
+ ByteBuffer sourceBuffer = ByteBuffer.wrap("Hello World!".getBytes(StandardCharsets.UTF_8));
+ AsyncRequestBody requestBody = ByteBuffersAsyncRequestBody.of(sourceBuffer);
+
+ TestSubscriber subscriber = new TestSubscriber();
+ requestBody.subscribe(subscriber);
+ subscriber.request(1);
+ TestSubscriber otherSubscriber = new TestSubscriber();
+ requestBody.subscribe(otherSubscriber);
+ otherSubscriber.request(1);
+
+ ByteBuffer publishedBuffer = subscriber.publishedResults.get(0);
+ ByteBuffer otherPublishedBuffer = otherSubscriber.publishedResults.get(0);
+
+ assertEquals(publishedBuffer, otherPublishedBuffer);
+ }
+
+ @Test
+ public void canceledSubscriberDoesNotReturnNewResults() {
+ AsyncRequestBody requestBody = ByteBuffersAsyncRequestBody.of(ByteBuffer.wrap(new byte[0]));
+
+ TestSubscriber subscriber = new TestSubscriber();
+ requestBody.subscribe(subscriber);
+
+ subscriber.subscription.cancel();
+ subscriber.request(1);
+
+ assertTrue(subscriber.publishedResults.isEmpty());
+ }
+
+ // Pending discussions on https://github.com/aws/aws-sdk-java-v2/issues/3928
+ @Test
+ public void directBuffersAreCoppiedToNonDirectBuffers() {
+ byte[] bytes = "Hello World!".getBytes(StandardCharsets.UTF_8);
+ ByteBuffer buffer = ByteBuffer.allocateDirect(bytes.length)
+ .put(bytes);
+ buffer.flip();
+ AsyncRequestBody requestBody = ByteBuffersAsyncRequestBody.of(buffer);
+
+ TestSubscriber subscriber = new TestSubscriber();
+ requestBody.subscribe(subscriber);
+ subscriber.request(1);
+
+ ByteBuffer publishedBuffer = subscriber.publishedResults.get(0);
+ assertFalse(publishedBuffer.isDirect());
+ byte[] publishedBytes = new byte[publishedBuffer.remaining()];
+ publishedBuffer.get(publishedBytes);
+ assertArrayEquals(bytes, publishedBytes);
+ }
+
+ @Test
+ public void staticOfByteBufferConstructorSetsLengthBasedOnBufferRemaining() {
+ ByteBuffer bb1 = ByteBuffer.allocate(2);
+ ByteBuffer bb2 = ByteBuffer.allocate(2);
+ bb2.position(1);
+ ByteBuffersAsyncRequestBody body = ByteBuffersAsyncRequestBody.of(bb1, bb2);
+ assertTrue(body.contentLength().isPresent());
+ assertEquals(bb1.remaining() + bb2.remaining(), body.contentLength().get());
+ }
+
+ @Test
+ public void staticFromBytesConstructorSetsLengthBasedOnArrayLength() {
+ byte[] bytes = new byte[2];
+ ByteBuffersAsyncRequestBody body = ByteBuffersAsyncRequestBody.from(bytes);
+ assertTrue(body.contentLength().isPresent());
+ assertEquals(bytes.length, body.contentLength().get());
+ }
+
+}
diff --git a/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/http/pipeline/stages/MakeAsyncHttpRequestStageTest.java b/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/http/pipeline/stages/MakeAsyncHttpRequestStageTest.java
index 092682e54998..df8126db2343 100644
--- a/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/http/pipeline/stages/MakeAsyncHttpRequestStageTest.java
+++ b/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/http/pipeline/stages/MakeAsyncHttpRequestStageTest.java
@@ -16,11 +16,14 @@
package software.amazon.awssdk.core.internal.http.pipeline.stages;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -31,6 +34,9 @@
import java.time.Duration;
import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
@@ -47,6 +53,7 @@
import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
import software.amazon.awssdk.core.internal.http.HttpClientDependencies;
import software.amazon.awssdk.core.internal.http.RequestExecutionContext;
+import software.amazon.awssdk.core.internal.http.TransformingAsyncResponseHandler;
import software.amazon.awssdk.core.internal.http.timers.ClientExecutionAndRequestTimerTestUtils;
import software.amazon.awssdk.core.internal.util.AsyncResponseHandlerTestUtils;
import software.amazon.awssdk.http.SdkHttpFullRequest;
@@ -54,6 +61,8 @@
import software.amazon.awssdk.http.async.AsyncExecuteRequest;
import software.amazon.awssdk.http.async.SdkAsyncHttpClient;
import software.amazon.awssdk.metrics.MetricCollector;
+import software.amazon.awssdk.utils.CompletableFutureUtils;
+import software.amazon.awssdk.utils.ThreadFactoryBuilder;
import utils.ValidSdkObjects;
@RunWith(MockitoJUnitRunner.class)
@@ -152,6 +161,82 @@ public void testExecute_contextContainsMetricCollector_addsChildToExecuteRequest
}
}
+ @Test
+ public void execute_handlerFutureCompletedNormally_futureCompletionExecutorRejectsWhenCompleteAsync_futureCompletedSynchronously() {
+ ExecutorService mockExecutor = mock(ExecutorService.class);
+ doThrow(new RejectedExecutionException("Busy")).when(mockExecutor).execute(any(Runnable.class));
+
+ SdkClientConfiguration config =
+ SdkClientConfiguration.builder()
+ .option(SdkAdvancedAsyncClientOption.FUTURE_COMPLETION_EXECUTOR, mockExecutor)
+ .option(ASYNC_HTTP_CLIENT, sdkAsyncHttpClient)
+ .build();
+ HttpClientDependencies dependencies = HttpClientDependencies.builder().clientConfiguration(config).build();
+
+ TransformingAsyncResponseHandler mockHandler = mock(TransformingAsyncResponseHandler.class);
+ CompletableFuture prepareFuture = new CompletableFuture();
+ when(mockHandler.prepare()).thenReturn(prepareFuture);
+
+ stage = new MakeAsyncHttpRequestStage<>(mockHandler, dependencies);
+
+ CompletableFuture requestFuture = CompletableFuture.completedFuture(
+ ValidSdkObjects.sdkHttpFullRequest().build());
+
+ CompletableFuture executeFuture = stage.execute(requestFuture, requestContext());
+
+ long testThreadId = Thread.currentThread().getId();
+ CompletableFuture afterWhenComplete =
+ executeFuture.whenComplete((r, t) -> assertThat(Thread.currentThread().getId()).isEqualTo(testThreadId));
+
+ prepareFuture.complete(null);
+
+ afterWhenComplete.join();
+
+ verify(mockExecutor).execute(any(Runnable.class));
+ }
+
+ @Test
+ public void execute_handlerFutureCompletedExceptionally_doesNotAttemptSynchronousComplete() {
+ String threadNamePrefix = "async-handle-test";
+ ExecutorService mockExecutor = Executors.newSingleThreadExecutor(
+ new ThreadFactoryBuilder().threadNamePrefix(threadNamePrefix).build());
+
+ SdkClientConfiguration config =
+ SdkClientConfiguration.builder()
+ .option(SdkAdvancedAsyncClientOption.FUTURE_COMPLETION_EXECUTOR, mockExecutor)
+ .option(ASYNC_HTTP_CLIENT, sdkAsyncHttpClient)
+ .build();
+ HttpClientDependencies dependencies = HttpClientDependencies.builder().clientConfiguration(config).build();
+
+ TransformingAsyncResponseHandler mockHandler = mock(TransformingAsyncResponseHandler.class);
+ CompletableFuture prepareFuture = spy(new CompletableFuture());
+ when(mockHandler.prepare()).thenReturn(prepareFuture);
+
+ stage = new MakeAsyncHttpRequestStage<>(mockHandler, dependencies);
+
+ CompletableFuture requestFuture = CompletableFuture.completedFuture(
+ ValidSdkObjects.sdkHttpFullRequest().build());
+
+ CompletableFuture executeFuture = stage.execute(requestFuture, requestContext());
+
+ try {
+ CompletableFuture afterHandle =
+ executeFuture.handle((r, t) -> assertThat(Thread.currentThread().getName()).startsWith(threadNamePrefix));
+
+ prepareFuture.completeExceptionally(new RuntimeException("parse error"));
+
+ afterHandle.join();
+
+ assertThatThrownBy(executeFuture::join)
+ .hasCauseInstanceOf(RuntimeException.class)
+ .hasMessageContaining("parse error");
+
+ verify(prepareFuture, times(0)).whenComplete(any());
+ } finally {
+ mockExecutor.shutdown();
+ }
+ }
+
private HttpClientDependencies clientDependencies(Duration timeout) {
SdkClientConfiguration configuration = SdkClientConfiguration.builder()
.option(SdkAdvancedAsyncClientOption.FUTURE_COMPLETION_EXECUTOR, Runnable::run)
diff --git a/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/metrics/ErrorTypeTest.java b/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/metrics/ErrorTypeTest.java
new file mode 100644
index 000000000000..6c20c57d8804
--- /dev/null
+++ b/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/metrics/ErrorTypeTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 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 software.amazon.awssdk.core.internal.metrics;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.io.IOException;
+import java.util.stream.Stream;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+import software.amazon.awssdk.core.exception.ApiCallAttemptTimeoutException;
+import software.amazon.awssdk.core.exception.ApiCallTimeoutException;
+import software.amazon.awssdk.core.exception.SdkClientException;
+import software.amazon.awssdk.core.exception.SdkServiceException;
+
+public class ErrorTypeTest {
+
+ @ParameterizedTest
+ @MethodSource("testCases")
+ public void fromException_mapsToCorrectType(TestCase tc) {
+ assertThat(SdkErrorType.fromException(tc.thrown)).isEqualTo(tc.expectedType);
+ }
+
+ private static Stream extends TestCase> testCases() {
+ return Stream.of(
+ tc(new IOException("I/O"), SdkErrorType.IO),
+
+ tc(TestServiceException.builder().build(), SdkErrorType.SERVER_ERROR),
+ tc(TestServiceException.builder().throttling(true).build(), SdkErrorType.THROTTLING),
+
+ tc(ApiCallAttemptTimeoutException.builder().message("Attempt timeout").build(), SdkErrorType.CONFIGURED_TIMEOUT),
+ tc(ApiCallTimeoutException.builder().message("Call timeout").build(), SdkErrorType.CONFIGURED_TIMEOUT),
+
+ tc(SdkClientException.create("Unmarshalling error"), SdkErrorType.OTHER),
+
+ tc(new OutOfMemoryError("OOM"), SdkErrorType.OTHER)
+ );
+ }
+
+ private static TestCase tc(Throwable thrown, SdkErrorType expectedType) {
+ return new TestCase(thrown, expectedType);
+ }
+
+ private static class TestCase {
+ private final Throwable thrown;
+ private final SdkErrorType expectedType;
+
+ public TestCase(Throwable thrown, SdkErrorType expectedType) {
+ this.thrown = thrown;
+ this.expectedType = expectedType;
+ }
+ }
+
+ private static class TestServiceException extends SdkServiceException {
+ private final boolean throttling;
+
+ protected TestServiceException(BuilderImpl b) {
+ super(b);
+ this.throttling = b.throttling;
+ }
+
+ @Override
+ public boolean isThrottlingException() {
+ return throttling;
+ }
+
+ public static Builder builder() {
+ return new BuilderImpl();
+ }
+
+ public interface Builder extends SdkServiceException.Builder {
+ Builder throttling(Boolean throttling);
+
+ @Override
+ TestServiceException build();
+ }
+
+ public static class BuilderImpl extends SdkServiceException.BuilderImpl implements Builder {
+ private boolean throttling;
+
+ @Override
+ public boolean equalsBySdkFields(Object other) {
+ return super.equalsBySdkFields(other);
+ }
+
+ @Override
+ public Builder throttling(Boolean throttling) {
+ this.throttling = throttling;
+ return this;
+ }
+
+ @Override
+ public TestServiceException build() {
+ return new TestServiceException(this);
+ }
+ }
+ }
+}
diff --git a/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/waiters/WaiterExecutorTest.java b/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/waiters/WaiterExecutorTest.java
new file mode 100644
index 000000000000..2df65de46e0b
--- /dev/null
+++ b/core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/waiters/WaiterExecutorTest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 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 software.amazon.awssdk.core.internal.waiters;
+
+import java.util.Arrays;
+import java.util.concurrent.atomic.LongAdder;
+import org.junit.jupiter.api.Test;
+import org.testng.Assert;
+import software.amazon.awssdk.core.retry.backoff.BackoffStrategy;
+import software.amazon.awssdk.core.waiters.WaiterAcceptor;
+import software.amazon.awssdk.core.waiters.WaiterOverrideConfiguration;
+
+class WaiterExecutorTest {
+ @Test
+ void largeMaxAttempts() {
+
+ int expectedAttempts = 10_000;
+
+ WaiterOverrideConfiguration conf =
+ WaiterOverrideConfiguration.builder()
+ .maxAttempts(expectedAttempts)
+ .backoffStrategy(BackoffStrategy.none())
+ .build();
+
+ WaiterExecutor sut =
+ new WaiterExecutor<>(new WaiterConfiguration(conf),
+ Arrays.asList(
+ WaiterAcceptor.retryOnResponseAcceptor(c -> c < expectedAttempts),
+ WaiterAcceptor.successOnResponseAcceptor(c -> c == expectedAttempts)
+ ));
+
+ LongAdder attemptCounter = new LongAdder();
+ sut.execute(() -> {
+ attemptCounter.increment();
+ return attemptCounter.intValue();
+ });
+
+ Assert.assertEquals(attemptCounter.intValue(), expectedAttempts);
+ }
+}
\ No newline at end of file
diff --git a/docs/design/core/metrics/MetricsList.md b/docs/design/core/metrics/MetricsList.md
index 1046d913de46..7203e4a6e531 100644
--- a/docs/design/core/metrics/MetricsList.md
+++ b/docs/design/core/metrics/MetricsList.md
@@ -29,7 +29,8 @@ class within `sdk-core`.
| AwsExtendedRequestId | `String` | The extended request ID of the service request.|
| UnmarshallingDuration | `Duration` | The duration of time taken to unmarshall the HTTP response to an SDK response. |
| ServiceCallDuration | `Duration` | The duration of time taken to connect to the service (or acquire a connection from the connection pool), send the serialized request and receive the initial response (e.g. HTTP status code and headers). This DOES NOT include the time taken to read the entire response from the service. |
-| `RetryCount` | `Integer` | The number of retries that the SDK performed in the execution of the request. 0 implies that the request worked the first time, and no retries were attempted. |
+| RetryCount | `Integer` | The number of retries that the SDK performed in the execution of the request. 0 implies that the request worked the first time, and no retries were attempted. |
+| ErrorType | `String` | The general type or category of error that was encountered for a failed API call attempt. The following are possible values: `Throttling` - The service responded with a throttling error. `ServerError` - The service responded with an error other than throttling. `ConfiguredTimeout` - A client timeout occurred, either at the API call level, or API call attempt level. `IO` - An I/O error occurred. `Other` - Catch-all for other errors that don't fall into the above categories.|
## HTTP Metrics
diff --git a/http-client-spi/pom.xml b/http-client-spi/pom.xml
index b330c46241e1..0cb525bb39bd 100644
--- a/http-client-spi/pom.xml
+++ b/http-client-spi/pom.xml
@@ -22,7 +22,7 @@
aws-sdk-java-pom
software.amazon.awssdk
- 2.20.68-SNAPSHOT
+ 2.20.93-SNAPSHOT
http-client-spi
AWS Java SDK :: HTTP Client Interface
diff --git a/http-clients/apache-client/pom.xml b/http-clients/apache-client/pom.xml
index e89d2ea099aa..00ffd95911ff 100644
--- a/http-clients/apache-client/pom.xml
+++ b/http-clients/apache-client/pom.xml
@@ -21,7 +21,7 @@
http-clients
software.amazon.awssdk
- 2.20.68-SNAPSHOT
+ 2.20.93-SNAPSHOT
apache-client
diff --git a/http-clients/apache-client/src/main/java/software/amazon/awssdk/http/apache/internal/conn/IdleConnectionReaper.java b/http-clients/apache-client/src/main/java/software/amazon/awssdk/http/apache/internal/conn/IdleConnectionReaper.java
index c40d7671f86d..af46691e5a19 100644
--- a/http-clients/apache-client/src/main/java/software/amazon/awssdk/http/apache/internal/conn/IdleConnectionReaper.java
+++ b/http-clients/apache-client/src/main/java/software/amazon/awssdk/http/apache/internal/conn/IdleConnectionReaper.java
@@ -16,8 +16,9 @@
package software.amazon.awssdk.http.apache.internal.conn;
import java.time.Duration;
+import java.util.Collections;
import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
+import java.util.WeakHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
@@ -48,7 +49,7 @@ public final class IdleConnectionReaper {
private volatile ReaperTask reaperTask;
private IdleConnectionReaper() {
- this.connectionManagers = new ConcurrentHashMap<>();
+ this.connectionManagers = Collections.synchronizedMap(new WeakHashMap<>());
this.executorServiceSupplier = () -> {
ExecutorService e = Executors.newSingleThreadExecutor(r -> {
diff --git a/http-clients/aws-crt-client/pom.xml b/http-clients/aws-crt-client/pom.xml
index bd5423befad0..041e4c41d6d5 100644
--- a/http-clients/aws-crt-client/pom.xml
+++ b/http-clients/aws-crt-client/pom.xml
@@ -21,7 +21,7 @@
http-clients
software.amazon.awssdk
- 2.20.68-SNAPSHOT
+ 2.20.93-SNAPSHOT
4.0.0
diff --git a/http-clients/netty-nio-client/pom.xml b/http-clients/netty-nio-client/pom.xml
index 495e63a41356..c9ddeb1a60e3 100644
--- a/http-clients/netty-nio-client/pom.xml
+++ b/http-clients/netty-nio-client/pom.xml
@@ -20,7 +20,7 @@
http-clients
software.amazon.awssdk
- 2.20.68-SNAPSHOT
+ 2.20.93-SNAPSHOT
4.0.0
@@ -85,6 +85,15 @@
io.netty
netty-transport-classes-epoll
+
+ io.netty
+ netty-resolver
+
+
+ io.netty
+ netty-resolver-dns
+ true
+
diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/NettyNioAsyncHttpClient.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/NettyNioAsyncHttpClient.java
index 78a3fa80fa87..c12aeab10180 100644
--- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/NettyNioAsyncHttpClient.java
+++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/NettyNioAsyncHttpClient.java
@@ -103,6 +103,7 @@ private NettyNioAsyncHttpClient(DefaultBuilder builder, AttributeMap serviceDefa
.sdkEventLoopGroup(sdkEventLoopGroup)
.sslProvider(resolveSslProvider(builder))
.proxyConfiguration(builder.proxyConfiguration)
+ .useNonBlockingDnsResolver(builder.useNonBlockingDnsResolver)
.build();
}
@@ -475,6 +476,15 @@ public interface Builder extends SdkAsyncHttpClient.Builder http2ConfigurationBuilderConsumer);
+
+ /**
+ * Configure whether to use a non-blocking dns resolver or not. False by default, as netty's default dns resolver is
+ * blocking; it namely calls java.net.InetAddress.getByName.
+ *
+ * When enabled, a non-blocking dns resolver will be used instead, by modifying netty's bootstrap configuration.
+ * See https://netty.io/news/2016/05/26/4-1-0-Final.html
+ */
+ Builder useNonBlockingDnsResolver(Boolean useNonBlockingDnsResolver);
}
/**
@@ -492,6 +502,7 @@ private static final class DefaultBuilder implements Builder {
private Http2Configuration http2Configuration;
private SslProvider sslProvider;
private ProxyConfiguration proxyConfiguration;
+ private Boolean useNonBlockingDnsResolver;
private DefaultBuilder() {
}
@@ -716,6 +727,16 @@ public void setHttp2Configuration(Http2Configuration http2Configuration) {
http2Configuration(http2Configuration);
}
+ @Override
+ public Builder useNonBlockingDnsResolver(Boolean useNonBlockingDnsResolver) {
+ this.useNonBlockingDnsResolver = useNonBlockingDnsResolver;
+ return this;
+ }
+
+ public void setUseNonBlockingDnsResolver(Boolean useNonBlockingDnsResolver) {
+ useNonBlockingDnsResolver(useNonBlockingDnsResolver);
+ }
+
@Override
public SdkAsyncHttpClient buildWithDefaults(AttributeMap serviceDefaults) {
if (standardOptions.get(SdkHttpConfigurationOption.TLS_NEGOTIATION_TIMEOUT) == null) {
diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/SdkEventLoopGroup.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/SdkEventLoopGroup.java
index abb665f2c39a..254211e9303f 100644
--- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/SdkEventLoopGroup.java
+++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/SdkEventLoopGroup.java
@@ -19,11 +19,13 @@
import io.netty.channel.ChannelFactory;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.DatagramChannel;
+import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import java.util.Optional;
import java.util.concurrent.ThreadFactory;
import software.amazon.awssdk.annotations.SdkPublicApi;
-import software.amazon.awssdk.http.nio.netty.internal.utils.SocketChannelResolver;
+import software.amazon.awssdk.http.nio.netty.internal.utils.ChannelResolver;
import software.amazon.awssdk.utils.ThreadFactoryBuilder;
import software.amazon.awssdk.utils.Validate;
@@ -39,7 +41,8 @@
*
*
Using {@link #create(EventLoopGroup)} to provide a custom {@link EventLoopGroup}. {@link ChannelFactory} will
* be resolved based on the type of {@link EventLoopGroup} provided via
- * {@link SocketChannelResolver#resolveSocketChannelFactory(EventLoopGroup)}
+ * {@link ChannelResolver#resolveSocketChannelFactory(EventLoopGroup)} and
+ * {@link ChannelResolver#resolveDatagramChannelFactory(EventLoopGroup)}
*
*
* Using {@link #create(EventLoopGroup, ChannelFactory)} to provide a custom {@link EventLoopGroup} and
@@ -63,12 +66,14 @@ public final class SdkEventLoopGroup {
private final EventLoopGroup eventLoopGroup;
private final ChannelFactory extends Channel> channelFactory;
+ private final ChannelFactory extends DatagramChannel> datagramChannelFactory;
SdkEventLoopGroup(EventLoopGroup eventLoopGroup, ChannelFactory extends Channel> channelFactory) {
Validate.paramNotNull(eventLoopGroup, "eventLoopGroup");
Validate.paramNotNull(channelFactory, "channelFactory");
this.eventLoopGroup = eventLoopGroup;
this.channelFactory = channelFactory;
+ this.datagramChannelFactory = ChannelResolver.resolveDatagramChannelFactory(eventLoopGroup);
}
/**
@@ -76,7 +81,8 @@ public final class SdkEventLoopGroup {
*/
private SdkEventLoopGroup(DefaultBuilder builder) {
this.eventLoopGroup = resolveEventLoopGroup(builder);
- this.channelFactory = resolveChannelFactory();
+ this.channelFactory = resolveSocketChannelFactory(builder);
+ this.datagramChannelFactory = resolveDatagramChannelFactory(builder);
}
/**
@@ -93,6 +99,13 @@ public ChannelFactory extends Channel> channelFactory() {
return channelFactory;
}
+ /**
+ * @return the {@link ChannelFactory} for datagram channels to be used with Netty Http Client.
+ */
+ public ChannelFactory extends DatagramChannel> datagramChannelFactory() {
+ return datagramChannelFactory;
+ }
+
/**
* Creates a new instance of SdkEventLoopGroup with {@link EventLoopGroup} and {@link ChannelFactory}
* to be used with {@link NettyNioAsyncHttpClient}.
@@ -116,7 +129,7 @@ public static SdkEventLoopGroup create(EventLoopGroup eventLoopGroup, ChannelFac
* @return a new instance of SdkEventLoopGroup
*/
public static SdkEventLoopGroup create(EventLoopGroup eventLoopGroup) {
- return create(eventLoopGroup, SocketChannelResolver.resolveSocketChannelFactory(eventLoopGroup));
+ return create(eventLoopGroup, ChannelResolver.resolveSocketChannelFactory(eventLoopGroup));
}
public static Builder builder() {
@@ -141,11 +154,22 @@ private EventLoopGroup resolveEventLoopGroup(DefaultBuilder builder) {
}*/
}
- private ChannelFactory extends Channel> resolveChannelFactory() {
- // Currently we only support NioEventLoopGroup
+ private ChannelFactory extends Channel> resolveSocketChannelFactory(DefaultBuilder builder) {
+ return builder.channelFactory;
+ }
+
+ private ChannelFactory extends DatagramChannel> resolveDatagramChannelFactory(DefaultBuilder builder) {
+ return builder.datagramChannelFactory;
+ }
+
+ private static ChannelFactory extends Channel> defaultSocketChannelFactory() {
return NioSocketChannel::new;
}
+ private static ChannelFactory extends DatagramChannel> defaultDatagramChannelFactory() {
+ return NioDatagramChannel::new;
+ }
+
/**
* A builder for {@link SdkEventLoopGroup}.
*
@@ -172,6 +196,24 @@ public interface Builder {
*/
Builder threadFactory(ThreadFactory threadFactory);
+ /**
+ * {@link ChannelFactory} to create socket channels used by the {@link EventLoopGroup}. If not set,
+ * NioSocketChannel is used.
+ *
+ * @param channelFactory ChannelFactory to use.
+ * @return This builder for method chaining.
+ */
+ Builder channelFactory(ChannelFactory extends Channel> channelFactory);
+
+ /**
+ * {@link ChannelFactory} to create datagram channels used by the {@link EventLoopGroup}. If not set,
+ * NioDatagramChannel is used.
+ *
+ * @param datagramChannelFactory ChannelFactory to use.
+ * @return This builder for method chaining.
+ */
+ Builder datagramChannelFactory(ChannelFactory extends DatagramChannel> datagramChannelFactory);
+
SdkEventLoopGroup build();
}
@@ -179,6 +221,8 @@ private static final class DefaultBuilder implements Builder {
private Integer numberOfThreads;
private ThreadFactory threadFactory;
+ private ChannelFactory extends Channel> channelFactory = defaultSocketChannelFactory();
+ private ChannelFactory extends DatagramChannel> datagramChannelFactory = defaultDatagramChannelFactory();
private DefaultBuilder() {
}
@@ -203,6 +247,26 @@ public void setThreadFactory(ThreadFactory threadFactory) {
threadFactory(threadFactory);
}
+ @Override
+ public Builder channelFactory(ChannelFactory extends Channel> channelFactory) {
+ this.channelFactory = channelFactory;
+ return this;
+ }
+
+ public void setChannelFactory(ChannelFactory extends Channel> channelFactory) {
+ channelFactory(channelFactory);
+ }
+
+ @Override
+ public Builder datagramChannelFactory(ChannelFactory extends DatagramChannel> datagramChannelFactory) {
+ this.datagramChannelFactory = datagramChannelFactory;
+ return this;
+ }
+
+ public void setDatagramChannelFactory(ChannelFactory extends DatagramChannel> datagramChannelFactory) {
+ datagramChannelFactory(datagramChannelFactory);
+ }
+
@Override
public SdkEventLoopGroup build() {
return new SdkEventLoopGroup(this);
diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/AwaitCloseChannelPoolMap.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/AwaitCloseChannelPoolMap.java
index 1d55e1841aa2..fbd727239239 100644
--- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/AwaitCloseChannelPoolMap.java
+++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/AwaitCloseChannelPoolMap.java
@@ -83,6 +83,7 @@ public void channelCreated(Channel ch) throws Exception {
private final ProxyConfiguration proxyConfiguration;
private final BootstrapProvider bootstrapProvider;
private final SslContextProvider sslContextProvider;
+ private final Boolean useNonBlockingDnsResolver;
private AwaitCloseChannelPoolMap(Builder builder, Function createBootStrapProvider) {
this.configuration = builder.configuration;
@@ -94,6 +95,7 @@ private AwaitCloseChannelPoolMap(Builder builder, Function init(ChannelFactory extends DatagramChannel> datagramChannelFactory) {
+ try {
+ Class> addressResolver = ClassLoaderHelper.loadClass(getAddressResolverGroup(), false, (Class) null);
+ Class> dnsNameResolverBuilder = ClassLoaderHelper.loadClass(getDnsNameResolverBuilder(), false, (Class) null);
+
+ Object dnsResolverObj = dnsNameResolverBuilder.newInstance();
+ Method method = dnsResolverObj.getClass().getMethod("channelFactory", ChannelFactory.class);
+ method.invoke(dnsResolverObj, datagramChannelFactory);
+
+ Object e = addressResolver.getConstructor(dnsNameResolverBuilder).newInstance(dnsResolverObj);
+ return (AddressResolverGroup) e;
+ } catch (ClassNotFoundException e) {
+ throw new IllegalStateException("Cannot find module io.netty.resolver.dns "
+ + " To use netty non blocking dns," +
+ " the 'netty-resolver-dns' module from io.netty must be on the class path. ", e);
+ } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException | InstantiationException e) {
+ throw new IllegalStateException("Failed to create AddressResolverGroup", e);
+ }
+ }
+
+ private static String getAddressResolverGroup() {
+ return "io.netty.resolver.dns.DnsAddressResolverGroup";
+ }
+
+ private static String getDnsNameResolverBuilder() {
+ return "io.netty.resolver.dns.DnsNameResolverBuilder";
+ }
+}
diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/utils/ChannelResolver.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/utils/ChannelResolver.java
new file mode 100644
index 000000000000..8770d683a679
--- /dev/null
+++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/utils/ChannelResolver.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 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 software.amazon.awssdk.http.nio.netty.internal.utils;
+
+import static software.amazon.awssdk.utils.FunctionalUtils.invokeSafely;
+
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFactory;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.ReflectiveChannelFactory;
+import io.netty.channel.epoll.EpollDatagramChannel;
+import io.netty.channel.epoll.EpollEventLoopGroup;
+import io.netty.channel.epoll.EpollSocketChannel;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.DatagramChannel;
+import io.netty.channel.socket.nio.NioDatagramChannel;
+import io.netty.channel.socket.nio.NioSocketChannel;
+import java.util.HashMap;
+import java.util.Map;
+import software.amazon.awssdk.annotations.SdkInternalApi;
+import software.amazon.awssdk.http.nio.netty.internal.DelegatingEventLoopGroup;
+
+@SdkInternalApi
+public final class ChannelResolver {
+
+ private static final Map KNOWN_EL_GROUPS_SOCKET_CHANNELS = new HashMap<>();
+ private static final Map KNOWN_EL_GROUPS_DATAGRAM_CHANNELS = new HashMap<>();
+
+ static {
+ KNOWN_EL_GROUPS_SOCKET_CHANNELS.put("io.netty.channel.kqueue.KQueueEventLoopGroup",
+ "io.netty.channel.kqueue.KQueueSocketChannel");
+ KNOWN_EL_GROUPS_SOCKET_CHANNELS.put("io.netty.channel.oio.OioEventLoopGroup",
+ "io.netty.channel.socket.oio.OioSocketChannel");
+
+ KNOWN_EL_GROUPS_DATAGRAM_CHANNELS.put("io.netty.channel.kqueue.KQueueEventLoopGroup",
+ "io.netty.channel.kqueue.KQueueDatagramChannel");
+ KNOWN_EL_GROUPS_DATAGRAM_CHANNELS.put("io.netty.channel.oio.OioEventLoopGroup",
+ "io.netty.channel.socket.oio.OioDatagramChannel");
+ }
+
+ private ChannelResolver() {
+ }
+
+ /**
+ * Attempts to determine the {@link ChannelFactory} class that corresponds to the given
+ * event loop group.
+ *
+ * @param eventLoopGroup the event loop group to determine the {@link ChannelFactory} for
+ * @return A {@link ChannelFactory} instance for the given event loop group.
+ */
+ @SuppressWarnings("unchecked")
+ public static ChannelFactory extends Channel> resolveSocketChannelFactory(EventLoopGroup eventLoopGroup) {
+ if (eventLoopGroup instanceof DelegatingEventLoopGroup) {
+ return resolveSocketChannelFactory(((DelegatingEventLoopGroup) eventLoopGroup).getDelegate());
+ }
+
+ if (eventLoopGroup instanceof NioEventLoopGroup) {
+ return NioSocketChannel::new;
+ }
+ if (eventLoopGroup instanceof EpollEventLoopGroup) {
+ return EpollSocketChannel::new;
+ }
+
+ String socketFqcn = KNOWN_EL_GROUPS_SOCKET_CHANNELS.get(eventLoopGroup.getClass().getName());
+ if (socketFqcn == null) {
+ throw new IllegalArgumentException("Unknown event loop group : " + eventLoopGroup.getClass());
+ }
+
+ return invokeSafely(() -> new ReflectiveChannelFactory(Class.forName(socketFqcn)));
+ }
+
+ /**
+ * Attempts to determine the {@link ChannelFactory} class for datagram channels that corresponds to the given
+ * event loop group.
+ *
+ * @param eventLoopGroup the event loop group to determine the {@link ChannelFactory} for
+ * @return A {@link ChannelFactory} instance for the given event loop group.
+ */
+ @SuppressWarnings("unchecked")
+ public static ChannelFactory extends DatagramChannel> resolveDatagramChannelFactory(EventLoopGroup eventLoopGroup) {
+ if (eventLoopGroup instanceof DelegatingEventLoopGroup) {
+ return resolveDatagramChannelFactory(((DelegatingEventLoopGroup) eventLoopGroup).getDelegate());
+ }
+
+ if (eventLoopGroup instanceof NioEventLoopGroup) {
+ return NioDatagramChannel::new;
+ }
+ if (eventLoopGroup instanceof EpollEventLoopGroup) {
+ return EpollDatagramChannel::new;
+ }
+
+ String datagramFqcn = KNOWN_EL_GROUPS_DATAGRAM_CHANNELS.get(eventLoopGroup.getClass().getName());
+ if (datagramFqcn == null) {
+ throw new IllegalArgumentException("Unknown event loop group : " + eventLoopGroup.getClass());
+ }
+
+ return invokeSafely(() -> new ReflectiveChannelFactory(Class.forName(datagramFqcn)));
+ }
+}
diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/utils/SocketChannelResolver.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/utils/SocketChannelResolver.java
deleted file mode 100644
index 1d80dad5850f..000000000000
--- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/utils/SocketChannelResolver.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright 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 software.amazon.awssdk.http.nio.netty.internal.utils;
-
-import static software.amazon.awssdk.utils.FunctionalUtils.invokeSafely;
-
-import io.netty.channel.Channel;
-import io.netty.channel.ChannelFactory;
-import io.netty.channel.EventLoopGroup;
-import io.netty.channel.ReflectiveChannelFactory;
-import io.netty.channel.epoll.EpollEventLoopGroup;
-import io.netty.channel.epoll.EpollSocketChannel;
-import io.netty.channel.nio.NioEventLoopGroup;
-import io.netty.channel.socket.nio.NioSocketChannel;
-import java.util.HashMap;
-import java.util.Map;
-import software.amazon.awssdk.annotations.SdkInternalApi;
-import software.amazon.awssdk.http.nio.netty.internal.DelegatingEventLoopGroup;
-
-@SdkInternalApi
-public final class SocketChannelResolver {
-
- private static final Map KNOWN_EL_GROUPS = new HashMap<>();
-
- static {
- KNOWN_EL_GROUPS.put("io.netty.channel.kqueue.KQueueEventLoopGroup", "io.netty.channel.kqueue.KQueueSocketChannel");
- KNOWN_EL_GROUPS.put("io.netty.channel.oio.OioEventLoopGroup", "io.netty.channel.socket.oio.OioSocketChannel");
- }
-
- private SocketChannelResolver() {
- }
-
- /**
- * Attempts to determine the {@link ChannelFactory} class that corresponds to the given
- * event loop group.
- *
- * @param eventLoopGroup the event loop group to determine the {@link ChannelFactory} for
- * @return A {@link ChannelFactory} instance for the given event loop group.
- */
- @SuppressWarnings("unchecked")
- public static ChannelFactory extends Channel> resolveSocketChannelFactory(EventLoopGroup eventLoopGroup) {
- if (eventLoopGroup instanceof DelegatingEventLoopGroup) {
- return resolveSocketChannelFactory(((DelegatingEventLoopGroup) eventLoopGroup).getDelegate());
- }
-
- if (eventLoopGroup instanceof NioEventLoopGroup) {
- return NioSocketChannel::new;
- }
- if (eventLoopGroup instanceof EpollEventLoopGroup) {
- return EpollSocketChannel::new;
- }
-
- String socketFqcn = KNOWN_EL_GROUPS.get(eventLoopGroup.getClass().getName());
- if (socketFqcn == null) {
- throw new IllegalArgumentException("Unknown event loop group : " + eventLoopGroup.getClass());
- }
-
- return invokeSafely(() -> new ReflectiveChannelFactory(Class.forName(socketFqcn)));
- }
-}
diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/NettyClientTlsAuthTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/NettyClientTlsAuthTest.java
index dc7c408c3c9f..f35c0914609d 100644
--- a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/NettyClientTlsAuthTest.java
+++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/NettyClientTlsAuthTest.java
@@ -39,6 +39,7 @@
import software.amazon.awssdk.http.EmptyPublisher;
import software.amazon.awssdk.http.FileStoreTlsKeyManagersProvider;
import software.amazon.awssdk.http.HttpTestUtils;
+import software.amazon.awssdk.http.SdkHttpConfigurationOption;
import software.amazon.awssdk.http.SdkHttpFullRequest;
import software.amazon.awssdk.http.SdkHttpMethod;
import software.amazon.awssdk.http.TlsKeyManagersProvider;
@@ -185,6 +186,24 @@ public void nonProxy_noKeyManagerGiven_shouldThrowException() {
.hasRootCauseInstanceOf(SSLException.class);
}
+ @Test
+ public void builderUsesProvidedKeyManagersProviderNonBlockingDns() {
+ TlsKeyManagersProvider mockKeyManagersProvider = mock(TlsKeyManagersProvider.class);
+ netty = NettyNioAsyncHttpClient.builder()
+ .useNonBlockingDnsResolver(true)
+ .proxyConfiguration(proxyCfg)
+ .tlsKeyManagersProvider(mockKeyManagersProvider)
+ .buildWithDefaults(AttributeMap.builder()
+ .put(TRUST_ALL_CERTIFICATES, true)
+ .build());
+
+ try {
+ sendRequest(netty, new RecordingResponseHandler());
+ } catch (Exception ignored) {
+ }
+ verify(mockKeyManagersProvider).keyManagers();
+ }
+
private void sendRequest(SdkAsyncHttpClient client, SdkAsyncHttpResponseHandler responseHandler) {
AsyncExecuteRequest req = AsyncExecuteRequest.builder()
.request(testSdkRequest())
diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/NettyNioAsyncHttpClientNonBlockingDnsTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/NettyNioAsyncHttpClientNonBlockingDnsTest.java
new file mode 100644
index 000000000000..9535c41c2b0a
--- /dev/null
+++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/NettyNioAsyncHttpClientNonBlockingDnsTest.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright 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 software.amazon.awssdk.http.nio.netty;
+
+import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
+import static com.github.tomakehurst.wiremock.client.WireMock.any;
+import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
+import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor;
+import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
+import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
+import static com.github.tomakehurst.wiremock.client.WireMock.verify;
+import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
+import static java.util.Collections.singletonMap;
+import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic;
+import static org.apache.commons.lang3.StringUtils.reverse;
+import static org.assertj.core.api.Assertions.assertThat;
+import static software.amazon.awssdk.http.nio.netty.NettyNioAsyncHttpClientTestUtils.assertCanReceiveBasicRequest;
+import static software.amazon.awssdk.http.nio.netty.NettyNioAsyncHttpClientTestUtils.createProvider;
+import static software.amazon.awssdk.http.nio.netty.NettyNioAsyncHttpClientTestUtils.createRequest;
+import static software.amazon.awssdk.http.nio.netty.NettyNioAsyncHttpClientTestUtils.makeSimpleRequest;
+
+import com.github.tomakehurst.wiremock.junit.WireMockRule;
+import java.io.IOException;
+import java.net.URI;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import org.assertj.core.api.Condition;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnitRunner;
+import software.amazon.awssdk.http.SdkHttpConfigurationOption;
+import software.amazon.awssdk.http.SdkHttpFullRequest;
+import software.amazon.awssdk.http.SdkHttpMethod;
+import software.amazon.awssdk.http.SdkHttpRequest;
+import software.amazon.awssdk.http.async.AsyncExecuteRequest;
+import software.amazon.awssdk.http.async.SdkAsyncHttpClient;
+import software.amazon.awssdk.utils.AttributeMap;
+
+@RunWith(MockitoJUnitRunner.class)
+public class NettyNioAsyncHttpClientNonBlockingDnsTest {
+
+ private final RecordingNetworkTrafficListener wiremockTrafficListener = new RecordingNetworkTrafficListener();
+
+ private static final SdkAsyncHttpClient client = NettyNioAsyncHttpClient.builder()
+ .useNonBlockingDnsResolver(true)
+ .buildWithDefaults(
+ AttributeMap.builder()
+ .put(SdkHttpConfigurationOption.TRUST_ALL_CERTIFICATES, true)
+ .build());
+ @Rule
+ public WireMockRule mockServer = new WireMockRule(wireMockConfig()
+ .dynamicPort()
+ .dynamicHttpsPort()
+ .networkTrafficListener(wiremockTrafficListener));
+
+ @Before
+ public void methodSetup() {
+ wiremockTrafficListener.reset();
+ }
+
+ @AfterClass
+ public static void tearDown() throws Exception {
+ client.close();
+ }
+
+ @Test
+ public void canSendContentAndGetThatContentBackNonBlockingDns() throws Exception {
+ String body = randomAlphabetic(50);
+ stubFor(any(urlEqualTo("/echo?reversed=true"))
+ .withRequestBody(equalTo(body))
+ .willReturn(aResponse().withBody(reverse(body))));
+ URI uri = URI.create("http://localhost:" + mockServer.port());
+
+ SdkHttpRequest request = createRequest(uri, "/echo", body, SdkHttpMethod.POST, singletonMap("reversed", "true"));
+
+ RecordingResponseHandler recorder = new RecordingResponseHandler();
+
+ client.execute(AsyncExecuteRequest.builder().request(request).requestContentPublisher(createProvider(body)).responseHandler(recorder).build());
+
+ recorder.completeFuture.get(5, TimeUnit.SECONDS);
+
+ verify(1, postRequestedFor(urlEqualTo("/echo?reversed=true")));
+
+ assertThat(recorder.fullResponseAsString()).isEqualTo(reverse(body));
+ }
+
+ @Test
+ public void defaultThreadFactoryUsesHelpfulName() throws Exception {
+ // Make a request to ensure a thread is primed
+ makeSimpleRequest(client, mockServer);
+
+ String expectedPattern = "aws-java-sdk-NettyEventLoop-\\d+-\\d+";
+ assertThat(Thread.getAllStackTraces().keySet())
+ .areAtLeast(1, new Condition<>(t -> t.getName().matches(expectedPattern),
+ "Matches default thread pattern: `%s`", expectedPattern));
+ }
+
+ @Test
+ public void canMakeBasicRequestOverHttp() throws Exception {
+ String smallBody = randomAlphabetic(10);
+ URI uri = URI.create("http://localhost:" + mockServer.port());
+
+ assertCanReceiveBasicRequest(client, uri, smallBody);
+ }
+
+ @Test
+ public void canMakeBasicRequestOverHttps() throws Exception {
+ String smallBody = randomAlphabetic(10);
+ URI uri = URI.create("https://localhost:" + mockServer.httpsPort());
+
+ assertCanReceiveBasicRequest(client, uri, smallBody);
+ }
+
+ @Test
+ public void canHandleLargerPayloadsOverHttp() throws Exception {
+ String largishBody = randomAlphabetic(25000);
+
+ URI uri = URI.create("http://localhost:" + mockServer.port());
+
+ assertCanReceiveBasicRequest(client, uri, largishBody);
+ }
+
+ @Test
+ public void canHandleLargerPayloadsOverHttps() throws Exception {
+ String largishBody = randomAlphabetic(25000);
+
+ URI uri = URI.create("https://localhost:" + mockServer.httpsPort());
+
+ assertCanReceiveBasicRequest(client, uri, largishBody);
+ }
+
+ @Test
+ public void requestContentOnlyEqualToContentLengthHeaderFromProvider() throws InterruptedException, ExecutionException, TimeoutException, IOException {
+ final String content = randomAlphabetic(32);
+ final String streamContent = content + reverse(content);
+ stubFor(any(urlEqualTo("/echo?reversed=true"))
+ .withRequestBody(equalTo(content))
+ .willReturn(aResponse().withBody(reverse(content))));
+ URI uri = URI.create("http://localhost:" + mockServer.port());
+
+ SdkHttpFullRequest request = createRequest(uri, "/echo", streamContent, SdkHttpMethod.POST, singletonMap("reversed", "true"));
+ request = request.toBuilder().putHeader("Content-Length", Integer.toString(content.length())).build();
+ RecordingResponseHandler recorder = new RecordingResponseHandler();
+
+ client.execute(AsyncExecuteRequest.builder().request(request).requestContentPublisher(createProvider(streamContent)).responseHandler(recorder).build());
+
+ recorder.completeFuture.get(5, TimeUnit.SECONDS);
+
+ // HTTP servers will stop processing the request as soon as it reads
+ // bytes equal to 'Content-Length' so we need to inspect the raw
+ // traffic to ensure that there wasn't anything after that.
+ assertThat(wiremockTrafficListener.requests().toString()).endsWith(content);
+ }
+}
diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/NettyNioAsyncHttpClientTestUtils.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/NettyNioAsyncHttpClientTestUtils.java
new file mode 100644
index 000000000000..04f9a906ee04
--- /dev/null
+++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/NettyNioAsyncHttpClientTestUtils.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright 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 software.amazon.awssdk.http.nio.netty;
+
+import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
+import static com.github.tomakehurst.wiremock.client.WireMock.any;
+import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor;
+import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
+import static com.github.tomakehurst.wiremock.client.WireMock.urlMatching;
+import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo;
+import static com.github.tomakehurst.wiremock.client.WireMock.verify;
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.util.Collections.emptyMap;
+import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic;
+import static org.apache.commons.lang3.StringUtils.isBlank;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import com.github.tomakehurst.wiremock.WireMockServer;
+import java.net.URI;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Stream;
+import org.reactivestreams.Subscriber;
+import org.reactivestreams.Subscription;
+import software.amazon.awssdk.http.SdkHttpFullRequest;
+import software.amazon.awssdk.http.SdkHttpMethod;
+import software.amazon.awssdk.http.SdkHttpRequest;
+import software.amazon.awssdk.http.async.AsyncExecuteRequest;
+import software.amazon.awssdk.http.async.SdkAsyncHttpClient;
+import software.amazon.awssdk.http.async.SdkHttpContentPublisher;
+
+public class NettyNioAsyncHttpClientTestUtils {
+
+ /**
+ * Make a simple async request and wait for it to fiish.
+ *
+ * @param client Client to make request with.
+ */
+ public static void makeSimpleRequest(SdkAsyncHttpClient client, WireMockServer mockServer) throws Exception {
+ String body = randomAlphabetic(10);
+ URI uri = URI.create("http://localhost:" + mockServer.port());
+ stubFor(any(urlPathEqualTo("/")).willReturn(aResponse().withBody(body)));
+ SdkHttpRequest request = createRequest(uri);
+ RecordingResponseHandler recorder = new RecordingResponseHandler();
+ client.execute(AsyncExecuteRequest.builder().request(request).requestContentPublisher(createProvider("")).responseHandler(recorder).build());
+ recorder.completeFuture.get(5, TimeUnit.SECONDS);
+ }
+
+ public static SdkHttpContentPublisher createProvider(String body) {
+ Stream chunks = splitStringBySize(body).stream()
+ .map(chunk -> ByteBuffer.wrap(chunk.getBytes(UTF_8)));
+ return new SdkHttpContentPublisher() {
+
+ @Override
+ public Optional contentLength() {
+ return Optional.of(Long.valueOf(body.length()));
+ }
+
+ @Override
+ public void subscribe(Subscriber super ByteBuffer> s) {
+ s.onSubscribe(new Subscription() {
+ @Override
+ public void request(long n) {
+ chunks.forEach(s::onNext);
+ s.onComplete();
+ }
+
+ @Override
+ public void cancel() {
+
+ }
+ });
+ }
+ };
+ }
+
+ public static SdkHttpFullRequest createRequest(URI uri) {
+ return createRequest(uri, "/", null, SdkHttpMethod.GET, emptyMap());
+ }
+
+ public static SdkHttpFullRequest createRequest(URI uri,
+ String resourcePath,
+ String body,
+ SdkHttpMethod method,
+ Map params) {
+ String contentLength = body == null ? null : String.valueOf(body.getBytes(UTF_8).length);
+ return SdkHttpFullRequest.builder()
+ .uri(uri)
+ .method(method)
+ .encodedPath(resourcePath)
+ .applyMutation(b -> params.forEach(b::putRawQueryParameter))
+ .applyMutation(b -> {
+ b.putHeader("Host", uri.getHost());
+ if (contentLength != null) {
+ b.putHeader("Content-Length", contentLength);
+ }
+ }).build();
+ }
+
+ public static void assertCanReceiveBasicRequest(SdkAsyncHttpClient client, URI uri, String body) throws Exception {
+ stubFor(any(urlPathEqualTo("/")).willReturn(aResponse().withHeader("Some-Header", "With Value").withBody(body)));
+
+ SdkHttpRequest request = createRequest(uri);
+
+ RecordingResponseHandler recorder = new RecordingResponseHandler();
+ client.execute(AsyncExecuteRequest.builder().request(request).requestContentPublisher(createProvider("")).responseHandler(recorder).build());
+
+ recorder.completeFuture.get(5, TimeUnit.SECONDS);
+
+ assertThat(recorder.responses).hasOnlyOneElementSatisfying(
+ headerResponse -> {
+ assertThat(headerResponse.headers()).containsKey("Some-Header");
+ assertThat(headerResponse.statusCode()).isEqualTo(200);
+ });
+
+ assertThat(recorder.fullResponseAsString()).isEqualTo(body);
+ verify(1, getRequestedFor(urlMatching("/")));
+ }
+
+ private static Collection splitStringBySize(String str) {
+ if (isBlank(str)) {
+ return Collections.emptyList();
+ }
+ ArrayList split = new ArrayList<>();
+ for (int i = 0; i <= str.length() / 1000; i++) {
+ split.add(str.substring(i * 1000, Math.min((i + 1) * 1000, str.length())));
+ }
+ return split;
+ }
+}
diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/NettyNioAsyncHttpClientWireMockTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/NettyNioAsyncHttpClientWireMockTest.java
index 9a1121e201f5..116119d36ea5 100644
--- a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/NettyNioAsyncHttpClientWireMockTest.java
+++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/NettyNioAsyncHttpClientWireMockTest.java
@@ -18,19 +18,14 @@
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.any;
import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
-import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor;
import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor;
import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
-import static com.github.tomakehurst.wiremock.client.WireMock.urlMatching;
import static com.github.tomakehurst.wiremock.client.WireMock.urlPathEqualTo;
import static com.github.tomakehurst.wiremock.client.WireMock.verify;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
-import static java.nio.charset.StandardCharsets.UTF_8;
-import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonMap;
import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic;
-import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.reverse;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
@@ -40,6 +35,10 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.when;
+import static software.amazon.awssdk.http.nio.netty.NettyNioAsyncHttpClientTestUtils.assertCanReceiveBasicRequest;
+import static software.amazon.awssdk.http.nio.netty.NettyNioAsyncHttpClientTestUtils.createProvider;
+import static software.amazon.awssdk.http.nio.netty.NettyNioAsyncHttpClientTestUtils.createRequest;
+import static software.amazon.awssdk.http.nio.netty.NettyNioAsyncHttpClientTestUtils.makeSimpleRequest;
import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.http.Fault;
@@ -49,25 +48,22 @@
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.DatagramChannel;
+import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.ssl.SslProvider;
import io.netty.util.AttributeKey;
import java.io.IOException;
import java.net.URI;
-import java.nio.ByteBuffer;
import java.time.Duration;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.Collections;
import java.util.List;
-import java.util.Map;
-import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
-import java.util.stream.Stream;
import javax.net.ssl.TrustManagerFactory;
import org.assertj.core.api.Condition;
import org.junit.AfterClass;
@@ -78,8 +74,6 @@
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import org.mockito.stubbing.Answer;
-import org.reactivestreams.Subscriber;
-import org.reactivestreams.Subscription;
import software.amazon.awssdk.http.HttpMetric;
import software.amazon.awssdk.http.HttpTestUtils;
import software.amazon.awssdk.http.SdkHttpConfigurationOption;
@@ -88,7 +82,6 @@
import software.amazon.awssdk.http.SdkHttpRequest;
import software.amazon.awssdk.http.async.AsyncExecuteRequest;
import software.amazon.awssdk.http.async.SdkAsyncHttpClient;
-import software.amazon.awssdk.http.async.SdkHttpContentPublisher;
import software.amazon.awssdk.http.nio.netty.internal.NettyConfiguration;
import software.amazon.awssdk.http.nio.netty.internal.SdkChannelPool;
import software.amazon.awssdk.http.nio.netty.internal.SdkChannelPoolMap;
@@ -183,7 +176,8 @@ public void invalidMaxPendingConnectionAcquireConfig_shouldPropagateException()
.maxConcurrency(1)
.maxPendingConnectionAcquires(0)
.build()) {
- assertThatThrownBy(() -> makeSimpleRequest(customClient)).hasMessageContaining("java.lang.IllegalArgumentException: maxPendingAcquires: 0 (expected: >= 1)");
+ assertThatThrownBy(() -> makeSimpleRequest(customClient, mockServer)).hasMessageContaining("java.lang"
+ + ".IllegalArgumentException: maxPendingAcquires: 0 (expected: >= 1)");
}
}
@@ -196,7 +190,7 @@ public void customFactoryIsUsed() throws Exception {
.threadFactory(threadFactory))
.build();
- makeSimpleRequest(customClient);
+ makeSimpleRequest(customClient, mockServer);
customClient.close();
Mockito.verify(threadFactory, atLeastOnce()).newThread(Mockito.any());
@@ -208,7 +202,7 @@ public void openSslBeingUsed() throws Exception {
NettyNioAsyncHttpClient.builder()
.sslProvider(SslProvider.OPENSSL)
.build()) {
- makeSimpleRequest(customClient);
+ makeSimpleRequest(customClient, mockServer);
}
}
@@ -218,7 +212,7 @@ public void defaultJdkSslProvider() throws Exception {
NettyNioAsyncHttpClient.builder()
.sslProvider(SslProvider.JDK)
.build()) {
- makeSimpleRequest(customClient);
+ makeSimpleRequest(customClient, mockServer);
customClient.close();
}
}
@@ -226,7 +220,7 @@ public void defaultJdkSslProvider() throws Exception {
@Test
public void defaultThreadFactoryUsesHelpfulName() throws Exception {
// Make a request to ensure a thread is primed
- makeSimpleRequest(client);
+ makeSimpleRequest(client, mockServer);
String expectedPattern = "aws-java-sdk-NettyEventLoop-\\d+-\\d+";
assertThat(Thread.getAllStackTraces().keySet())
@@ -247,7 +241,7 @@ public void customThreadCountIsRespected() throws Exception {
// Have to make enough requests to prime the threads
for (int i = 0; i < threadCount + 1; i++) {
- makeSimpleRequest(customClient);
+ makeSimpleRequest(customClient, mockServer);
}
customClient.close();
@@ -267,7 +261,7 @@ public void customEventLoopGroup_NotClosedWhenClientIsClosed() throws Exception
.eventLoopGroup(SdkEventLoopGroup.create(eventLoopGroup, NioSocketChannel::new))
.build();
- makeSimpleRequest(customClient);
+ makeSimpleRequest(customClient, mockServer);
customClient.close();
Mockito.verify(threadFactory, atLeastOnce()).newThread(Mockito.any());
@@ -287,7 +281,7 @@ public void customChannelFactoryIsUsed() throws Exception {
.eventLoopGroup(SdkEventLoopGroup.create(customEventLoopGroup, channelFactory))
.build();
- makeSimpleRequest(customClient);
+ makeSimpleRequest(customClient, mockServer);
customClient.close();
Mockito.verify(channelFactory, atLeastOnce()).newChannel();
@@ -335,7 +329,7 @@ public void responseConnectionReused_shouldReleaseChannel() throws Exception {
.maxConcurrency(1)
.build();
- makeSimpleRequest(customClient);
+ makeSimpleRequest(customClient, mockServer);
verifyChannelRelease(channel);
assertThat(channel.isShutdown()).isFalse();
@@ -446,27 +440,12 @@ public void builderUsesProvidedTrustManagersProvider() throws Exception {
}
}
- /**
- * Make a simple async request and wait for it to fiish.
- *
- * @param client Client to make request with.
- */
- private void makeSimpleRequest(SdkAsyncHttpClient client) throws Exception {
- String body = randomAlphabetic(10);
- URI uri = URI.create("http://localhost:" + mockServer.port());
- stubFor(any(urlPathEqualTo("/")).willReturn(aResponse().withBody(body)));
- SdkHttpRequest request = createRequest(uri);
- RecordingResponseHandler recorder = new RecordingResponseHandler();
- client.execute(AsyncExecuteRequest.builder().request(request).requestContentPublisher(createProvider("")).responseHandler(recorder).build());
- recorder.completeFuture.get(5, TimeUnit.SECONDS);
- }
-
@Test
public void canMakeBasicRequestOverHttp() throws Exception {
String smallBody = randomAlphabetic(10);
URI uri = URI.create("http://localhost:" + mockServer.port());
- assertCanReceiveBasicRequest(uri, smallBody);
+ assertCanReceiveBasicRequest(client, uri, smallBody);
}
@Test
@@ -474,7 +453,7 @@ public void canMakeBasicRequestOverHttps() throws Exception {
String smallBody = randomAlphabetic(10);
URI uri = URI.create("https://localhost:" + mockServer.httpsPort());
- assertCanReceiveBasicRequest(uri, smallBody);
+ assertCanReceiveBasicRequest(client, uri, smallBody);
}
@Test
@@ -483,7 +462,7 @@ public void canHandleLargerPayloadsOverHttp() throws Exception {
URI uri = URI.create("http://localhost:" + mockServer.port());
- assertCanReceiveBasicRequest(uri, largishBody);
+ assertCanReceiveBasicRequest(client, uri, largishBody);
}
@Test
@@ -492,7 +471,7 @@ public void canHandleLargerPayloadsOverHttps() throws Exception {
URI uri = URI.create("https://localhost:" + mockServer.httpsPort());
- assertCanReceiveBasicRequest(uri, largishBody);
+ assertCanReceiveBasicRequest(client, uri, largishBody);
}
@Test
@@ -579,88 +558,6 @@ public ChannelFuture close() {
assertThat(channelClosedFuture.get(5, TimeUnit.SECONDS)).isTrue();
}
- private void assertCanReceiveBasicRequest(URI uri, String body) throws Exception {
- stubFor(any(urlPathEqualTo("/")).willReturn(aResponse().withHeader("Some-Header", "With Value").withBody(body)));
-
- SdkHttpRequest request = createRequest(uri);
-
- RecordingResponseHandler recorder = new RecordingResponseHandler();
- client.execute(AsyncExecuteRequest.builder().request(request).requestContentPublisher(createProvider("")).responseHandler(recorder).build());
-
- recorder.completeFuture.get(5, TimeUnit.SECONDS);
-
- assertThat(recorder.responses).hasOnlyOneElementSatisfying(
- headerResponse -> {
- assertThat(headerResponse.headers()).containsKey("Some-Header");
- assertThat(headerResponse.statusCode()).isEqualTo(200);
- });
-
- assertThat(recorder.fullResponseAsString()).isEqualTo(body);
- verify(1, getRequestedFor(urlMatching("/")));
- }
-
- private SdkHttpContentPublisher createProvider(String body) {
- Stream chunks = splitStringBySize(body).stream()
- .map(chunk -> ByteBuffer.wrap(chunk.getBytes(UTF_8)));
- return new SdkHttpContentPublisher() {
-
- @Override
- public Optional contentLength() {
- return Optional.of(Long.valueOf(body.length()));
- }
-
- @Override
- public void subscribe(Subscriber super ByteBuffer> s) {
- s.onSubscribe(new Subscription() {
- @Override
- public void request(long n) {
- chunks.forEach(s::onNext);
- s.onComplete();
- }
-
- @Override
- public void cancel() {
-
- }
- });
- }
- };
- }
-
- private SdkHttpFullRequest createRequest(URI uri) {
- return createRequest(uri, "/", null, SdkHttpMethod.GET, emptyMap());
- }
-
- private SdkHttpFullRequest createRequest(URI uri,
- String resourcePath,
- String body,
- SdkHttpMethod method,
- Map params) {
- String contentLength = body == null ? null : String.valueOf(body.getBytes(UTF_8).length);
- return SdkHttpFullRequest.builder()
- .uri(uri)
- .method(method)
- .encodedPath(resourcePath)
- .applyMutation(b -> params.forEach(b::putRawQueryParameter))
- .applyMutation(b -> {
- b.putHeader("Host", uri.getHost());
- if (contentLength != null) {
- b.putHeader("Content-Length", contentLength);
- }
- }).build();
- }
-
- private static Collection splitStringBySize(String str) {
- if (isBlank(str)) {
- return Collections.emptyList();
- }
- ArrayList split = new ArrayList<>();
- for (int i = 0; i <= str.length() / 1000; i++) {
- split.add(str.substring(i * 1000, Math.min((i + 1) * 1000, str.length())));
- }
- return split;
- }
-
// Needs to be a non-anon class in order to spy
public static class CustomThreadFactory implements ThreadFactory {
@Override
@@ -719,7 +616,7 @@ public void createNettyClient_ReadWriteTimeoutCanBeZero() throws Exception {
.writeTimeout(Duration.ZERO)
.build();
- makeSimpleRequest(customClient);
+ makeSimpleRequest(customClient, mockServer);
customClient.close();
}
diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/ProxyWireMockTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/ProxyWireMockTest.java
index f797a760fdf7..438d65e1f9fc 100644
--- a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/ProxyWireMockTest.java
+++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/ProxyWireMockTest.java
@@ -126,6 +126,30 @@ public void proxyConfigured_hostInNonProxySet_doesNotConnect() {
assertThat(responseHandler.fullResponseAsString()).isEqualTo("hello");
}
+ @Test
+ public void proxyConfigured_hostInNonProxySet_nonBlockingDns_doesNotConnect() {
+ RecordingResponseHandler responseHandler = new RecordingResponseHandler();
+ AsyncExecuteRequest req = AsyncExecuteRequest.builder()
+ .request(testSdkRequest())
+ .responseHandler(responseHandler)
+ .requestContentPublisher(new EmptyPublisher())
+ .build();
+
+ ProxyConfiguration cfg = proxyCfg.toBuilder()
+ .nonProxyHosts(Stream.of("localhost").collect(Collectors.toSet()))
+ .build();
+
+ client = NettyNioAsyncHttpClient.builder()
+ .proxyConfiguration(cfg)
+ .useNonBlockingDnsResolver(true)
+ .build();
+
+ client.execute(req).join();
+
+ responseHandler.completeFuture.join();
+ assertThat(responseHandler.fullResponseAsString()).isEqualTo("hello");
+ }
+
private SdkHttpFullRequest testSdkRequest() {
return SdkHttpFullRequest.builder()
.method(SdkHttpMethod.GET)
diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/SdkEventLoopGroupTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/SdkEventLoopGroupTest.java
index a3ae76469359..bb2598345cff 100644
--- a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/SdkEventLoopGroupTest.java
+++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/SdkEventLoopGroupTest.java
@@ -18,8 +18,15 @@
import static org.assertj.core.api.Assertions.assertThat;
import io.netty.channel.DefaultEventLoopGroup;
+import io.netty.channel.epoll.EpollDatagramChannel;
+import io.netty.channel.epoll.EpollEventLoopGroup;
+import io.netty.channel.epoll.EpollSocketChannel;
import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.oio.OioEventLoopGroup;
+import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
+import io.netty.channel.socket.oio.OioDatagramChannel;
+import io.netty.channel.socket.oio.OioSocketChannel;
import org.junit.Test;
public class SdkEventLoopGroupTest {
@@ -28,13 +35,24 @@ public class SdkEventLoopGroupTest {
public void creatingUsingBuilder() {
SdkEventLoopGroup sdkEventLoopGroup = SdkEventLoopGroup.builder().numberOfThreads(1).build();
assertThat(sdkEventLoopGroup.channelFactory()).isNotNull();
+ assertThat(sdkEventLoopGroup.datagramChannelFactory()).isNotNull();
assertThat(sdkEventLoopGroup.eventLoopGroup()).isNotNull();
}
@Test
- public void creatingUsingStaticMethod() {
+ public void creatingUsingStaticMethod_A() {
SdkEventLoopGroup sdkEventLoopGroup = SdkEventLoopGroup.create(new NioEventLoopGroup(), NioSocketChannel::new);
assertThat(sdkEventLoopGroup.channelFactory()).isNotNull();
+ assertThat(sdkEventLoopGroup.datagramChannelFactory().newChannel()).isInstanceOf(NioDatagramChannel.class);
+ assertThat(sdkEventLoopGroup.eventLoopGroup()).isNotNull();
+ }
+
+ @Test
+ public void creatingUsingStaticMethod_B() {
+ SdkEventLoopGroup sdkEventLoopGroup = SdkEventLoopGroup.create(new OioEventLoopGroup(), OioSocketChannel::new);
+ assertThat(sdkEventLoopGroup.channelFactory()).isNotNull();
+ assertThat(sdkEventLoopGroup.datagramChannelFactory()).isNotNull();
+ assertThat(sdkEventLoopGroup.datagramChannelFactory().newChannel()).isInstanceOf(OioDatagramChannel.class);
assertThat(sdkEventLoopGroup.eventLoopGroup()).isNotNull();
}
@@ -43,6 +61,7 @@ public void notProvidingChannelFactory_channelFactoryResolved() {
SdkEventLoopGroup sdkEventLoopGroup = SdkEventLoopGroup.create(new NioEventLoopGroup());
assertThat(sdkEventLoopGroup.channelFactory()).isNotNull();
+ assertThat(sdkEventLoopGroup.datagramChannelFactory().newChannel()).isInstanceOf(NioDatagramChannel.class);
}
@Test(expected = IllegalArgumentException.class)
diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/AwaitCloseChannelPoolMapTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/AwaitCloseChannelPoolMapTest.java
index 3b72f71be4db..17289d1ca3b3 100644
--- a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/AwaitCloseChannelPoolMapTest.java
+++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/AwaitCloseChannelPoolMapTest.java
@@ -118,7 +118,7 @@ public void get_callsInjectedBootstrapProviderCorrectly() {
channelPoolMap = new AwaitCloseChannelPoolMap(builder, null, bootstrapProvider);
channelPoolMap.get(targetUri);
- verify(bootstrapProvider).createBootstrap("some-awesome-service-1234.amazonaws.com", 8080);
+ verify(bootstrapProvider).createBootstrap("some-awesome-service-1234.amazonaws.com", 8080, null);
}
@Test
@@ -151,7 +151,7 @@ public void get_usingProxy_callsInjectedBootstrapProviderCorrectly() {
channelPoolMap = new AwaitCloseChannelPoolMap(builder, shouldProxyCache, bootstrapProvider);
channelPoolMap.get(targetUri);
- verify(bootstrapProvider).createBootstrap("localhost", mockProxy.port());
+ verify(bootstrapProvider).createBootstrap("localhost", mockProxy.port(), null);
}
@Test
diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/BootstrapProviderTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/BootstrapProviderTest.java
index 337cb7ba2ec2..914587b85df3 100644
--- a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/BootstrapProviderTest.java
+++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/BootstrapProviderTest.java
@@ -42,7 +42,19 @@ public class BootstrapProviderTest {
// connection attempt and not cached between connection attempts.
@Test
public void createBootstrap_usesUnresolvedInetSocketAddress() {
- Bootstrap bootstrap = bootstrapProvider.createBootstrap("some-awesome-service-1234.amazonaws.com", 443);
+ Bootstrap bootstrap = bootstrapProvider.createBootstrap("some-awesome-service-1234.amazonaws.com", 443, false);
+
+ SocketAddress socketAddress = bootstrap.config().remoteAddress();
+
+ assertThat(socketAddress).isInstanceOf(InetSocketAddress.class);
+ InetSocketAddress inetSocketAddress = (InetSocketAddress)socketAddress;
+
+ assertThat(inetSocketAddress.isUnresolved()).isTrue();
+ }
+
+ @Test
+ public void createBootstrapNonBlockingDns_usesUnresolvedInetSocketAddress() {
+ Bootstrap bootstrap = bootstrapProvider.createBootstrap("some-awesome-service-1234.amazonaws.com", 443, true);
SocketAddress socketAddress = bootstrap.config().remoteAddress();
@@ -54,7 +66,7 @@ public void createBootstrap_usesUnresolvedInetSocketAddress() {
@Test
public void createBootstrap_defaultConfiguration_tcpKeepAliveShouldBeFalse() {
- Bootstrap bootstrap = bootstrapProvider.createBootstrap("some-awesome-service-1234.amazonaws.com", 443);
+ Bootstrap bootstrap = bootstrapProvider.createBootstrap("some-awesome-service-1234.amazonaws.com", 443, false);
Boolean keepAlive = (Boolean) bootstrap.config().options().get(ChannelOption.SO_KEEPALIVE);
assertThat(keepAlive).isFalse();
@@ -70,7 +82,7 @@ public void createBootstrap_tcpKeepAliveTrue_shouldApply() {
nettyConfiguration,
new SdkChannelOptions());
- Bootstrap bootstrap = provider.createBootstrap("some-awesome-service-1234.amazonaws.com", 443);
+ Bootstrap bootstrap = provider.createBootstrap("some-awesome-service-1234.amazonaws.com", 443, false);
Boolean keepAlive = (Boolean) bootstrap.config().options().get(ChannelOption.SO_KEEPALIVE);
assertThat(keepAlive).isTrue();
}
diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/DnsResolverLoaderTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/DnsResolverLoaderTest.java
new file mode 100644
index 000000000000..40db804aacaf
--- /dev/null
+++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/DnsResolverLoaderTest.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 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 software.amazon.awssdk.http.nio.netty.internal;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import io.netty.channel.epoll.EpollDatagramChannel;
+import io.netty.channel.socket.nio.NioDatagramChannel;
+import io.netty.channel.socket.oio.OioDatagramChannel;
+import io.netty.resolver.dns.DnsAddressResolverGroup;
+import org.junit.jupiter.api.Test;
+
+public class DnsResolverLoaderTest {
+
+ @Test
+ public void canResolveChannelFactory() {
+ assertThat(DnsResolverLoader.init(NioDatagramChannel::new)).isInstanceOf(DnsAddressResolverGroup.class);
+ assertThat(DnsResolverLoader.init(EpollDatagramChannel::new)).isInstanceOf(DnsAddressResolverGroup.class);
+ assertThat(DnsResolverLoader.init(OioDatagramChannel::new)).isInstanceOf(DnsAddressResolverGroup.class);
+ }
+}
diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/utils/SocketChannelResolverTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/utils/ChannelResolverTest.java
similarity index 70%
rename from http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/utils/SocketChannelResolverTest.java
rename to http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/utils/ChannelResolverTest.java
index 472c417d4485..45edd2b81bb1 100644
--- a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/utils/SocketChannelResolverTest.java
+++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/utils/ChannelResolverTest.java
@@ -16,39 +16,47 @@
package software.amazon.awssdk.http.nio.netty.internal.utils;
import static org.assertj.core.api.Assertions.assertThat;
-import static software.amazon.awssdk.http.nio.netty.internal.utils.SocketChannelResolver.resolveSocketChannelFactory;
+import static software.amazon.awssdk.http.nio.netty.internal.utils.ChannelResolver.resolveDatagramChannelFactory;
+import static software.amazon.awssdk.http.nio.netty.internal.utils.ChannelResolver.resolveSocketChannelFactory;
import io.netty.channel.epoll.Epoll;
+import io.netty.channel.epoll.EpollDatagramChannel;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.epoll.EpollSocketChannel;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.oio.OioEventLoopGroup;
+import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
+import io.netty.channel.socket.oio.OioDatagramChannel;
import io.netty.channel.socket.oio.OioSocketChannel;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.Test;
import software.amazon.awssdk.http.nio.netty.internal.DelegatingEventLoopGroup;
-public class SocketChannelResolverTest {
+public class ChannelResolverTest {
@Test
public void canDetectFactoryForStandardNioEventLoopGroup() {
assertThat(resolveSocketChannelFactory(new NioEventLoopGroup()).newChannel()).isInstanceOf(NioSocketChannel.class);
+ assertThat(resolveDatagramChannelFactory(new NioEventLoopGroup()).newChannel()).isInstanceOf(NioDatagramChannel.class);
}
@Test
public void canDetectEpollEventLoopGroupFactory() {
Assumptions.assumeTrue(Epoll.isAvailable());
assertThat(resolveSocketChannelFactory(new EpollEventLoopGroup()).newChannel()).isInstanceOf(EpollSocketChannel.class);
+ assertThat(resolveDatagramChannelFactory(new EpollEventLoopGroup()).newChannel()).isInstanceOf(EpollDatagramChannel.class);
}
@Test
public void worksWithDelegateEventLoopGroupsFactory() {
assertThat(resolveSocketChannelFactory(new DelegatingEventLoopGroup(new NioEventLoopGroup()) {}).newChannel()).isInstanceOf(NioSocketChannel.class);
+ assertThat(resolveDatagramChannelFactory(new DelegatingEventLoopGroup(new NioEventLoopGroup()) {}).newChannel()).isInstanceOf(NioDatagramChannel.class);
}
@Test
public void worksWithOioEventLoopGroupFactory() {
assertThat(resolveSocketChannelFactory(new OioEventLoopGroup()).newChannel()).isInstanceOf(OioSocketChannel.class);
+ assertThat(resolveDatagramChannelFactory(new OioEventLoopGroup()).newChannel()).isInstanceOf(OioDatagramChannel.class);
}
}
diff --git a/http-clients/pom.xml b/http-clients/pom.xml
index 215c88e3b607..6eb6972bcab5 100644
--- a/http-clients/pom.xml
+++ b/http-clients/pom.xml
@@ -21,7 +21,7 @@
aws-sdk-java-pom
software.amazon.awssdk
- 2.20.68-SNAPSHOT
+ 2.20.93-SNAPSHOT
4.0.0
diff --git a/http-clients/url-connection-client/pom.xml b/http-clients/url-connection-client/pom.xml
index ce4a9e4bcdb4..8e86b6129823 100644
--- a/http-clients/url-connection-client/pom.xml
+++ b/http-clients/url-connection-client/pom.xml
@@ -20,7 +20,7 @@
http-clients
software.amazon.awssdk
- 2.20.68-SNAPSHOT
+ 2.20.93-SNAPSHOT
4.0.0
diff --git a/metric-publishers/cloudwatch-metric-publisher/pom.xml b/metric-publishers/cloudwatch-metric-publisher/pom.xml
index bfbbefcd15da..5fa3f415b02b 100644
--- a/metric-publishers/cloudwatch-metric-publisher/pom.xml
+++ b/metric-publishers/cloudwatch-metric-publisher/pom.xml
@@ -17,7 +17,7 @@
software.amazon.awssdk
metric-publishers
- 2.20.68-SNAPSHOT
+ 2.20.93-SNAPSHOT
cloudwatch-metric-publisher
diff --git a/metric-publishers/pom.xml b/metric-publishers/pom.xml
index ed1280e30fb4..cd9b49acaebd 100644
--- a/metric-publishers/pom.xml
+++ b/metric-publishers/pom.xml
@@ -17,7 +17,7 @@
software.amazon.awssdk
aws-sdk-java-pom
- 2.20.68-SNAPSHOT
+ 2.20.93-SNAPSHOT
metric-publishers
diff --git a/pom.xml b/pom.xml
index eef73675ef92..b91ae696a996 100644
--- a/pom.xml
+++ b/pom.xml
@@ -20,7 +20,7 @@
4.0.0
software.amazon.awssdk
aws-sdk-java-pom
- 2.20.68-SNAPSHOT
+ 2.20.93-SNAPSHOT
pom
AWS Java SDK :: Parent
The Amazon Web Services SDK for Java provides Java APIs
@@ -87,10 +87,11 @@
${scm.github.url}
+ ${scm.github.connection}
${project.version}
- 2.20.67
+ 2.20.92
2.13.2
2.13.4.2
2.13.2
@@ -114,7 +115,7 @@
2.2.21
1.15
1.29
- 0.21.12
+ 0.22.2
5.8.1
@@ -173,7 +174,8 @@
${skipTests}
${project.basedir}/src/it/java
${session.executionRootDirectory}
- https://github.com/aws/aws-sdk-java-v2.git
+ https://github.com/aws/aws-sdk-java-v2
+ scm:git:git://github.com/aws/aws-sdk-java-v2.git
@@ -693,9 +695,6 @@
publishing
-
- https://github.com/aws/aws-sdk-java-v2/tree/release
-
diff --git a/release-scripts/pom.xml b/release-scripts/pom.xml
index 550cc0900420..98ec1d277261 100644
--- a/release-scripts/pom.xml
+++ b/release-scripts/pom.xml
@@ -22,7 +22,7 @@
software.amazon.awssdk
aws-sdk-java-pom
- 2.20.68-SNAPSHOT
+ 2.20.93-SNAPSHOT
../pom.xml
release-scripts
diff --git a/services-custom/dynamodb-enhanced/pom.xml b/services-custom/dynamodb-enhanced/pom.xml
index a303372d4725..fe0749340b44 100644
--- a/services-custom/dynamodb-enhanced/pom.xml
+++ b/services-custom/dynamodb-enhanced/pom.xml
@@ -21,7 +21,7 @@
software.amazon.awssdk
services-custom
- 2.20.68-SNAPSHOT
+ 2.20.93-SNAPSHOT
dynamodb-enhanced
AWS Java SDK :: DynamoDB :: Enhanced Client
diff --git a/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/DefaultAttributeConverterProvider.java b/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/DefaultAttributeConverterProvider.java
index 45db89f5283c..88cfbe39e82f 100644
--- a/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/DefaultAttributeConverterProvider.java
+++ b/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/DefaultAttributeConverterProvider.java
@@ -43,7 +43,6 @@
import software.amazon.awssdk.enhanced.dynamodb.internal.converter.attribute.DocumentAttributeConverter;
import software.amazon.awssdk.enhanced.dynamodb.internal.converter.attribute.DoubleAttributeConverter;
import software.amazon.awssdk.enhanced.dynamodb.internal.converter.attribute.DurationAttributeConverter;
-import software.amazon.awssdk.enhanced.dynamodb.internal.converter.attribute.EnumAttributeConverter;
import software.amazon.awssdk.enhanced.dynamodb.internal.converter.attribute.FloatAttributeConverter;
import software.amazon.awssdk.enhanced.dynamodb.internal.converter.attribute.InstantAsStringAttributeConverter;
import software.amazon.awssdk.enhanced.dynamodb.internal.converter.attribute.IntegerAttributeConverter;
diff --git a/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedClient.java b/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedClient.java
index 256cd3e9fdba..5c2448a985d7 100644
--- a/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedClient.java
+++ b/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedClient.java
@@ -269,7 +269,7 @@ default BatchWriteResult batchWriteItem(Consumer
@@ -297,6 +297,7 @@ default BatchWriteResult batchWriteItem(Consumer
+ * See {@link DynamoDbClient#transactGetItems(Consumer)} to learn more about {@code TransactGetItems}.
*
* @param request A {@link TransactGetItemsEnhancedRequest} containing keys with table references.
* @return a list of {@link Document} with the results.
@@ -307,7 +308,7 @@ default List transactGetItems(TransactGetItemsEnhancedRequest request)
/**
* Retrieves multiple items from one or more tables in a single atomic transaction. TransactGetItem is a composite operation
- * where the request contains a set of up to 25 get requests, each containing a table reference and a
+ * where the request contains a set of get requests, each containing a table reference and a
* {@link GetItemEnhancedRequest}. The list of results correspond to the ordering of the request definitions; for example
* the third addGetItem() call on the request builder will match the third result (index 2) of the result.
*
@@ -336,9 +337,12 @@ default List transactGetItems(TransactGetItemsEnhancedRequest request)
* MyItem item = results.get(3).getItem(secondItemTable);
* }
*
+ *
+ * See {@link DynamoDbClient#transactGetItems(Consumer)} to learn more about {@code TransactGetItems}.
*
* @param requestConsumer a {@link Consumer} of {@link TransactGetItemsEnhancedRequest} containing keys with table references.
* @return a list of {@link Document} with the results.
+ *
*/
default List transactGetItems(Consumer requestConsumer) {
throw new UnsupportedOperationException();
@@ -346,7 +350,7 @@ default List transactGetItems(Consumer
* Condition check of item - {@link ConditionCheck}
@@ -384,6 +388,7 @@ default List transactGetItems(Consumer
+ * See {@link DynamoDbClient#transactWriteItems(Consumer)} to learn more about {@code TransactWriteItems}.
*
* @param request A {@link BatchWriteItemEnhancedRequest} containing keys grouped by tables.
*/
@@ -393,7 +398,7 @@ default Void transactWriteItems(TransactWriteItemsEnhancedRequest request) {
/**
* Writes and/or modifies multiple items from one or more tables in a single atomic transaction. TransactGetItem is a
- * composite operation where the request contains a set of up to 25 action requests, each containing a table reference and
+ * composite operation where the request contains a set of action requests, each containing a table reference and
* one of the following requests:
*
* Condition check of item - {@link ConditionCheck}
@@ -427,6 +432,7 @@ default Void transactWriteItems(TransactWriteItemsEnhancedRequest request) {
* .addUpdateItem(secondItemTable, i -> i.item(item4)));
* }
*
+ * See {@link DynamoDbClient#transactWriteItems(Consumer)} to learn more about {@code TransactWriteItems}.
*
* @param requestConsumer a {@link Consumer} of {@link TransactWriteItemsEnhancedRequest} containing keys and items grouped
* by tables.
diff --git a/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/EnumAttributeConverter.java b/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/EnumAttributeConverter.java
new file mode 100644
index 000000000000..a44a5e2070f0
--- /dev/null
+++ b/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/EnumAttributeConverter.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright 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 software.amazon.awssdk.enhanced.dynamodb;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.function.Function;
+import software.amazon.awssdk.annotations.SdkPublicApi;
+import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
+import software.amazon.awssdk.utils.Validate;
+
+/**
+ * A converter between an {@link Enum} and {@link AttributeValue}.
+ *
+ *
+ * This stores values in DynamoDB as a string.
+ *
+ *
+ * Use EnumAttributeConverter::create in order to use Enum::toString as the enum identifier
+ *
+ *
+ * Use EnumAttributeConverter::createWithNameAsKeys in order to use Enum::name as the enum identifier
+ *
+ *
+ * This can be created via {@link #create(Class)}.
+ */
+@SdkPublicApi
+public final class EnumAttributeConverter> implements AttributeConverter {
+
+ private final Class enumClass;
+ private final Map enumValueMap;
+
+ private final Function keyExtractor;
+
+ private EnumAttributeConverter(Class enumClass, Function keyExtractor) {
+ this.enumClass = enumClass;
+ this.keyExtractor = keyExtractor;
+
+ Map mutableEnumValueMap = new LinkedHashMap<>();
+ Arrays.stream(enumClass.getEnumConstants())
+ .forEach(enumConstant -> mutableEnumValueMap.put(keyExtractor.apply(enumConstant), enumConstant));
+
+ this.enumValueMap = Collections.unmodifiableMap(mutableEnumValueMap);
+ }
+
+ /**
+ * Creates an EnumAttributeConverter for an {@link Enum}.
+ *
+ *
+ * Uses Enum::toString as the enum identifier.
+ *
+ * @param enumClass The enum class to be used
+ * @return an EnumAttributeConverter
+ * @param the enum subclass
+ */
+ public static > EnumAttributeConverter create(Class enumClass) {
+ return new EnumAttributeConverter<>(enumClass, Enum::toString);
+ }
+
+ /**
+ * Creates an EnumAttributeConverter for an {@link Enum}.
+ *
+ *
+ * Uses Enum::name as the enum identifier.
+ *
+ * @param enumClass The enum class to be used
+ * @return an EnumAttributeConverter
+ * @param the enum subclass
+ */
+ public static > EnumAttributeConverter createWithNameAsKeys(Class enumClass) {
+ return new EnumAttributeConverter<>(enumClass, Enum::name);
+ }
+
+ /**
+ * Returns the proper {@link AttributeValue} for the given enum type.
+ *
+ * @param input the enum type to be converted
+ * @return AttributeValue
+ */
+ @Override
+ public AttributeValue transformFrom(T input) {
+ return AttributeValue.builder().s(keyExtractor.apply(input)).build();
+ }
+
+ /**
+ * Returns the proper enum type for the given {@link AttributeValue} input.
+ *
+ * @param input the AttributeValue to be converted
+ * @return an enum type
+ */
+ @Override
+ public T transformTo(AttributeValue input) {
+ Validate.isTrue(input.s() != null, "Cannot convert non-string value to enum.");
+ T returnValue = enumValueMap.get(input.s());
+
+ if (returnValue == null) {
+ throw new IllegalArgumentException(String.format("Unable to convert string value '%s' to enum type '%s'",
+ input.s(), enumClass));
+ }
+
+ return returnValue;
+ }
+
+ /**
+ * Returns the {@link EnhancedType} of the converter.
+ *
+ * @return EnhancedType
+ */
+ @Override
+ public EnhancedType type() {
+ return EnhancedType.of(enumClass);
+ }
+
+ /**
+ * Returns the {@link AttributeValueType} of the converter.
+ *
+ * @return AttributeValueType
+ */
+ @Override
+ public AttributeValueType attributeValueType() {
+ return AttributeValueType.S;
+ }
+}
diff --git a/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/TableSchema.java b/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/TableSchema.java
index bdf4ee35cbdb..2aa9d100d2c2 100644
--- a/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/TableSchema.java
+++ b/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/TableSchema.java
@@ -53,6 +53,18 @@ static StaticTableSchema.Builder builder(Class itemClass) {
return StaticTableSchema.builder(itemClass);
}
+ /**
+ * Returns a builder for the {@link StaticTableSchema} implementation of this interface which allows all attributes,
+ * tags and table structure to be directly declared in the builder.
+ *
+ * @param itemType The {@link EnhancedType} of the item this {@link TableSchema} will map records to.
+ * @param The type of the item this {@link TableSchema} will map records to.
+ * @return A newly initialized {@link StaticTableSchema.Builder}.
+ */
+ static StaticTableSchema.Builder builder(EnhancedType itemType) {
+ return StaticTableSchema.builder(itemType);
+ }
+
/**
* Returns a builder for the {@link StaticImmutableTableSchema} implementation of this interface which allows all
* attributes, tags and table structure to be directly declared in the builder.
@@ -69,6 +81,22 @@ static StaticImmutableTableSchema.Builder builder(Class immutabl
return StaticImmutableTableSchema.builder(immutableItemClass, immutableBuilderClass);
}
+ /**
+ * Returns a builder for the {@link StaticImmutableTableSchema} implementation of this interface which allows all
+ * attributes, tags and table structure to be directly declared in the builder.
+ *
+ * @param immutableItemType The {@link EnhancedType} of the immutable item this {@link TableSchema} will map records to.
+ * @param immutableBuilderType The {@link EnhancedType} of the class that can be used to construct immutable items this
+ * {@link TableSchema} maps records to.
+ * @param The type of the immutable item this {@link TableSchema} will map records to.
+ * @param The type of the builder used by this {@link TableSchema} to construct immutable items with.
+ * @return A newly initialized {@link StaticImmutableTableSchema.Builder}
+ */
+ static StaticImmutableTableSchema.Builder builder(EnhancedType immutableItemType,
+ EnhancedType immutableBuilderType) {
+ return StaticImmutableTableSchema.builder(immutableItemType, immutableBuilderType);
+ }
+
/**
* Scans a bean class that has been annotated with DynamoDb bean annotations and then returns a
* {@link BeanTableSchema} implementation of this interface that can map records to and from items of that bean
diff --git a/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/internal/client/DefaultDynamoDbTable.java b/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/internal/client/DefaultDynamoDbTable.java
index 6c9f0f69265e..8b6d8412969b 100644
--- a/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/internal/client/DefaultDynamoDbTable.java
+++ b/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/internal/client/DefaultDynamoDbTable.java
@@ -15,13 +15,20 @@
package software.amazon.awssdk.enhanced.dynamodb.internal.client;
+import static java.util.Collections.emptyList;
import static software.amazon.awssdk.enhanced.dynamodb.internal.EnhancedClientUtils.createKeyFromItem;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
import java.util.function.Consumer;
+import java.util.stream.Collectors;
import software.amazon.awssdk.annotations.SdkInternalApi;
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClientExtension;
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbTable;
+import software.amazon.awssdk.enhanced.dynamodb.IndexMetadata;
import software.amazon.awssdk.enhanced.dynamodb.Key;
+import software.amazon.awssdk.enhanced.dynamodb.KeyAttributeMetadata;
import software.amazon.awssdk.enhanced.dynamodb.TableMetadata;
import software.amazon.awssdk.enhanced.dynamodb.TableSchema;
import software.amazon.awssdk.enhanced.dynamodb.internal.operations.CreateTableOperation;
@@ -39,6 +46,8 @@
import software.amazon.awssdk.enhanced.dynamodb.model.DeleteItemEnhancedRequest;
import software.amazon.awssdk.enhanced.dynamodb.model.DeleteItemEnhancedResponse;
import software.amazon.awssdk.enhanced.dynamodb.model.DescribeTableEnhancedResponse;
+import software.amazon.awssdk.enhanced.dynamodb.model.EnhancedGlobalSecondaryIndex;
+import software.amazon.awssdk.enhanced.dynamodb.model.EnhancedLocalSecondaryIndex;
import software.amazon.awssdk.enhanced.dynamodb.model.GetItemEnhancedRequest;
import software.amazon.awssdk.enhanced.dynamodb.model.PageIterable;
import software.amazon.awssdk.enhanced.dynamodb.model.PutItemEnhancedRequest;
@@ -51,6 +60,7 @@
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.DescribeTableRequest;
import software.amazon.awssdk.services.dynamodb.model.DescribeTableResponse;
+import software.amazon.awssdk.services.dynamodb.model.ProjectionType;
@SdkInternalApi
public class DefaultDynamoDbTable implements DynamoDbTable {
@@ -115,7 +125,51 @@ public void createTable(Consumer requestCons
@Override
public void createTable() {
- createTable(CreateTableEnhancedRequest.builder().build());
+ Map> indexGroups = splitSecondaryIndicesToLocalAndGlobalOnes();
+ createTable(CreateTableEnhancedRequest.builder()
+ .localSecondaryIndices(extractLocalSecondaryIndices(indexGroups))
+ .globalSecondaryIndices(extractGlobalSecondaryIndices(indexGroups))
+ .build());
+ }
+
+ private Map> splitSecondaryIndicesToLocalAndGlobalOnes() {
+ String primaryPartitionKeyName = tableSchema.tableMetadata().primaryPartitionKey();
+ Collection indices = tableSchema.tableMetadata().indices();
+ return indices.stream()
+ .filter(index -> !TableMetadata.primaryIndexName().equals(index.name()))
+ .collect(Collectors.groupingBy(metadata -> {
+ String partitionKeyName = metadata.partitionKey().map(KeyAttributeMetadata::name).orElse(null);
+ if (partitionKeyName == null || primaryPartitionKeyName.equals(partitionKeyName)) {
+ return IndexType.LSI;
+ }
+ return IndexType.GSI;
+ }));
+ }
+
+ private List extractLocalSecondaryIndices(Map> indicesGroups) {
+ return indicesGroups.getOrDefault(IndexType.LSI, emptyList()).stream()
+ .map(this::mapIndexMetadataToEnhancedLocalSecondaryIndex)
+ .collect(Collectors.toList());
+ }
+
+ private EnhancedLocalSecondaryIndex mapIndexMetadataToEnhancedLocalSecondaryIndex(IndexMetadata indexMetadata) {
+ return EnhancedLocalSecondaryIndex.builder()
+ .indexName(indexMetadata.name())
+ .projection(pb -> pb.projectionType(ProjectionType.ALL))
+ .build();
+ }
+
+ private List extractGlobalSecondaryIndices(Map> indicesGroups) {
+ return indicesGroups.getOrDefault(IndexType.GSI, emptyList()).stream()
+ .map(this::mapIndexMetadataToEnhancedGlobalSecondaryIndex)
+ .collect(Collectors.toList());
+ }
+
+ private EnhancedGlobalSecondaryIndex mapIndexMetadataToEnhancedGlobalSecondaryIndex(IndexMetadata indexMetadata) {
+ return EnhancedGlobalSecondaryIndex.builder()
+ .indexName(indexMetadata.name())
+ .projection(pb -> pb.projectionType(ProjectionType.ALL))
+ .build();
}
@Override
diff --git a/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/internal/client/IndexType.java b/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/internal/client/IndexType.java
new file mode 100644
index 000000000000..0fd1fc28cd82
--- /dev/null
+++ b/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/internal/client/IndexType.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 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 software.amazon.awssdk.enhanced.dynamodb.internal.client;
+
+import software.amazon.awssdk.annotations.SdkInternalApi;
+
+/**
+ * Enum collecting types of secondary indexes
+ */
+@SdkInternalApi
+public enum IndexType {
+ LSI,
+ GSI
+}
diff --git a/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/internal/converter/attribute/EnumAttributeConverter.java b/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/internal/converter/attribute/EnumAttributeConverter.java
deleted file mode 100644
index 18395a82656b..000000000000
--- a/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/internal/converter/attribute/EnumAttributeConverter.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright 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 software.amazon.awssdk.enhanced.dynamodb.internal.converter.attribute;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import software.amazon.awssdk.annotations.SdkInternalApi;
-import software.amazon.awssdk.enhanced.dynamodb.AttributeConverter;
-import software.amazon.awssdk.enhanced.dynamodb.AttributeValueType;
-import software.amazon.awssdk.enhanced.dynamodb.EnhancedType;
-import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
-import software.amazon.awssdk.utils.Validate;
-
-/**
- * A converter between an {@link Enum} and {@link AttributeValue}.
- *
- *
- * This stores values in DynamoDB as a string.
- *
- *
- * This can be created via {@link #create(Class)}.
- */
-@SdkInternalApi
-public class EnumAttributeConverter> implements AttributeConverter {
-
- private final Class enumClass;
- private final Map enumValueMap;
-
- private EnumAttributeConverter(Class enumClass) {
- this.enumClass = enumClass;
-
- Map mutableEnumValueMap = new LinkedHashMap<>();
- Arrays.stream(enumClass.getEnumConstants())
- .forEach(enumConstant -> mutableEnumValueMap.put(enumConstant.toString(), enumConstant));
-
- this.enumValueMap = Collections.unmodifiableMap(mutableEnumValueMap);
- }
-
- public static > EnumAttributeConverter create(Class enumClass) {
- return new EnumAttributeConverter<>(enumClass);
- }
-
- @Override
- public AttributeValue transformFrom(T input) {
- return AttributeValue.builder().s(input.toString()).build();
- }
-
- @Override
- public T transformTo(AttributeValue input) {
- Validate.isTrue(input.s() != null, "Cannot convert non-string value to enum.");
- T returnValue = enumValueMap.get(input.s());
-
- if (returnValue == null) {
- throw new IllegalArgumentException(String.format("Unable to convert string value '%s' to enum type '%s'",
- input.s(), enumClass));
- }
-
- return returnValue;
- }
-
- @Override
- public EnhancedType type() {
- return EnhancedType.of(enumClass);
- }
-
- @Override
- public AttributeValueType attributeValueType() {
- return AttributeValueType.S;
- }
-}
diff --git a/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/internal/operations/CreateTableOperation.java b/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/internal/operations/CreateTableOperation.java
index 9bfe3f2528f7..055b685dbe4d 100644
--- a/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/internal/operations/CreateTableOperation.java
+++ b/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/internal/operations/CreateTableOperation.java
@@ -74,7 +74,7 @@ public CreateTableRequest generateRequest(TableSchema tableSchema,
List sdkGlobalSecondaryIndices = null;
List sdkLocalSecondaryIndices = null;
- if (this.request.globalSecondaryIndices() != null) {
+ if (this.request.globalSecondaryIndices() != null && !this.request.globalSecondaryIndices().isEmpty()) {
sdkGlobalSecondaryIndices =
this.request.globalSecondaryIndices().stream().map(gsi -> {
String indexPartitionKey = tableSchema.tableMetadata().indexPartitionKey(gsi.indexName());
@@ -92,7 +92,7 @@ public CreateTableRequest generateRequest(TableSchema tableSchema,
}).collect(Collectors.toList());
}
- if (this.request.localSecondaryIndices() != null) {
+ if (this.request.localSecondaryIndices() != null && !this.request.localSecondaryIndices().isEmpty()) {
sdkLocalSecondaryIndices =
this.request.localSecondaryIndices().stream().map(lsi -> {
Optional indexSortKey = tableSchema.tableMetadata().indexSortKey(lsi.indexName());
diff --git a/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/mapper/ImmutableAttribute.java b/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/mapper/ImmutableAttribute.java
index d22f87af61a9..d5622bfc6df6 100644
--- a/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/mapper/ImmutableAttribute.java
+++ b/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/mapper/ImmutableAttribute.java
@@ -91,6 +91,19 @@ public static Builder builder(Class itemClass,
return new Builder<>(attributeType);
}
+ /**
+ * Constructs a new builder for this class using supplied types.
+ * @param itemType The {@link EnhancedType} of the immutable item that this attribute composes.
+ * @param builderType The {@link EnhancedType} of the builder for the immutable item that this attribute composes.
+ * @param attributeType A {@link EnhancedType} that represents the type of the value this attribute stores.
+ * @return A new typed builder for an attribute.
+ */
+ public static Builder