Skip to content

Commit f727239

Browse files
authored
URI Cache for DynamoDB Account Id based Endpoints (#6244)
* URI cache for DynamoDB account id based endpoint - PR #6087 * Update SdkUri to use BoundedCache * Update BoundedCache * Address comments
1 parent 2955ab0 commit f727239

File tree

25 files changed

+1088
-27
lines changed

25 files changed

+1088
-27
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"type": "feature",
3+
"category": "Amazon DynamoDB",
4+
"contributor": "",
5+
"description": "Enable caching results to URI constructors for account-id based endpoints"
6+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"type": "feature",
3+
"category": "AWS SDK for Java v2",
4+
"contributor": "",
5+
"description": "Add support for caching results to URI constructors for account-id based endpoints"
6+
}

build-tools/src/main/resources/software/amazon/awssdk/spotbugs-suppressions.xml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -353,10 +353,14 @@
353353
</Or>
354354
<Bug pattern="ASYNC_BLOCKING_CALL"/>
355355
</Match>
356-
356+
357357
<!-- False positive -->
358358
<Match>
359359
<Class name="software.amazon.awssdk.v2migration.EnumCasingToV2$Visitor"/>
360360
<Bug pattern="NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE"/>
361361
</Match>
362+
<Match>
363+
<Class name="software.amazon.awssdk.utils.uri.SdkUri" />
364+
<Bug pattern="BC_UNCONFIRMED_CAST_OF_RETURN_VALUE" />
365+
</Match>
362366
</FindBugsFilter>

