Skip to content

Commit 578cc0d

Browse files
authored
Wraps s3 client in cross regional client when enabled (#4080)
* Wraps s3 client in cross regional client when enabled * Adds composer interface
1 parent a87b09a commit 578cc0d

File tree

26 files changed

+810
-103
lines changed

26 files changed

+810
-103
lines changed

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

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.util.HashMap;
2020
import java.util.List;
2121
import java.util.Map;
22+
import software.amazon.awssdk.awscore.internal.client.ClientComposer;
2223
import software.amazon.awssdk.core.retry.RetryMode;
2324
import software.amazon.awssdk.core.traits.PayloadTrait;
2425
import software.amazon.awssdk.utils.AttributeMap;
@@ -212,6 +213,20 @@ public class CustomizationConfig {
212213
*/
213214
private boolean delegateSyncClientClass;
214215

216+
/**
217+
* Fully qualified name of a class that given the default sync client instance can return the final client instance,
218+
* for instance by decorating the client with specific-purpose implementations of the client interface.
219+
* The class should implement the {@link ClientComposer} interface. See S3 customization.config for an example.
220+
*/
221+
private String syncClientComposer;
222+
223+
/**
224+
* Fully qualified name of a class that given the default async client instance can return the final client instance,
225+
* for instance by decorating the client with specific-purpose implementations of the client interface.
226+
* The class should implement the {@link ClientComposer} interface. See S3 customization.config for an example.
227+
*/
228+
private String asyncClientComposer;
229+
215230
/**
216231
* Whether to skip generating endpoint tests from endpoint-tests.json
217232
*/
@@ -561,6 +576,22 @@ public void setDelegateAsyncClientClass(boolean delegateAsyncClientClass) {
561576
this.delegateAsyncClientClass = delegateAsyncClientClass;
562577
}
563578

579+
public String getSyncClientComposer() {
580+
return syncClientComposer;
581+
}
582+
583+
public void setSyncClientComposer(String syncClientComposer) {
584+
this.syncClientComposer = syncClientComposer;
585+
}
586+
587+
public String getAsyncClientComposer() {
588+
return asyncClientComposer;
589+
}
590+
591+
public void setAsyncClientComposer(String asyncClientComposer) {
592+
this.asyncClientComposer = asyncClientComposer;
593+
}
594+
564595
public boolean isDelegateSyncClientClass() {
565596
return delegateSyncClientClass;
566597
}

codegen/src/main/java/software/amazon/awssdk/codegen/model/intermediate/IntermediateModel.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,21 @@ public String getSdkResponseBaseClassName() {
235235
}
236236
}
237237

238+
public Optional<String> syncClientComposerClassName() {
239+
if (customizationConfig.getSyncClientComposer() != null) {
240+
return Optional.of(customizationConfig.getSyncClientComposer());
241+
}
242+
return Optional.empty();
243+
}
244+
245+
public Optional<String> asyncClientComposerClassName() {
246+
String asyncClientComposer = customizationConfig.getAsyncClientComposer();
247+
if (customizationConfig.getAsyncClientComposer() != null) {
248+
return Optional.of(asyncClientComposer);
249+
}
250+
return Optional.empty();
251+
}
252+
238253
public String getFileHeader() {
239254
return FILE_HEADER;
240255
}

codegen/src/main/java/software/amazon/awssdk/codegen/poet/builder/AsyncClientBuilderClass.java

Lines changed: 50 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import software.amazon.awssdk.annotations.SdkInternalApi;
2525
import software.amazon.awssdk.auth.token.credentials.SdkTokenProvider;
2626
import software.amazon.awssdk.awscore.client.config.AwsClientOption;
27+
import software.amazon.awssdk.awscore.internal.client.ClientComposer;
2728
import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel;
2829
import software.amazon.awssdk.codegen.poet.ClassSpec;
2930
import software.amazon.awssdk.codegen.poet.PoetExtension;
@@ -80,7 +81,10 @@ public TypeSpec poetSpec() {
8081
builder.addMethod(bearerTokenProviderMethod());
8182
}
8283

83-
return builder.addMethod(buildClientMethod()).build();
84+
builder.addMethod(buildClientMethod());
85+
builder.addMethod(initializeServiceClientConfigMethod());
86+
87+
return builder.build();
8488
}
8589

