Skip to content

Commit d1f46a6

Browse files
authored
Generate DelegatingS3Client (#3745)
* Change DelegatingS3AsyncClient from internal to protected API class * Move DelegatingS3AsyncClient out of internal package * Generate DelegatingS3Client * Fix poet functional tests * Refactor add consumer method * Fix sonarcloud bug - add override and make protected * Refactoring - Address comments pt 1 * Refactor poetSpec - addressing comments pt2 * Refactoring - address comments pt3 * Refactor async client classes * Refactoring - address comments pt4
1 parent a99d4da commit d1f46a6

29 files changed

+2309
-1130
lines changed

codegen/src/main/java/software/amazon/awssdk/codegen/emitters/tasks/SyncClientGeneratorTasks.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import software.amazon.awssdk.codegen.poet.builder.SyncClientBuilderClass;
2424
import software.amazon.awssdk.codegen.poet.builder.SyncClientBuilderInterface;
2525
import software.amazon.awssdk.codegen.poet.client.ClientSimpleMethodsIntegrationTests;
26+
import software.amazon.awssdk.codegen.poet.client.DelegatingSyncClientClass;
2627
import software.amazon.awssdk.codegen.poet.client.SyncClientClass;
2728
import software.amazon.awssdk.codegen.poet.client.SyncClientInterface;
2829
import software.amazon.awssdk.codegen.poet.endpointdiscovery.EndpointDiscoveryCacheLoaderGenerator;
@@ -53,13 +54,20 @@ protected List<GeneratorTask> createTasks() throws Exception {
5354
if (model.getEndpointOperation().isPresent()) {
5455
tasks.add(createEndpointDiscoveryCacheLoaderTask());
5556
}
57+
if (model.getCustomizationConfig().isDelegateSyncClientClass()) {
58+
tasks.add(createDecoratorClientClassTask());
59+
}
5660
return tasks;
5761
}
5862

5963
private GeneratorTask createClientClassTask() throws IOException {
6064
return createPoetGeneratorTask(new SyncClientClass(generatorTaskParams));
6165
}
6266

67+
private GeneratorTask createDecoratorClientClassTask() throws IOException {
68+
return createPoetGeneratorTask(new DelegatingSyncClientClass(model));
69+
}
70+
6371
private GeneratorTask createClientBuilderTask() throws IOException {
6472
return createPoetGeneratorTask(new SyncClientBuilderClass(model));
6573
}

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
@@ -206,6 +206,11 @@ public class CustomizationConfig {
206206
*/
207207
private boolean delegateAsyncClientClass;
208208

209+
/**
210+
* Whether to generate an abstract decorator class that delegates to the sync service client
211+
*/
212+
private boolean delegateSyncClientClass;
213+
209214
/**
210215
* Whether to skip generating endpoint tests from endpoint-tests.json
211216
*/
@@ -547,6 +552,14 @@ public void setDelegateAsyncClientClass(boolean delegateAsyncClientClass) {
547552
this.delegateAsyncClientClass = delegateAsyncClientClass;
548553
}
549554

555+
public boolean isDelegateSyncClientClass() {
556+
return delegateSyncClientClass;
557+
}
558+
559+
public void setDelegateSyncClientClass(boolean delegateSyncClientClass) {
560+
this.delegateSyncClientClass = delegateSyncClientClass;
561+
}
562+
550563
public boolean isSkipEndpointTestGeneration() {
551564
return skipEndpointTestGeneration;
552565
}

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

Lines changed: 100 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@
1515

1616
package software.amazon.awssdk.codegen.poet.client;
1717

18-
import static com.squareup.javapoet.TypeSpec.Builder;
1918
import static java.util.Collections.singletonList;
19+
import static java.util.stream.Collectors.toList;
2020
import static javax.lang.model.element.Modifier.FINAL;
2121
import static javax.lang.model.element.Modifier.PRIVATE;
22+
import static javax.lang.model.element.Modifier.PROTECTED;
23+
import static javax.lang.model.element.Modifier.PUBLIC;
2224
import static javax.lang.model.element.Modifier.STATIC;
2325
import static software.amazon.awssdk.codegen.internal.Constant.EVENT_PUBLISHER_PARAM_NAME;
2426
import static software.amazon.awssdk.codegen.poet.client.ClientClassUtils.addS3ArnableFieldCode;
@@ -35,13 +37,15 @@
3537
import com.squareup.javapoet.TypeSpec;
3638
import java.net.URI;
3739
import java.nio.ByteBuffer;
40+
import java.util.ArrayList;
3841
import java.util.Collections;
42+
import java.util.Comparator;
3943
import java.util.List;
4044
import java.util.concurrent.CompletableFuture;
4145
import java.util.concurrent.Executor;
4246
import java.util.concurrent.ScheduledExecutorService;
4347
import java.util.stream.Collectors;
44-
import javax.lang.model.element.Modifier;
48+
import java.util.stream.Stream;
4549
import org.reactivestreams.Publisher;
4650
import org.slf4j.Logger;
4751
import org.slf4j.LoggerFactory;
@@ -99,66 +103,105 @@ public AsyncClientClass(GeneratorTaskParams dependencies) {
99103
}
100104

101105
@Override
102-
public TypeSpec poetSpec() {
106+
protected TypeSpec.Builder createTypeSpec() {
107+
return PoetUtils.createClassBuilder(className);
108+
}
109+
110+
@Override
111+
protected void addInterfaceClass(TypeSpec.Builder type) {
103112
ClassName interfaceClass = poetExtensions.getClientClass(model.getMetadata().getAsyncInterface());
104-
Builder classBuilder = PoetUtils.createClassBuilder(className);
105-
classBuilder.addAnnotation(SdkInternalApi.class)
106-
.addModifiers(Modifier.FINAL)
107-
.addField(FieldSpec.builder(ClassName.get(Logger.class), "log")
108-
.addModifiers(Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL)
109-
.initializer("$T.getLogger($T.class)", LoggerFactory.class,
110-
className)
111-
.build())
112-
.addField(AsyncClientHandler.class, "clientHandler", Modifier.PRIVATE, Modifier.FINAL)
113-
.addField(protocolSpec.protocolFactory(model))
114-
.addField(SdkClientConfiguration.class, "clientConfiguration", Modifier.PRIVATE, Modifier.FINAL)
115-
.addSuperinterface(interfaceClass)
116-
.addJavadoc("Internal implementation of {@link $1T}.\n\n@see $1T#builder()",
117-
interfaceClass)
118-
.addMethod(constructor(classBuilder))
119-
.addMethod(nameMethod())
120-
.addMethods(operations())
121-
.addMethod(closeMethod())
122-
.addMethods(protocolSpec.additionalMethods())
123-
.addMethod(protocolSpec.initProtocolFactory(model))
124-
.addMethod(resolveMetricPublishersMethod());
113+
type.addSuperinterface(interfaceClass)
114+
.addJavadoc("Internal implementation of {@link $1T}.\n\n@see $1T#builder()", interfaceClass);
115+
}
116+
117+
@Override
118+
protected void addAnnotations(TypeSpec.Builder type) {
119+
type.addAnnotation(SdkInternalApi.class);
120+
}
121+
122+
@Override
123+
protected void addModifiers(TypeSpec.Builder type) {
124+
type.addModifiers(FINAL);
125+
}
126+
127+
@Override
128+
protected void addFields(TypeSpec.Builder type) {
129+
type.addField(FieldSpec.builder(ClassName.get(Logger.class), "log")
130+
.addModifiers(PRIVATE, STATIC, FINAL)
131+
.initializer("$T.getLogger($T.class)", LoggerFactory.class,
132+
className)
133+
.build())
134+
.addField(AsyncClientHandler.class, "clientHandler", PRIVATE, FINAL)
135+
.addField(protocolSpec.protocolFactory(model))
136+
.addField(SdkClientConfiguration.class, "clientConfiguration", PRIVATE, FINAL);
125137

126138
// Kinesis doesn't support CBOR for STS yet so need another protocol factory for JSON
127139
if (model.getMetadata().isCborProtocol()) {
128-
classBuilder.addField(AwsJsonProtocolFactory.class, "jsonProtocolFactory", Modifier.PRIVATE, Modifier.FINAL);
140+
type.addField(AwsJsonProtocolFactory.class, "jsonProtocolFactory", PRIVATE, FINAL);
129141
}
130142

143+
model.getEndpointOperation().ifPresent(
144+
o -> type.addField(EndpointDiscoveryRefreshCache.class, "endpointDiscoveryCache", PRIVATE));
145+
}
146+
147+
@Override
148+
protected void addAdditionalMethods(TypeSpec.Builder type) {
149+
type.addMethod(constructor(type))
150+
.addMethod(nameMethod())
151+
.addMethods(protocolSpec.additionalMethods())
152+
.addMethod(protocolSpec.initProtocolFactory(model))
153+
.addMethod(resolveMetricPublishersMethod());
154+
131155
if (model.hasPaginators()) {
132-
classBuilder.addMethod(applyPaginatorUserAgentMethod(poetExtensions, model));
156+
type.addMethod(applyPaginatorUserAgentMethod(poetExtensions, model));
133157
}
134158

135159
if (model.containsRequestSigners() || model.containsRequestEventStreams() || hasStreamingV4AuthOperations()) {
136-
classBuilder.addMethod(applySignerOverrideMethod(poetExtensions, model));
137-
classBuilder.addMethod(isSignerOverriddenOnClientMethod());
160+
type.addMethod(applySignerOverrideMethod(poetExtensions, model));
161+
type.addMethod(isSignerOverriddenOnClientMethod());
138162
}
139163

140-
if (model.getCustomizationConfig().getUtilitiesMethod() != null) {
141-
classBuilder.addMethod(utilitiesMethod());
142-
}
164+
protocolSpec.createErrorResponseHandler().ifPresent(type::addMethod);
165+
}
143166

144-
model.getEndpointOperation().ifPresent(
145-
o -> classBuilder.addField(EndpointDiscoveryRefreshCache.class, "endpointDiscoveryCache", PRIVATE));
167+
@Override
168+
protected void addWaiterMethod(TypeSpec.Builder type) {
169+
type.addField(FieldSpec.builder(ClassName.get(ScheduledExecutorService.class), "executorService")
170+
.addModifiers(PRIVATE, FINAL)
171+
.build());
172+
173+
MethodSpec waiter = MethodSpec.methodBuilder("waiter")
174+
.addModifiers(PUBLIC)
175+
.addAnnotation(Override.class)
176+
.addStatement("return $T.builder().client(this)"
177+
+ ".scheduledExecutorService(executorService).build()",
178+
poetExtensions.getAsyncWaiterInterface())
179+
.returns(poetExtensions.getAsyncWaiterInterface())
180+
.build();
181+
182+
type.addMethod(waiter);
183+
}
146184

147-
protocolSpec.createErrorResponseHandler().ifPresent(classBuilder::addMethod);
185+
@Override
186+
protected List<MethodSpec> operations() {
187+
return model.getOperations().values().stream()
188+
.flatMap(this::operations)
189+
.sorted(Comparator.comparing(m -> m.name))
190+
.collect(toList());
191+
}
148192

149-
if (model.hasWaiters()) {
150-
classBuilder.addField(FieldSpec.builder(ClassName.get(ScheduledExecutorService.class), "executorService")
151-
.addModifiers(PRIVATE, FINAL)
152-
.build());
153-
classBuilder.addMethod(waiterImplMethod());
193+
private Stream<MethodSpec> operations(OperationModel opModel) {
194+
List<MethodSpec> methods = new ArrayList<>();
195+
methods.add(traditionalMethod(opModel));
196+
if (opModel.isPaginated()) {
197+
methods.add(paginatedTraditionalMethod(opModel));
154198
}
155-
156-
return classBuilder.build();
199+
return methods.stream();
157200
}
158201

159-
private MethodSpec constructor(Builder classBuilder) {
202+
private MethodSpec constructor(TypeSpec.Builder classBuilder) {
160203
MethodSpec.Builder builder = MethodSpec.constructorBuilder()
161-
.addModifiers(Modifier.PROTECTED)
204+
.addModifiers(PROTECTED)
162205
.addParameter(SdkClientConfiguration.class, "clientConfiguration")
163206
.addStatement("this.clientHandler = new $T(clientConfiguration)",
164207
AwsAsyncClientHandler.class)
@@ -174,8 +217,7 @@ private MethodSpec constructor(Builder classBuilder) {
174217
builder.addStatement("this.jsonProtocolFactory = init($T.builder()).build()", AwsJsonProtocolFactory.class);
175218
}
176219
if (hasOperationWithEventStreamOutput()) {
177-
classBuilder.addField(FieldSpec.builder(ClassName.get(Executor.class), "executor",
178-
Modifier.PRIVATE, Modifier.FINAL)
220+
classBuilder.addField(FieldSpec.builder(ClassName.get(Executor.class), "executor", PRIVATE, FINAL)
179221
.build());
180222
builder.addStatement("this.executor = clientConfiguration.option($T.FUTURE_COMPLETION_EXECUTOR)",
181223
SdkAdvancedAsyncClientOption.class);
@@ -216,24 +258,27 @@ private boolean hasOperationWithEventStreamOutput() {
216258
private MethodSpec nameMethod() {
217259
return MethodSpec.methodBuilder("serviceName")
218260
.addAnnotation(Override.class)
219-
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
261+
.addModifiers(PUBLIC, FINAL)
220262
.returns(String.class)
221263
.addStatement("return SERVICE_NAME")
222264
.build();
223265
}
224266

225-
private MethodSpec closeMethod() {
226-
return MethodSpec.methodBuilder("close")
227-
.addAnnotation(Override.class)
228-
.addModifiers(Modifier.PUBLIC)
229-
.addStatement("$N.close()", "clientHandler")
230-
.build();
267+
@Override
268+
protected void addCloseMethod(TypeSpec.Builder type) {
269+
MethodSpec method = MethodSpec.methodBuilder("close")
270+
.addAnnotation(Override.class)
271+
.addModifiers(PUBLIC)
272+
.addStatement("$N.close()", "clientHandler")
273+
.build();
274+
275+
type.addMethod(method);
231276
}
232277

233278
@Override
234279
protected MethodSpec.Builder operationBody(MethodSpec.Builder builder, OperationModel opModel) {
235280

236-
builder.addModifiers(Modifier.PUBLIC)
281+
builder.addModifiers(PUBLIC)
237282
.addAnnotation(Override.class);
238283

239284
builder.addStatement("$T<$T> metricPublishers = "
@@ -362,7 +407,7 @@ protected MethodSpec.Builder operationBody(MethodSpec.Builder builder, Operation
362407

363408
@Override
364409
protected MethodSpec.Builder paginatedMethodBody(MethodSpec.Builder builder, OperationModel opModel) {
365-
return builder.addModifiers(Modifier.PUBLIC)
410+
return builder.addModifiers(PUBLIC)
366411
.addStatement("return new $T(this, applyPaginatorUserAgent($L))",
367412
poetExtensions.getResponseClassForPaginatedAsyncOperation(opModel.getOperationName()),
368413
opModel.getInput().getVariableName());
@@ -434,25 +479,14 @@ protected MethodSpec utilitiesMethod() {
434479

435480
return MethodSpec.methodBuilder(UtilitiesMethod.METHOD_NAME)
436481
.returns(returnType)
437-
.addModifiers(Modifier.PUBLIC)
482+
.addModifiers(PUBLIC)
438483
.addAnnotation(Override.class)
439484
.addStatement("return $T.create($L)",
440485
instanceType,
441486
String.join(",", config.getCreateMethodParams()))
442487
.build();
443488
}
444489

445-
private MethodSpec waiterImplMethod() {
446-
return MethodSpec.methodBuilder("waiter")
447-
.addModifiers(Modifier.PUBLIC)
448-
.addAnnotation(Override.class)
449-
.addStatement("return $T.builder().client(this)"
450-
+ ".scheduledExecutorService(executorService).build()",
451-
poetExtensions.getAsyncWaiterInterface())
452-
.returns(poetExtensions.getAsyncWaiterInterface())
453-
.build();
454-
}
455-
456490
private MethodSpec resolveMetricPublishersMethod() {
457491
String clientConfigName = "clientConfiguration";
458492
String requestOverrideConfigName = "requestOverrideConfiguration";

0 commit comments

Comments
 (0)