codegen/src/main/java/software/amazon/awssdk/codegen/model/config/customization/CustomizationConfig.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,11 @@ public class CustomizationConfig {
357357
*/
358358
private boolean enableEnvironmentBearerToken = false;
359359

360+
/**
361+
* A boolean flag to indicate if the code-generated endpoint providers class should cache the calls to URI constructors.
362+
*/
363+
private boolean enableEndpointProviderUriCaching;
364+
360365
private CustomizationConfig() {
361366
}
362367

@@ -939,4 +944,12 @@ public boolean isEnableEnvironmentBearerToken() {
939944
public void setEnableEnvironmentBearerToken(boolean enableEnvironmentBearerToken) {
940945
this.enableEnvironmentBearerToken = enableEnvironmentBearerToken;
941946
}
947+
948+
public boolean getEnableEndpointProviderUriCaching() {
949+
return enableEndpointProviderUriCaching;
950+
}
951+
952+
public void setEnableEndpointProviderUriCaching(boolean enableEndpointProviderUriCaching) {
953+
this.enableEndpointProviderUriCaching = enableEndpointProviderUriCaching;
954+
}
942955
}

codegen/src/main/java/software/amazon/awssdk/codegen/poet/rules2/CodeGeneratorVisitor.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import software.amazon.awssdk.awscore.endpoints.authscheme.SigV4aAuthScheme;
3030
import software.amazon.awssdk.codegen.model.config.customization.KeyTypePair;
3131
import software.amazon.awssdk.endpoints.Endpoint;
32+
import software.amazon.awssdk.utils.uri.SdkUri;
3233

3334
public class CodeGeneratorVisitor extends WalkRuleExpressionVisitor {
3435
private static final Logger log = LoggerFactory.getLogger(CodeGeneratorVisitor.class);
@@ -38,17 +39,20 @@ public class CodeGeneratorVisitor extends WalkRuleExpressionVisitor {
3839
private final SymbolTable symbolTable;
3940
private final Map<String, KeyTypePair> knownEndpointAttributes;
4041
private final Map<String, ComputeScopeTree.Scope> ruleIdToScope;
42+
private final boolean endpointCaching;
4143

4244
public CodeGeneratorVisitor(RuleRuntimeTypeMirror typeMirror,
4345
SymbolTable symbolTable,
4446
Map<String, KeyTypePair> knownEndpointAttributes,
4547
Map<String, ComputeScopeTree.Scope> ruleIdToScope,
48+
boolean endpointCaching,
4649
CodeBlock.Builder builder) {
4750
this.builder = builder;
4851
this.symbolTable = symbolTable;
4952
this.knownEndpointAttributes = knownEndpointAttributes;
5053
this.ruleIdToScope = ruleIdToScope;
5154
this.typeMirror = typeMirror;
55+
this.endpointCaching = endpointCaching;
5256
}
5357

5458
@Override
@@ -329,7 +333,11 @@ private String callParams(String ruleId) {
329333
@Override
330334
public Void visitEndpointExpression(EndpointExpression e) {
331335
builder.add("return $T.endpoint(", typeMirror.rulesResult().type());
332-
builder.add("$T.builder().url($T.create(", Endpoint.class, URI.class);
336+
if (endpointCaching) {
337+
builder.add("$T.builder().url($T.getInstance().create(", Endpoint.class, SdkUri.class);
338+
} else {
339+
builder.add("$T.builder().url($T.create(", Endpoint.class, URI.class);
340+
}
333341
e.url().accept(this);
334342
builder.add("))");
335343
e.headers().accept(this);

codegen/src/main/java/software/amazon/awssdk/codegen/poet/rules2/EndpointProviderSpec2.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,10 +229,12 @@ private MethodSpec.Builder methodBuilderForRule(RuleSetExpression expr) {
229229
}
230230

231231
private void codegenExpr(RuleSetExpression expr, CodeBlock.Builder builder) {
232+
boolean useEndpointCaching = intermediateModel.getCustomizationConfig().getEnableEndpointProviderUriCaching();
232233
CodeGeneratorVisitor visitor = new CodeGeneratorVisitor(typeMirror,
233234
utils.symbolTable(),
234235
knownEndpointAttributes,
235236
utils.scopesByName(),
237+
useEndpointCaching,
236238
builder);
237239
visitor.visitRuleSetExpression(expr);
238240
}

codegen/src/test/java/software/amazon/awssdk/codegen/poet/ClientTestModels.java

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,10 @@ public static IntermediateModel awsJsonServiceModels() {
3939
File customizationModel = new File(ClientTestModels.class.getResource("client/c2j/json/customization.config").getFile());
4040
File paginatorsModel = new File(ClientTestModels.class.getResource("client/c2j/json/paginators.json").getFile());
4141
C2jModels models = C2jModels.builder()
42-
.serviceModel(getServiceModel(serviceModel))
43-
.customizationConfig(getCustomizationConfig(customizationModel))
44-
.paginatorsModel(getPaginatorsModel(paginatorsModel))
45-
.build();
42+
.serviceModel(getServiceModel(serviceModel))
43+
.customizationConfig(getCustomizationConfig(customizationModel))
44+
.paginatorsModel(getPaginatorsModel(paginatorsModel))
45+
.build();
4646

4747
return new IntermediateModelBuilder(models).build();
4848
}
@@ -136,13 +136,13 @@ public static IntermediateModel queryServiceModels() {
136136
new File(ClientTestModels.class.getResource("client/c2j/query/endpoint-tests.json").getFile());
137137

138138
C2jModels models = C2jModels
139-
.builder()
140-
.serviceModel(getServiceModel(serviceModel))
141-
.customizationConfig(getCustomizationConfig(customizationModel))
142-
.waitersModel(getWaiters(waitersModel))
139+
.builder()
140+
.serviceModel(getServiceModel(serviceModel))
141+
.customizationConfig(getCustomizationConfig(customizationModel))
142+
.waitersModel(getWaiters(waitersModel))
143143
.endpointRuleSetModel(getEndpointRuleSet(endpointRuleSetModel))
144144
.endpointTestSuiteModel(getEndpointTestSuite(endpointTestsModel))
145-
.build();
145+
.build();
146146

147147
return new IntermediateModelBuilder(models).build();
148148
}
@@ -206,6 +206,28 @@ public static IntermediateModel queryServiceModelsWithUnknownEndpointProperties(
206206
return new IntermediateModelBuilder(models).build();
207207
}
208208

209+
public static IntermediateModel queryServiceModelsWithUriCache() {
210+
File serviceModel = new File(ClientTestModels.class.getResource("client/c2j/query/service-2.json").getFile());
211+
File customizationModel =
212+
new File(ClientTestModels.class.getResource("client/c2j/query/customization-uri-cache.config").getFile());
213+
File waitersModel = new File(ClientTestModels.class.getResource("client/c2j/query/waiters-2.json").getFile());
214+
File endpointRuleSetModel =
215+
new File(ClientTestModels.class.getResource("client/c2j/query/endpoint-rule-set.json").getFile());
216+
File endpointTestsModel =
217+
new File(ClientTestModels.class.getResource("client/c2j/query/endpoint-tests.json").getFile());
218+
219+
C2jModels models = C2jModels
220+
.builder()
221+
.serviceModel(getServiceModel(serviceModel))
222+
.customizationConfig(getCustomizationConfig(customizationModel))
223+
.waitersModel(getWaiters(waitersModel))
224+
.endpointRuleSetModel(getEndpointRuleSet(endpointRuleSetModel))
225+
.endpointTestSuiteModel(getEndpointTestSuite(endpointTestsModel))
226+
.build();
227+
228+
return new IntermediateModelBuilder(models).build();
229+
}
230+
209231
public static IntermediateModel queryServiceModelsEndpointAuthParamsWithAllowList() {
210232
File serviceModel = new File(ClientTestModels.class.getResource("client/c2j/query/service-2.json").getFile());
211233
File customizationModel =
@@ -473,9 +495,9 @@ public static IntermediateModel customContentTypeModels() {
473495
File customizationModel = new File(ClientTestModels.class.getResource("client/c2j/customservicemetadata/customization.config").getFile());
474496

475497
C2jModels models = C2jModels.builder()
476-
.serviceModel(getServiceModel(serviceModel))
477-
.customizationConfig(getCustomizationConfig(customizationModel))
478-
.build();
498+
.serviceModel(getServiceModel(serviceModel))
499+
.customizationConfig(getCustomizationConfig(customizationModel))
500+
.build();
479501

480502
return new IntermediateModelBuilder(models).build();
481503
}

codegen/src/test/java/software/amazon/awssdk/codegen/poet/rules/EndpointProviderCompiledRulesClassSpecTest.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,11 @@ void unknownEndpointProperties() {
4444
new EndpointProviderSpec2(ClientTestModels.queryServiceModelsWithUnknownEndpointProperties());
4545
assertThat(endpointProviderSpec, generatesTo("endpoint-provider-unknown-property-class.java"));
4646
}
47+
48+
@Test
49+
void endpointProviderClassWithUriCache() {
50+
ClassSpec endpointProviderSpec =
51+
new EndpointProviderSpec2(ClientTestModels.queryServiceModelsWithUriCache());
52+
assertThat(endpointProviderSpec, generatesTo("endpoint-provider-uri-cache-class.java"));
53+
}
4754
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
{
2+
"authPolicyActions" : {
3+
"skip" : true
4+
},
5+
"skipEndpointTests": {
6+
"test case 4": "Does not work"
7+
},
8+
"endpointParameters": {
9+
"CustomEndpointArray": {
10+
"required": false,
11+
"documentation": "Parameter from the customization config",
12+
"type": "StringArray"
13+
},
14+
"ArnList": {
15+
"required": false,
16+
"documentation": "Parameter from the customization config",
17+
"type": "StringArray"
18+
}
19+
},
20+
"customOperationContextParams": [
21+
{
22+
"operationName": "OperationWithCustomizedOperationContextParam",
23+
"operationContextParamsMap": {
24+
"customEndpointArray": {
25+
"path": "ListMember.StringList[*].LeafString"
26+
}
27+
}
28+
}
29+
],
30+
"preClientExecutionRequestCustomizer": {
31+
"OperationWithCustomMember": {
32+
"methodName": "dummyRequestModifier",
33+
"className": "software.amazon.awssdk.codegen.internal.UtilsTest"
34+
}
35+
},
36+
"enableEndpointProviderUriCaching": true
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
package software.amazon.awssdk.services.query.endpoints.internal;
2+
3+
import java.util.Arrays;
4+
import java.util.concurrent.CompletableFuture;
5+
import software.amazon.awssdk.annotations.Generated;
6+
import software.amazon.awssdk.annotations.SdkInternalApi;
7+
import software.amazon.awssdk.awscore.endpoints.AwsEndpointAttribute;
8+
import software.amazon.awssdk.awscore.endpoints.authscheme.SigV4AuthScheme;
9+
import software.amazon.awssdk.awscore.endpoints.authscheme.SigV4aAuthScheme;
10+
import software.amazon.awssdk.core.exception.SdkClientException;
11+
import software.amazon.awssdk.endpoints.Endpoint;
12+
import software.amazon.awssdk.regions.Region;
13+
import software.amazon.awssdk.services.query.endpoints.QueryEndpointParams;
14+
import software.amazon.awssdk.services.query.endpoints.QueryEndpointProvider;
15+
import software.amazon.awssdk.utils.CompletableFutureUtils;
16+
import software.amazon.awssdk.utils.Validate;
17+
import software.amazon.awssdk.utils.uri.SdkUri;
18+
19+
@Generated("software.amazon.awssdk:codegen")
20+
@SdkInternalApi
21+
public final class DefaultQueryEndpointProvider implements QueryEndpointProvider {
22+
@Override
23+
public CompletableFuture<Endpoint> resolveEndpoint(QueryEndpointParams params) {
24+
Validate.notNull(params.region(), "Parameter 'region' must not be null");
25+
try {
26+
Region region = params.region();
27+
String regionId = region == null ? null : region.id();
28+
RuleResult result = endpointRule0(params, regionId);
29+
if (result.canContinue()) {
30+
throw SdkClientException.create("Rule engine did not reach an error or endpoint result");
31+
}
32+
if (result.isError()) {
33+
String errorMsg = result.error();
34+
if (errorMsg.contains("Invalid ARN") && errorMsg.contains(":s3:::")) {
35+
errorMsg += ". Use the bucket name instead of simple bucket ARNs in GetBucketLocationRequest.";
36+
}
37+
throw SdkClientException.create(errorMsg);
38+
}
39+
return CompletableFuture.completedFuture(result.endpoint());
40+
} catch (Exception error) {
41+
return CompletableFutureUtils.failedFuture(error);
42+
}
43+
}
44+
45+
private static RuleResult endpointRule0(QueryEndpointParams params, String region) {
46+
return endpointRule1(params, region);
47+
}
48+
49+
private static RuleResult endpointRule1(QueryEndpointParams params, String region) {
50+
RulePartition partitionResult = RulesFunctions.awsPartition(region);
51+
if (partitionResult != null) {
52+
RuleResult result = endpointRule2(params, partitionResult);
53+
if (result.isResolved()) {
54+
return result;
55+
}
56+
result = endpointRule6(params, region, partitionResult);
57+
if (result.isResolved()) {
58+
return result;
59+
}
60+
return RuleResult.error(region + " is not a valid HTTP host-label");
61+
if (params.useFipsEndpoint() == null && params.useDualStackEndpoint() != null && params.useDualStackEndpoint()
62+
&& params.arnList() != null) {
63+
String firstArn = RulesFunctions.listAccess(params.arnList(), 0);
64+
if (firstArn != null) {
65+
RuleArn parsedArn = RulesFunctions.awsParseArn(firstArn);
66+
if (parsedArn != null) {
67+
return RuleResult.endpoint(Endpoint
68+
.builder()
69+
.url(SdkUri.getInstance().create(
70+
"https://" + params.endpointId() + ".query." + partitionResult.dualStackDnsSuffix()))
71+
.putAttribute(
72+
AwsEndpointAttribute.AUTH_SCHEMES,
73+
Arrays.asList(SigV4aAuthScheme.builder().signingName("query")
74+
.signingRegionSet(Arrays.asList("*")).build())).build());
75+
}
76+
}
77+
}
78+
}
79+
return RuleResult.carryOn();
80+
}
81+
82+
private static RuleResult endpointRule2(QueryEndpointParams params, RulePartition partitionResult) {
83+
if (params.endpointId() != null) {
84+
if (params.useFipsEndpoint() != null && params.useFipsEndpoint()) {
85+
return RuleResult.error("FIPS endpoints not supported with multi-region endpoints");
86+
}
87+
if (params.useFipsEndpoint() == null && params.useDualStackEndpoint() != null && params.useDualStackEndpoint()) {
88+
return RuleResult.endpoint(Endpoint
89+
.builder()
90+
.url(SdkUri.getInstance().create(
91+
"https://" + params.endpointId() + ".query." + partitionResult.dualStackDnsSuffix()))
92+
.putAttribute(
93+
AwsEndpointAttribute.AUTH_SCHEMES,
94+
Arrays.asList(SigV4aAuthScheme.builder().signingName("query")
95+
.signingRegionSet(Arrays.asList("*")).build())).build());
96+
}
97+
return RuleResult.endpoint(Endpoint
98+
.builder()
99+
.url(SdkUri.getInstance().create("https://" + params.endpointId() + ".query." + partitionResult.dnsSuffix()))
100+
.putAttribute(
101+
AwsEndpointAttribute.AUTH_SCHEMES,
102+
Arrays.asList(SigV4aAuthScheme.builder().signingName("query").signingRegionSet(Arrays.asList("*"))
103+
.build())).build());
104+
}
105+
return RuleResult.carryOn();
106+
}
107+
108+
private static RuleResult endpointRule6(QueryEndpointParams params, String region, RulePartition partitionResult) {
109+
if (RulesFunctions.isValidHostLabel(region, false)) {
110+
if (params.useFipsEndpoint() != null && params.useFipsEndpoint() && params.useDualStackEndpoint() == null) {
111+
return RuleResult.endpoint(Endpoint
112+
.builder()
113+
.url(SdkUri.getInstance().create("https://query-fips." + region + "." + partitionResult.dnsSuffix()))
114+
.putAttribute(
115+
AwsEndpointAttribute.AUTH_SCHEMES,
116+
Arrays.asList(SigV4aAuthScheme.builder().signingName("query")
117+
.signingRegionSet(Arrays.asList("*")).build())).build());
118+
}
119+
if (params.useDualStackEndpoint() != null && params.useDualStackEndpoint() && params.useFipsEndpoint() == null) {
120+
return RuleResult.endpoint(Endpoint
121+
.builder()
122+
.url(SdkUri.getInstance().create("https://query." + region + "." + partitionResult.dualStackDnsSuffix()))
123+
.putAttribute(
124+
AwsEndpointAttribute.AUTH_SCHEMES,
125+
Arrays.asList(SigV4aAuthScheme.builder().signingName("query")
126+
.signingRegionSet(Arrays.asList("*")).build(),
127+
SigV4AuthScheme.builder().signingName("query").signingRegion(region).build())).build());
128+
}
129+
if (params.useDualStackEndpoint() != null && params.useFipsEndpoint() != null && params.useDualStackEndpoint()
130+
&& params.useFipsEndpoint()) {
131+
return RuleResult.endpoint(Endpoint
132+
.builder()
133+
.url(SdkUri.getInstance().create(
134+
"https://query-fips." + region + "." + partitionResult.dualStackDnsSuffix()))
135+
.putAttribute(
136+
AwsEndpointAttribute.AUTH_SCHEMES,
137+
Arrays.asList(SigV4aAuthScheme.builder().signingName("query")
138+
.signingRegionSet(Arrays.asList("*")).build())).build());
139+
}
140+
return RuleResult.endpoint(Endpoint.builder()
141+
.url(SdkUri.getInstance().create("https://query." + region + "." + partitionResult.dnsSuffix())).build());
142+
}
143+
return RuleResult.carryOn();
144+
}
145+
146+
@Override
147+
public boolean equals(Object rhs) {
148+
return rhs != null && getClass().equals(rhs.getClass());
149+
}
150+
151+
@Override
152+
public int hashCode() {
153+
return getClass().hashCode();
154+
}
155+
}

0 commit comments

Comments
 (0)