Skip to content

Generate DelegatingS3Client #3745

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Feb 10, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import software.amazon.awssdk.codegen.poet.builder.SyncClientBuilderClass;
import software.amazon.awssdk.codegen.poet.builder.SyncClientBuilderInterface;
import software.amazon.awssdk.codegen.poet.client.ClientSimpleMethodsIntegrationTests;
import software.amazon.awssdk.codegen.poet.client.DelegatingSyncClientClass;
import software.amazon.awssdk.codegen.poet.client.SyncClientClass;
import software.amazon.awssdk.codegen.poet.client.SyncClientInterface;
import software.amazon.awssdk.codegen.poet.endpointdiscovery.EndpointDiscoveryCacheLoaderGenerator;
Expand Down Expand Up @@ -53,13 +54,20 @@ protected List<GeneratorTask> createTasks() throws Exception {
if (model.getEndpointOperation().isPresent()) {
tasks.add(createEndpointDiscoveryCacheLoaderTask());
}
if (model.getCustomizationConfig().isDelegateSyncClientClass()) {
tasks.add(createDecoratorClientClassTask());
}
return tasks;
}

private GeneratorTask createClientClassTask() throws IOException {
return createPoetGeneratorTask(new SyncClientClass(generatorTaskParams));
}

private GeneratorTask createDecoratorClientClassTask() throws IOException {
return createPoetGeneratorTask(new DelegatingSyncClientClass(model));
}

