Skip to content

Commit c3d4502

Browse files
committed
Wraps s3 client in cross regional client when enabled
1 parent a87b09a commit c3d4502

File tree

22 files changed

+747
-93
lines changed

22 files changed

+747
-93
lines changed

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,14 @@ public class CustomizationConfig {
212212
*/
213213
private boolean delegateSyncClientClass;
214214

215+
/**
216+
* Fully qualified name of a class that given the default client instance can return the final client instance,
217+
* for instance by decorating the client with specific-purpose implementations of the client interface.
218+
* The class should expose one async and one sync public static method that takes an instance of the client class
219+
* and the client configuration and returns the same type for the client. See S3 customization.config for an example.
220+
*/
221+
private String clientComposerFactory;
222+
215223
/**
216224
* Whether to skip generating endpoint tests from endpoint-tests.json
217225
*/
@@ -561,6 +569,14 @@ public void setDelegateAsyncClientClass(boolean delegateAsyncClientClass) {
561569
this.delegateAsyncClientClass = delegateAsyncClientClass;
562570
}
563571

572+
public String getClientComposerFactory() {
573+
return clientComposerFactory;
574+
}
575+
576+
public void setClientComposerFactory(String clientComposerFactory) {
577+
this.clientComposerFactory = clientComposerFactory;
578+
}
579+
564580
public boolean isDelegateSyncClientClass() {
565581
return delegateSyncClientClass;
566582
}

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

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

238+
public Optional<String> clientComposerClassName() {
239+
if (customizationConfig.getClientComposerFactory() != null) {
240+
return Optional.of(customizationConfig.getClientComposerFactory());
241+
}
242+
return Optional.empty();
243+
}
244+
238245
public String getFileHeader() {
239246
return FILE_HEADER;
240247
}

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

Lines changed: 47 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,10 @@ public TypeSpec poetSpec() {
8080
builder.addMethod(bearerTokenProviderMethod());
8181
}
8282

83-
return builder.addMethod(buildClientMethod()).build();
83+
builder.addMethod(buildClientMethod());
84+
builder.addMethod(initializeServiceClientConfigMethod());
85+
86+
return builder.build();
8487
}
8588

8689
private MethodSpec endpointDiscoveryEnabled() {
@@ -120,30 +123,26 @@ private MethodSpec endpointProviderMethod() {
120123
}
121124

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.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();
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.asyncClientConfiguration()",
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.clientComposerClassName().isPresent()) {
140+
builder.addStatement("return $T.composeAsync(client, clientConfiguration)",
141+
PoetUtils.classNameFromFqcn(model.clientComposerClassName().get()));
142+
} else {
143+
builder.addStatement("return client");
144+
}
145+
return builder.build();
147146
}
148147

149148
private MethodSpec bearerTokenProviderMethod() {
@@ -157,6 +156,29 @@ private MethodSpec bearerTokenProviderMethod() {
157156
.build();
158157
}
159158

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

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

Lines changed: 47 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,10 @@ public TypeSpec poetSpec() {
8080
builder.addMethod(tokenProviderMethodImpl());
8181
}
8282

83-
return builder.addMethod(buildClientMethod()).build();
83+
builder.addMethod(buildClientMethod());
84+
builder.addMethod(initializeServiceClientConfigMethod());
85+
86+
return builder.build();
8487
}
8588

8689
private MethodSpec endpointDiscoveryEnabled() {
@@ -120,29 +123,26 @@ private MethodSpec endpointProviderMethod() {
120123

121124

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.clientComposerClassName().isPresent()) {
140+
builder.addStatement("return $T.composeSync(client, clientConfiguration)",
141+
PoetUtils.classNameFromFqcn(model.clientComposerClassName().get()));
142+
} else {
143+
builder.addStatement("return client");
144+
}
145+
return builder.build();
146146
}
147147

148148
private MethodSpec tokenProviderMethodImpl() {
@@ -156,6 +156,29 @@ private MethodSpec tokenProviderMethodImpl() {
156156
.build();
157157
}
158158

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

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+
public void testUserAgentClass() {
3030
ClassSpec useragentspec = new UserAgentUtilsSpec(ClientTestModels.restJsonServiceModels());
3131
assertThat(useragentspec, generatesTo("test-user-agent-class.java"));
3232
}

codegen/src/test/resources/software/amazon/awssdk/codegen/poet/builder/test-async-client-builder-class.java

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,20 @@ public DefaultJsonAsyncClientBuilder tokenProvider(SdkTokenProvider tokenProvide
3333
protected final JsonAsyncClient buildClient() {
3434
SdkClientConfiguration clientConfiguration = super.asyncClientConfiguration();
3535
this.validateClientOptions(clientConfiguration);
36+
JsonServiceClientConfiguration serviceClientConfiguration = initializeServiceClientConfig(clientConfiguration);
37+
JsonAsyncClient client = new DefaultJsonAsyncClient(serviceClientConfiguration, clientConfiguration);
38+
return client;
39+
}
40+
41+
private JsonServiceClientConfiguration initializeServiceClientConfig(SdkClientConfiguration clientConfig) {
3642
URI endpointOverride = null;
37-
EndpointProvider endpointProvider = clientConfiguration.option(SdkClientOption.ENDPOINT_PROVIDER);
38-
if (clientConfiguration.option(SdkClientOption.ENDPOINT_OVERRIDDEN) != null
39-
&& Boolean.TRUE.equals(clientConfiguration.option(SdkClientOption.ENDPOINT_OVERRIDDEN))) {
40-
endpointOverride = clientConfiguration.option(SdkClientOption.ENDPOINT);
43+
EndpointProvider endpointProvider = clientConfig.option(SdkClientOption.ENDPOINT_PROVIDER);
44+
if (clientConfig.option(SdkClientOption.ENDPOINT_OVERRIDDEN) != null
45+
&& Boolean.TRUE.equals(clientConfig.option(SdkClientOption.ENDPOINT_OVERRIDDEN))) {
46+
endpointOverride = clientConfig.option(SdkClientOption.ENDPOINT);
4147
}
42-
JsonServiceClientConfiguration serviceClientConfiguration = JsonServiceClientConfiguration.builder()
43-
.overrideConfiguration(overrideConfiguration()).region(clientConfiguration.option(AwsClientOption.AWS_REGION))
44-
.endpointOverride(endpointOverride).endpointProvider(endpointProvider).build();
45-
return new DefaultJsonAsyncClient(serviceClientConfiguration, clientConfiguration);
48+
return JsonServiceClientConfiguration.builder().overrideConfiguration(overrideConfiguration())
49+
.region(clientConfig.option(AwsClientOption.AWS_REGION)).endpointOverride(endpointOverride)
50+
.endpointProvider(endpointProvider).build();
4651
}
4752
}

0 commit comments

Comments
 (0)