8690
private MethodSpec endpointDiscoveryEnabled() {
@@ -120,30 +124,28 @@ private MethodSpec endpointProviderMethod() {
120124
}
121125

122126
private MethodSpec buildClientMethod() {
123-
return MethodSpec.methodBuilder("buildClient")
124-
.addAnnotation(Override.class)
125-
.addModifiers(Modifier.PROTECTED, Modifier.FINAL)
126-
.returns(clientInterfaceName)
127-
.addStatement("$T clientConfiguration = super.asyncClientConfiguration()", SdkClientConfiguration.class)
128-
.addStatement("this.validateClientOptions(clientConfiguration)")
129-
.addStatement("$T endpointOverride = null", URI.class)
130-
.addStatement("$T endpointProvider = clientConfiguration.option($T.ENDPOINT_PROVIDER)",
131-
EndpointProvider.class,
132-
SdkClientOption.class)
133-
.addCode("if (clientConfiguration.option($T.ENDPOINT_OVERRIDDEN) != null"
134-
+ "&& $T.TRUE.equals(clientConfiguration.option($T.ENDPOINT_OVERRIDDEN))) {"
135-
+ "endpointOverride = clientConfiguration.option($T.ENDPOINT);"
136-
+ "}",
137-
SdkClientOption.class, Boolean.class, SdkClientOption.class, SdkClientOption.class)
138-
.addStatement("$T serviceClientConfiguration = $T.builder()"
139-
+ ".overrideConfiguration(overrideConfiguration())"
140-
+ ".region(clientConfiguration.option($T.AWS_REGION))"
141-
+ ".endpointOverride(endpointOverride)"
142-
+ ".endpointProvider(endpointProvider)"
143-
+ ".build()",
144-
serviceConfigClassName, serviceConfigClassName, AwsClientOption.class)
145-
.addStatement("return new $T(serviceClientConfiguration, clientConfiguration)", clientClassName)
146-
.build();
127+
MethodSpec.Builder builder = MethodSpec.methodBuilder("buildClient")
128+
.addAnnotation(Override.class)
129+
.addModifiers(Modifier.PROTECTED, Modifier.FINAL)
130+
.returns(clientInterfaceName)
131+
.addStatement("$T clientConfiguration = super.asyncClientConfiguration()",
132+
SdkClientConfiguration.class)
133+
.addStatement("this.validateClientOptions(clientConfiguration)")
134+
.addStatement("$T serviceClientConfiguration = initializeServiceClientConfig"
135+
+ "(clientConfiguration)",
136+
serviceConfigClassName);
137+
138+
builder.addStatement("$1T client = new $2T(serviceClientConfiguration, clientConfiguration)",
139+
clientInterfaceName, clientClassName);
140+
if (model.asyncClientComposerClassName().isPresent()) {
141+
builder.addStatement("$1T composer = new $2T()",
142+
ClientComposer.class,
143+
PoetUtils.classNameFromFqcn(model.asyncClientComposerClassName().get()));
144+
builder.addStatement("return ($T) composer.compose(client, clientConfiguration)", clientInterfaceName);
145+
} else {
146+
builder.addStatement("return client");
147+
}
148+
return builder.build();
147149
}
148150

149151
private MethodSpec bearerTokenProviderMethod() {
@@ -157,6 +159,29 @@ private MethodSpec bearerTokenProviderMethod() {
157159
.build();
158160
}
159161

162+
private MethodSpec initializeServiceClientConfigMethod() {
163+
return MethodSpec.methodBuilder("initializeServiceClientConfig").addModifiers(Modifier.PRIVATE)
164+
.addParameter(SdkClientConfiguration.class, "clientConfig")
165+
.returns(serviceConfigClassName)
166+
.addStatement("$T endpointOverride = null", URI.class)
167+
.addStatement("$T endpointProvider = clientConfig.option($T.ENDPOINT_PROVIDER)",
168+
EndpointProvider.class,
169+
SdkClientOption.class)
170+
.addCode("if (clientConfig.option($T.ENDPOINT_OVERRIDDEN) != null"
171+
+ "&& $T.TRUE.equals(clientConfig.option($T.ENDPOINT_OVERRIDDEN))) {"
172+
+ "endpointOverride = clientConfig.option($T.ENDPOINT);"
173+
+ "}",
174+
SdkClientOption.class, Boolean.class, SdkClientOption.class, SdkClientOption.class)
175+
.addStatement("return $T.builder()"
176+
+ ".overrideConfiguration(overrideConfiguration())"
177+
+ ".region(clientConfig.option($T.AWS_REGION))"
178+
+ ".endpointOverride(endpointOverride)"
179+
+ ".endpointProvider(endpointProvider)"
180+
+ ".build()",
181+
serviceConfigClassName, AwsClientOption.class)
182+
.build();
183+
}
184+
160185
@Override
161186
public ClassName className() {
162187
return builderClassName;

codegen/src/main/java/software/amazon/awssdk/codegen/poet/builder/SyncClientBuilderClass.java

Lines changed: 50 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import software.amazon.awssdk.annotations.SdkInternalApi;
2525
import software.amazon.awssdk.auth.token.credentials.SdkTokenProvider;
2626
import software.amazon.awssdk.awscore.client.config.AwsClientOption;
27+
import software.amazon.awssdk.awscore.internal.client.ClientComposer;
2728
import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel;
2829
import software.amazon.awssdk.codegen.poet.ClassSpec;
2930
import software.amazon.awssdk.codegen.poet.PoetExtension;
@@ -80,7 +81,10 @@ public TypeSpec poetSpec() {
8081
builder.addMethod(tokenProviderMethodImpl());
8182
}
8283

83-
return builder.addMethod(buildClientMethod()).build();
84+
builder.addMethod(buildClientMethod());
85+
builder.addMethod(initializeServiceClientConfigMethod());
86+
87+
return builder.build();
8488
}
8589

8690
private MethodSpec endpointDiscoveryEnabled() {
@@ -118,31 +122,29 @@ private MethodSpec endpointProviderMethod() {
118122
.build();
119123
}
120124

121-
122125
private MethodSpec buildClientMethod() {
123-
return MethodSpec.methodBuilder("buildClient")
124-
.addAnnotation(Override.class)
125-
.addModifiers(Modifier.PROTECTED, Modifier.FINAL)
126-
.returns(clientInterfaceName)
127-
.addStatement("$T clientConfiguration = super.syncClientConfiguration()", SdkClientConfiguration.class)
128-
.addStatement("this.validateClientOptions(clientConfiguration)")
129-
.addStatement("$T endpointOverride = null", URI.class)
130-
.addStatement("$T endpointProvider = clientConfiguration.option($T.ENDPOINT_PROVIDER)",
131-
EndpointProvider.class, SdkClientOption.class)
132-
.addCode("if (clientConfiguration.option($T.ENDPOINT_OVERRIDDEN) != null"
133-
+ "&& $T.TRUE.equals(clientConfiguration.option($T.ENDPOINT_OVERRIDDEN))) {"
134-
+ "endpointOverride = clientConfiguration.option($T.ENDPOINT);"
135-
+ "}",
136-
SdkClientOption.class, Boolean.class, SdkClientOption.class, SdkClientOption.class)
137-
.addStatement("$T serviceClientConfiguration = $T.builder()"
138-
+ ".overrideConfiguration(overrideConfiguration())"
139-
+ ".region(clientConfiguration.option($T.AWS_REGION))"
140-
+ ".endpointOverride(endpointOverride)"
141-
+ ".endpointProvider(endpointProvider)"
142-
+ ".build()",
143-
serviceConfigClassName, serviceConfigClassName, AwsClientOption.class)
144-
.addStatement("return new $T(serviceClientConfiguration, clientConfiguration)", clientClassName)
145-
.build();
126+
MethodSpec.Builder builder = MethodSpec.methodBuilder("buildClient")
127+
.addAnnotation(Override.class)
128+
.addModifiers(Modifier.PROTECTED, Modifier.FINAL)
129+
.returns(clientInterfaceName)
130+
.addStatement("$T clientConfiguration = super.syncClientConfiguration()",
131+
SdkClientConfiguration.class)
132+
.addStatement("this.validateClientOptions(clientConfiguration)")
133+
.addStatement("$T serviceClientConfiguration = initializeServiceClientConfig"
134+
+ "(clientConfiguration)",
135+
serviceConfigClassName);
136+
137+
builder.addStatement("$1T client = new $2T(serviceClientConfiguration, clientConfiguration)",
138+
clientInterfaceName, clientClassName);
139+
if (model.syncClientComposerClassName().isPresent()) {
140+
builder.addStatement("$1T composer = new $2T()",
141+
ClientComposer.class,
142+
PoetUtils.classNameFromFqcn(model.syncClientComposerClassName().get()));
143+
builder.addStatement("return ($T) composer.compose(client, clientConfiguration)", clientInterfaceName);
144+
} else {
145+
builder.addStatement("return client");
146+
}
147+
return builder.build();
146148
}
147149

148150
private MethodSpec tokenProviderMethodImpl() {
@@ -156,6 +158,29 @@ private MethodSpec tokenProviderMethodImpl() {
156158
.build();
157159
}
158160

161+
private MethodSpec initializeServiceClientConfigMethod() {
162+
return MethodSpec.methodBuilder("initializeServiceClientConfig").addModifiers(Modifier.PRIVATE)
163+
.addParameter(SdkClientConfiguration.class, "clientConfig")
164+
.returns(serviceConfigClassName)
165+
.addStatement("$T endpointOverride = null", URI.class)
166+
.addStatement("$T endpointProvider = clientConfig.option($T.ENDPOINT_PROVIDER)",
167+
EndpointProvider.class,
168+
SdkClientOption.class)
169+
.addCode("if (clientConfig.option($T.ENDPOINT_OVERRIDDEN) != null"
170+
+ "&& $T.TRUE.equals(clientConfig.option($T.ENDPOINT_OVERRIDDEN))) {"
171+
+ "endpointOverride = clientConfig.option($T.ENDPOINT);"
172+
+ "}",
173+
SdkClientOption.class, Boolean.class, SdkClientOption.class, SdkClientOption.class)
174+
.addStatement("return $T.builder()"
175+
+ ".overrideConfiguration(overrideConfiguration())"
176+
+ ".region(clientConfig.option($T.AWS_REGION))"
177+
+ ".endpointOverride(endpointOverride)"
178+
+ ".endpointProvider(endpointProvider)"
179+
+ ".build()",
180+
serviceConfigClassName, AwsClientOption.class)
181+
.build();
182+
}
183+
159184
@Override
160185
public ClassName className() {
161186
return builderClassName;

codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/DelegatingAsyncClientClass.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ protected MethodSpec.Builder operationBody(MethodSpec.Builder builder, Operation
192192
builder.addModifiers(PUBLIC)
193193
.addAnnotation(Override.class);
194194

195-
if (builder.parameters.size() < 1) {
195+
if (builder.parameters.isEmpty()) {
196196
throw new IllegalStateException("All client methods must have an argument");
197197
}
198198

codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/DelegatingSyncClientClass.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ protected MethodSpec.Builder operationBody(MethodSpec.Builder builder, Operation
151151
builder.addModifiers(PUBLIC)
152152
.addAnnotation(Override.class);
153153

154-
if (builder.parameters.size() < 1) {
154+
if (builder.parameters.isEmpty()) {
155155
throw new IllegalStateException("All client methods must have an argument");
156156
}
157157

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,19 @@ public static IntermediateModel customContentTypeModels() {
146146
return new IntermediateModelBuilder(models).build();
147147
}
148148

149+
public static IntermediateModel composedClientJsonServiceModels() {
150+
File serviceModel = new File(ClientTestModels.class.getResource("client/c2j/rest-json/service-2.json").getFile());
151+
File customizationModel =
152+
new File(ClientTestModels.class.getResource("client/c2j/composedclient/customization.config").getFile());
153+
CustomizationConfig customizationConfig = getCustomizationConfig(customizationModel);
154+
C2jModels models = C2jModels.builder()
155+
.serviceModel(getServiceModel(serviceModel))
156+
.customizationConfig(customizationConfig)
157+
.build();
158+
159+
return new IntermediateModelBuilder(models).build();
160+
}
161+
149162
public static IntermediateModel internalConfigModels() {
150163
File serviceModel = new File(ClientTestModels.class.getResource("client/c2j/internalconfig/service-2.json").getFile());
151164
File customizationModel = new File(ClientTestModels.class.getResource("client/c2j/internalconfig/customization.config").getFile());

codegen/src/test/java/software/amazon/awssdk/codegen/poet/builder/BuilderClassTest.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,11 @@ public void syncClientBuilderClass() throws Exception {
6868
validateGeneration(SyncClientBuilderClass::new, "test-sync-client-builder-class.java");
6969
}
7070

71+
@Test
72+
public void syncComposedClientBuilderClass() throws Exception {
73+
validateComposedClientGeneration(SyncClientBuilderClass::new, "test-composed-sync-client-builder-class.java");
74+
}
75+
7176
@Test
7277
public void asyncClientBuilderInterface() throws Exception {
7378
validateGeneration(AsyncClientBuilderInterface::new, "test-async-client-builder-interface.java");
@@ -78,10 +83,20 @@ public void asyncClientBuilderClass() throws Exception {
7883
validateGeneration(AsyncClientBuilderClass::new, "test-async-client-builder-class.java");
7984
}
8085

86+
@Test
87+
public void asyncComposedClientBuilderClass() throws Exception {
88+
validateComposedClientGeneration(AsyncClientBuilderClass::new, "test-composed-async-client-builder-class.java");
89+
}
90+
8191
private void validateGeneration(Function<IntermediateModel, ClassSpec> generatorConstructor, String expectedClassName) {
8292
assertThat(generatorConstructor.apply(ClientTestModels.restJsonServiceModels()), generatesTo(expectedClassName));
8393
}
8494

95+
private void validateComposedClientGeneration(Function<IntermediateModel, ClassSpec> generatorConstructor,
96+
String expectedClassName) {
97+
assertThat(generatorConstructor.apply(ClientTestModels.composedClientJsonServiceModels()), generatesTo(expectedClassName));
98+
}
99+
85100
private void validateBearerAuthGeneration(Function<IntermediateModel, ClassSpec> generatorConstructor,
86101
String expectedClassName) {
87102
assertThat(generatorConstructor.apply(ClientTestModels.bearerAuthServiceModels()), generatesTo(expectedClassName));

codegen/src/test/java/software/amazon/awssdk/codegen/poet/common/UserAgentClassSpecTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
public class UserAgentClassSpecTest {
2727

2828
@Test
29-
public void testGeneratedResponseForSyncOperations() {
29+
void testUserAgentClass() {
3030
ClassSpec useragentspec = new UserAgentUtilsSpec(ClientTestModels.restJsonServiceModels());
3131
assertThat(useragentspec, generatesTo("test-user-agent-class.java"));
3232
}

0 commit comments

Comments
 (0)