private GeneratorTask createClientBuilderTask() throws IOException {
return createPoetGeneratorTask(new SyncClientBuilderClass(model));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,11 @@ public class CustomizationConfig {
*/
private boolean delegateAsyncClientClass;

/**
* Whether to generate an abstract decorator class that delegates to the sync service client
*/
private boolean delegateSyncClientClass;

/**
* Whether to skip generating endpoint tests from endpoint-tests.json
*/
Expand Down Expand Up @@ -547,6 +552,14 @@ public void setDelegateAsyncClientClass(boolean delegateAsyncClientClass) {
this.delegateAsyncClientClass = delegateAsyncClientClass;
}

public boolean isDelegateSyncClientClass() {
return delegateSyncClientClass;
}

public void setDelegateSyncClientClass(boolean delegateSyncClientClass) {
this.delegateSyncClientClass = delegateSyncClientClass;
}

public boolean isSkipEndpointTestGeneration() {
return skipEndpointTestGeneration;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@

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

import static com.squareup.javapoet.TypeSpec.Builder;
import static java.util.Collections.singletonList;
import static java.util.stream.Collectors.toList;
import static javax.lang.model.element.Modifier.FINAL;
import static javax.lang.model.element.Modifier.PRIVATE;
import static javax.lang.model.element.Modifier.PROTECTED;
import static javax.lang.model.element.Modifier.PUBLIC;
import static javax.lang.model.element.Modifier.STATIC;
import static software.amazon.awssdk.codegen.internal.Constant.EVENT_PUBLISHER_PARAM_NAME;
import static software.amazon.awssdk.codegen.poet.client.ClientClassUtils.addS3ArnableFieldCode;
Expand All @@ -35,13 +37,15 @@
import com.squareup.javapoet.TypeSpec;
import java.net.URI;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.stream.Collectors;
import javax.lang.model.element.Modifier;
import java.util.stream.Stream;
import org.reactivestreams.Publisher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -99,66 +103,105 @@ public AsyncClientClass(GeneratorTaskParams dependencies) {
}

@Override
public TypeSpec poetSpec() {
protected TypeSpec.Builder createTypeSpec() {
return PoetUtils.createClassBuilder(className);
}

@Override
protected void addInterfaceClass(TypeSpec.Builder type) {
ClassName interfaceClass = poetExtensions.getClientClass(model.getMetadata().getAsyncInterface());
Builder classBuilder = PoetUtils.createClassBuilder(className);
classBuilder.addAnnotation(SdkInternalApi.class)
.addModifiers(Modifier.FINAL)
.addField(FieldSpec.builder(ClassName.get(Logger.class), "log")
.addModifiers(Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL)
.initializer("$T.getLogger($T.class)", LoggerFactory.class,
className)
.build())
.addField(AsyncClientHandler.class, "clientHandler", Modifier.PRIVATE, Modifier.FINAL)
.addField(protocolSpec.protocolFactory(model))
.addField(SdkClientConfiguration.class, "clientConfiguration", Modifier.PRIVATE, Modifier.FINAL)
.addSuperinterface(interfaceClass)
.addJavadoc("Internal implementation of {@link $1T}.\n\n@see $1T#builder()",
interfaceClass)
.addMethod(constructor(classBuilder))
.addMethod(nameMethod())
.addMethods(operations())
.addMethod(closeMethod())
.addMethods(protocolSpec.additionalMethods())
.addMethod(protocolSpec.initProtocolFactory(model))
.addMethod(resolveMetricPublishersMethod());
type.addSuperinterface(interfaceClass)
.addJavadoc("Internal implementation of {@link $1T}.\n\n@see $1T#builder()", interfaceClass);
}

@Override
protected void addAnnotations(TypeSpec.Builder type) {
type.addAnnotation(SdkInternalApi.class);
}

@Override
protected void addModifiers(TypeSpec.Builder type) {
type.addModifiers(FINAL);
}

@Override
protected void addFields(TypeSpec.Builder type) {
type.addField(FieldSpec.builder(ClassName.get(Logger.class), "log")
.addModifiers(PRIVATE, STATIC, FINAL)
.initializer("$T.getLogger($T.class)", LoggerFactory.class,
className)
.build())
.addField(AsyncClientHandler.class, "clientHandler", PRIVATE, FINAL)
.addField(protocolSpec.protocolFactory(model))
.addField(SdkClientConfiguration.class, "clientConfiguration", PRIVATE, FINAL);

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

model.getEndpointOperation().ifPresent(
o -> type.addField(EndpointDiscoveryRefreshCache.class, "endpointDiscoveryCache", PRIVATE));
}

@Override
protected void addAdditionalMethods(TypeSpec.Builder type) {
type.addMethod(constructor(type))
.addMethod(nameMethod())
.addMethods(protocolSpec.additionalMethods())
.addMethod(protocolSpec.initProtocolFactory(model))
.addMethod(resolveMetricPublishersMethod());

if (model.hasPaginators()) {
classBuilder.addMethod(applyPaginatorUserAgentMethod(poetExtensions, model));
type.addMethod(applyPaginatorUserAgentMethod(poetExtensions, model));
}

if (model.containsRequestSigners() || model.containsRequestEventStreams() || hasStreamingV4AuthOperations()) {
classBuilder.addMethod(applySignerOverrideMethod(poetExtensions, model));
classBuilder.addMethod(isSignerOverriddenOnClientMethod());
type.addMethod(applySignerOverrideMethod(poetExtensions, model));
type.addMethod(isSignerOverriddenOnClientMethod());
}

if (model.getCustomizationConfig().getUtilitiesMethod() != null) {
classBuilder.addMethod(utilitiesMethod());
}
protocolSpec.createErrorResponseHandler().ifPresent(type::addMethod);
}

model.getEndpointOperation().ifPresent(
o -> classBuilder.addField(EndpointDiscoveryRefreshCache.class, "endpointDiscoveryCache", PRIVATE));
@Override
protected void addWaiterMethod(TypeSpec.Builder type) {
type.addField(FieldSpec.builder(ClassName.get(ScheduledExecutorService.class), "executorService")
.addModifiers(PRIVATE, FINAL)
.build());

MethodSpec waiter = MethodSpec.methodBuilder("waiter")
.addModifiers(PUBLIC)
.addAnnotation(Override.class)
.addStatement("return $T.builder().client(this)"
+ ".scheduledExecutorService(executorService).build()",
poetExtensions.getAsyncWaiterInterface())
.returns(poetExtensions.getAsyncWaiterInterface())
.build();

type.addMethod(waiter);
}

protocolSpec.createErrorResponseHandler().ifPresent(classBuilder::addMethod);
@Override
protected List<MethodSpec> operations() {
return model.getOperations().values().stream()
.flatMap(this::operations)
.sorted(Comparator.comparing(m -> m.name))
.collect(toList());
}

if (model.hasWaiters()) {
classBuilder.addField(FieldSpec.builder(ClassName.get(ScheduledExecutorService.class), "executorService")
.addModifiers(PRIVATE, FINAL)
.build());
classBuilder.addMethod(waiterImplMethod());
private Stream<MethodSpec> operations(OperationModel opModel) {
List<MethodSpec> methods = new ArrayList<>();
methods.add(traditionalMethod(opModel));
if (opModel.isPaginated()) {
methods.add(paginatedTraditionalMethod(opModel));
}

return classBuilder.build();
return methods.stream();
}

private MethodSpec constructor(Builder classBuilder) {
private MethodSpec constructor(TypeSpec.Builder classBuilder) {
MethodSpec.Builder builder = MethodSpec.constructorBuilder()
.addModifiers(Modifier.PROTECTED)
.addModifiers(PROTECTED)
.addParameter(SdkClientConfiguration.class, "clientConfiguration")
.addStatement("this.clientHandler = new $T(clientConfiguration)",
AwsAsyncClientHandler.class)
Expand All @@ -174,8 +217,7 @@ private MethodSpec constructor(Builder classBuilder) {
builder.addStatement("this.jsonProtocolFactory = init($T.builder()).build()", AwsJsonProtocolFactory.class);
}
if (hasOperationWithEventStreamOutput()) {
classBuilder.addField(FieldSpec.builder(ClassName.get(Executor.class), "executor",
Modifier.PRIVATE, Modifier.FINAL)
classBuilder.addField(FieldSpec.builder(ClassName.get(Executor.class), "executor", PRIVATE, FINAL)
.build());
builder.addStatement("this.executor = clientConfiguration.option($T.FUTURE_COMPLETION_EXECUTOR)",
SdkAdvancedAsyncClientOption.class);
Expand Down Expand Up @@ -216,24 +258,27 @@ private boolean hasOperationWithEventStreamOutput() {
private MethodSpec nameMethod() {
return MethodSpec.methodBuilder("serviceName")
.addAnnotation(Override.class)
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addModifiers(PUBLIC, FINAL)
.returns(String.class)
.addStatement("return SERVICE_NAME")
.build();
}

private MethodSpec closeMethod() {
return MethodSpec.methodBuilder("close")
.addAnnotation(Override.class)
.addModifiers(Modifier.PUBLIC)
.addStatement("$N.close()", "clientHandler")
.build();
@Override
protected void addCloseMethod(TypeSpec.Builder type) {
MethodSpec method = MethodSpec.methodBuilder("close")
.addAnnotation(Override.class)
.addModifiers(PUBLIC)
.addStatement("$N.close()", "clientHandler")
.build();

type.addMethod(method);
}

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

builder.addModifiers(Modifier.PUBLIC)
builder.addModifiers(PUBLIC)
.addAnnotation(Override.class);

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

@Override
protected MethodSpec.Builder paginatedMethodBody(MethodSpec.Builder builder, OperationModel opModel) {
return builder.addModifiers(Modifier.PUBLIC)
return builder.addModifiers(PUBLIC)
.addStatement("return new $T(this, applyPaginatorUserAgent($L))",
poetExtensions.getResponseClassForPaginatedAsyncOperation(opModel.getOperationName()),
opModel.getInput().getVariableName());
Expand Down Expand Up @@ -434,25 +479,14 @@ protected MethodSpec utilitiesMethod() {

return MethodSpec.methodBuilder(UtilitiesMethod.METHOD_NAME)
.returns(returnType)
.addModifiers(Modifier.PUBLIC)
.addModifiers(PUBLIC)
.addAnnotation(Override.class)
.addStatement("return $T.create($L)",
instanceType,
String.join(",", config.getCreateMethodParams()))
.build();
}

private MethodSpec waiterImplMethod() {
return MethodSpec.methodBuilder("waiter")
.addModifiers(Modifier.PUBLIC)
.addAnnotation(Override.class)
.addStatement("return $T.builder().client(this)"
+ ".scheduledExecutorService(executorService).build()",
poetExtensions.getAsyncWaiterInterface())
.returns(poetExtensions.getAsyncWaiterInterface())
.build();
}

private MethodSpec resolveMetricPublishersMethod() {
String clientConfigName = "clientConfiguration";
String requestOverrideConfigName = "requestOverrideConfiguration";
Expand Down
Loading