Skip to content

Commit d50b76b

Browse files
committed
Subclass events to do visitor dispatch
Generate events as discrete subclasses of the base shape class that take care dispatching to the correct visitor method. The base shape is what we would generate as a normal model class for a non request/response shape today. However, because they need to be extended, they are no longer final; insteach any method that can be made final (such as property getters) is made final. This commit also generates static builder methods on the event stream interface to make discovery and creation of events easier.
1 parent ad6544c commit d50b76b

File tree

84 files changed

+5558
-3753
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

84 files changed

+5558
-3753
lines changed

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,4 +211,9 @@
211211
<Method name="execute"/>
212212
<Bug pattern="RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE"/>
213213
</Match>
214+
215+
<Match>
216+
<Package name="~software\.amazon\.awssdk\.services\.protocolrestjson\.model\..*"/>
217+
<Bug pattern="NM_SAME_SIMPLE_NAME_AS_INTERFACE, NM_SAME_SIMPLE_NAME_AS_SUPERCLASS"/>
218+
</Match>
214219
</FindBugsFilter>

codegen/src/main/java/software/amazon/awssdk/codegen/customization/processors/DefaultCustomizationProcessor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public static CodegenCustomizationProcessor getProcessorFor(
3434
new ShapeSubstitutionsProcessor(config.getShapeSubstitutions()),
3535
new OperationModifiersProcessor(config.getOperationModifiers()),
3636
new RemoveExceptionMessagePropertyProcessor(),
37-
new ExcludeEventNameFromVisitMethodProcessor()
37+
new UseLegacyEventGenerationSchemeProcessor()
3838
);
3939
}
4040
}
Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,13 @@
2727
import software.amazon.awssdk.codegen.model.service.ServiceModel;
2828

2929
/**
30-
* This process enforces constraints placed on the "excludeEventNameFromVisitMethod"; i.e. that no two members
30+
* This process enforces constraints placed on the "useLegacyEventGenerationSchemeProcessor"; i.e. that no two members
3131
* of the same event stream sharing the same shape have this customization enabled for them. This processor does not
3232
* modify the the service or intermediate model.
3333
*/
34-
public class ExcludeEventNameFromVisitMethodProcessor implements CodegenCustomizationProcessor {
35-
private static final String CUSTOMIZATION_NAME = "ExcludeEventNameFromVisitMethod";
36-
private static final Logger log = LoggerFactory.getLogger(ExcludeEventNameFromVisitMethodProcessor.class);
34+
public class UseLegacyEventGenerationSchemeProcessor implements CodegenCustomizationProcessor {
35+
private static final String CUSTOMIZATION_NAME = "UseLegacyEventGenerationScheme";
36+
private static final Logger log = LoggerFactory.getLogger(UseLegacyEventGenerationSchemeProcessor.class);
3737

3838
@Override
3939
public void preprocess(ServiceModel serviceModel) {
@@ -42,14 +42,15 @@ public void preprocess(ServiceModel serviceModel) {
4242

4343
@Override
4444
public void postprocess(IntermediateModel intermediateModel) {
45-
Map<String, List<String>> excludeEventNameFromVisitMethod = intermediateModel.getCustomizationConfig()
46-
.getExcludeEventNameFromVisitMethod();
45+
Map<String, List<String>> useLegacyEventGenerationScheme = intermediateModel.getCustomizationConfig()
46+
.getUseLegacyEventGenerationScheme();
4747

48-
excludeEventNameFromVisitMethod.forEach((eventStream, members) -> {
48+
useLegacyEventGenerationScheme.forEach((eventStream, members) -> {
4949
ShapeModel shapeModel = getShapeByC2jName(intermediateModel, eventStream);
5050

5151
if (shapeModel == null || !shapeModel.isEventStream()) {
52-
log.warn("Encountered " + CUSTOMIZATION_NAME + " for unrecognized eventstream " + eventStream);
52+
log.warn(String.format("Encountered %s for unrecognized eventstream %s",
53+
CUSTOMIZATION_NAME, eventStream));
5354
return;
5455
}
5556

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

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,15 @@
1717

1818
import static software.amazon.awssdk.utils.FunctionalUtils.safeFunction;
1919

20-
import java.io.IOException;
2120
import java.util.ArrayList;
2221
import java.util.List;
22+
import java.util.Locale;
23+
import java.util.stream.Collectors;
2324
import software.amazon.awssdk.codegen.emitters.GeneratorTask;
2425
import software.amazon.awssdk.codegen.emitters.GeneratorTaskParams;
2526
import software.amazon.awssdk.codegen.emitters.PoetGeneratorTask;
27+
import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel;
28+
import software.amazon.awssdk.codegen.model.intermediate.MemberModel;
2629
import software.amazon.awssdk.codegen.model.intermediate.Metadata;
2730
import software.amazon.awssdk.codegen.model.intermediate.ShapeModel;
2831
import software.amazon.awssdk.codegen.model.intermediate.ShapeType;
@@ -31,20 +34,23 @@
3134
import software.amazon.awssdk.codegen.poet.model.AwsServiceBaseRequestSpec;
3235
import software.amazon.awssdk.codegen.poet.model.AwsServiceBaseResponseSpec;
3336
import software.amazon.awssdk.codegen.poet.model.AwsServiceModel;
37+
import software.amazon.awssdk.codegen.poet.model.EventModelSpec;
38+
import software.amazon.awssdk.codegen.poet.model.EventStreamSpecHelper;
3439
import software.amazon.awssdk.codegen.poet.model.ResponseMetadataSpec;
3540
import software.amazon.awssdk.codegen.poet.model.ServiceModelCopiers;
3641

3742
class ModelClassGeneratorTasks extends BaseGeneratorTasks {
38-
3943
private final String modelClassDir;
44+
private final IntermediateModel intermediateModel;
4045

4146
ModelClassGeneratorTasks(GeneratorTaskParams dependencies) {
4247
super(dependencies);
4348
this.modelClassDir = dependencies.getPathProvider().getModelDirectory();
49+
this.intermediateModel = dependencies.getModel();
4450
}
4551

4652
@Override
47-
protected List<GeneratorTask> createTasks() throws Exception {
53+
protected List<GeneratorTask> createTasks() {
4854
List<GeneratorTask> tasks = new ArrayList<>();
4955

5056
tasks.add(new PoetGeneratorTask(modelClassDir, model.getFileHeader(), new AwsServiceBaseRequestSpec(model)));
@@ -55,6 +61,8 @@ protected List<GeneratorTask> createTasks() throws Exception {
5561
.map(safeFunction(this::createTask))
5662
.forEach(tasks::add);
5763

64+
tasks.addAll(eventModelGenerationTasks());
65+
5866
new ServiceModelCopiers(model).copierSpecs().stream()
5967
.map(safeFunction(spec -> new PoetGeneratorTask(modelClassDir, model.getFileHeader(), spec)))
6068
.forEach(tasks::add);
@@ -72,7 +80,7 @@ private boolean shouldGenerateShape(ShapeModel shapeModel) {
7280
return true;
7381
}
7482

75-
private GeneratorTask createTask(ShapeModel shapeModel) throws IOException {
83+
private GeneratorTask createTask(ShapeModel shapeModel) {
7684
Metadata metadata = model.getMetadata();
7785
ClassSpec classSpec;
7886
if (shapeModel.getShapeType() == ShapeType.Enum) {
@@ -82,4 +90,25 @@ private GeneratorTask createTask(ShapeModel shapeModel) throws IOException {
8290
}
8391
return new PoetGeneratorTask(modelClassDir, model.getFileHeader(), classSpec);
8492
}
93+
94+
private List<GeneratorTask> eventModelGenerationTasks() {
95+
return model.getShapes().values().stream()
96+
.filter(ShapeModel::isEventStream)
97+
.flatMap(eventStream -> {
98+
EventStreamSpecHelper eventStreamSpecHelper = new EventStreamSpecHelper(eventStream,
99+
intermediateModel);
100+
101+
return eventStream.getMembers().stream()
102+
.filter(e -> e.getShape().isEvent())
103+
.filter(e -> !eventStreamSpecHelper.useLegacyGenerationScheme(e))
104+
.map(e -> createEventGenerationTask(e, eventStream));
105+
})
106+
.collect(Collectors.toList());
107+
}
108+
109+
private GeneratorTask createEventGenerationTask(MemberModel memberModel, ShapeModel eventStream) {
110+
EventModelSpec spec = new EventModelSpec(memberModel, eventStream, model);
111+
String outputDir = modelClassDir + "/" + eventStream.getShapeName().toLowerCase(Locale.ENGLISH);
112+
return new PoetGeneratorTask(outputDir, model.getFileHeader(), spec);
113+
}
85114
}

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

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -177,14 +177,13 @@ public class CustomizationConfig {
177177
private boolean allowEndpointOverrideForEndpointDiscoveryRequiredOperations = false;
178178

179179
/**
180-
* Customization to instruct the code generator *not* to include the event's name in the {@code Visitor#visit*()}
181-
* method; i.e. the signature of the method will be {@code void visit(<Event Shape type>)}.
180+
* Customization to instruct the code generator to use the legacy model generation scheme for the given events.
182181
* <p>
183182
* <b>NOTE</b>This customization is primarily here to preserve backwards compatibility with existing code before the
184183
* generation scheme for the visitor methods was changed. There should be no good reason to use this customization
185184
* for any other purpose.
186185
*/
187-
private Map<String, List<String>> excludeEventNameFromVisitMethod = new HashMap<>();
186+
private Map<String, List<String>> useLegacyEventGenerationScheme = new HashMap<>();
188187

189188
private CustomizationConfig() {
190189
}
@@ -465,11 +464,11 @@ public void setAllowEndpointOverrideForEndpointDiscoveryRequiredOperations(
465464
allowEndpointOverrideForEndpointDiscoveryRequiredOperations;
466465
}
467466

468-
public Map<String, List<String>> getExcludeEventNameFromVisitMethod() {
469-
return excludeEventNameFromVisitMethod;
467+
public Map<String, List<String>> getUseLegacyEventGenerationScheme() {
468+
return useLegacyEventGenerationScheme;
470469
}
471470

472-
public void setExcludeEventNameFromVisitMethod(Map<String, List<String>> excludeEventNameFromVisitMethod) {
473-
this.excludeEventNameFromVisitMethod = excludeEventNameFromVisitMethod;
471+
public void setUseLegacyEventGenerationScheme(Map<String, List<String>> useLegacyEventGenerationScheme) {
472+
this.useLegacyEventGenerationScheme = useLegacyEventGenerationScheme;
474473
}
475474
}

codegen/src/main/java/software/amazon/awssdk/codegen/poet/eventstream/EventStreamVisitorBuilderImplSpec.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,10 @@
2828
import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel;
2929
import software.amazon.awssdk.codegen.model.intermediate.MemberModel;
3030
import software.amazon.awssdk.codegen.model.intermediate.OperationModel;
31+
import software.amazon.awssdk.codegen.model.intermediate.ShapeModel;
3132
import software.amazon.awssdk.codegen.poet.PoetExtensions;
3233
import software.amazon.awssdk.codegen.poet.PoetUtils;
34+
import software.amazon.awssdk.codegen.poet.model.EventStreamSpecHelper;
3335

3436
/**
3537
* Generates the implementation for the builder of an event stream visitor.
@@ -41,6 +43,7 @@ public class EventStreamVisitorBuilderImplSpec extends EventStreamVisitorBuilder
4143
private final ClassName visitorType;
4244
private final ClassName visitorBuilderType;
4345
private final ClassName eventStreamBaseClass;
46+
private final EventStreamSpecHelper eventStreamSpecHelper;
4447

4548
public EventStreamVisitorBuilderImplSpec(GeneratorTaskParams params, OperationModel operationModel) {
4649
super(params.getPoetExtensions(), operationModel);
@@ -49,8 +52,10 @@ public EventStreamVisitorBuilderImplSpec(GeneratorTaskParams params, OperationMo
4952
this.opModel = operationModel;
5053
this.visitorType = poetExt.eventStreamResponseHandlerVisitorType(opModel);
5154
this.visitorBuilderType = poetExt.eventStreamResponseHandlerVisitorBuilderType(opModel);
52-
this.eventStreamBaseClass = poetExt.getModelClassFromShape(
53-
EventStreamUtils.getEventStreamInResponse(operationModel.getOutputShape()));
55+
56+
ShapeModel eventStream = EventStreamUtils.getEventStreamInResponse(operationModel.getOutputShape());
57+
this.eventStreamBaseClass = poetExt.getModelClassFromShape(eventStream);
58+
this.eventStreamSpecHelper = new EventStreamSpecHelper(eventStream, intermediateModel);
5459
}
5560

5661
@Override
@@ -110,7 +115,8 @@ protected MethodSpec.Builder applyVisitSubTypeMethodSpecUpdates(TypeSpec.Builder
110115

111116
constrBuilder.addStatement("this.$1L = builder.$1L != null ?\n"
112117
+ "builder.$1L :\n"
113-
+ "$2T.super::visit", consumerField.name, visitorType);
118+
+ "$2T.super::$3N", consumerField.name, visitorType,
119+
eventStreamSpecHelper.visitMethodName(event));
114120
return methodBuilder
115121
.addAnnotation(Override.class)
116122
.addStatement("$L.accept(event)", consumerField.name);

codegen/src/main/java/software/amazon/awssdk/codegen/poet/eventstream/EventStreamVisitorInterfaceSpec.java

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@
1919
import com.squareup.javapoet.MethodSpec;
2020
import com.squareup.javapoet.ParameterSpec;
2121
import com.squareup.javapoet.TypeSpec;
22-
import java.util.List;
23-
import java.util.Map;
2422
import javax.lang.model.element.Modifier;
2523
import software.amazon.awssdk.codegen.docs.DocumentationBuilder;
2624
import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel;
@@ -30,24 +28,24 @@
3028
import software.amazon.awssdk.codegen.poet.ClassSpec;
3129
import software.amazon.awssdk.codegen.poet.PoetExtensions;
3230
import software.amazon.awssdk.codegen.poet.PoetUtils;
33-
import software.amazon.awssdk.utils.internal.CodegenNamingUtils;
31+
import software.amazon.awssdk.codegen.poet.model.EventStreamSpecHelper;
3432

3533
/**
3634
* Spec for generated visitor interface.
3735
*/
3836
class EventStreamVisitorInterfaceSpec implements ClassSpec {
39-
private final IntermediateModel intermediateModel;
4037
private final PoetExtensions poetExt;
4138
private final OperationModel operationModel;
4239
private final ShapeModel eventStreamShape;
4340
private final ClassName eventStreamBaseClass;
41+
private final EventStreamSpecHelper eventStreamSpecHelper;
4442

4543
EventStreamVisitorInterfaceSpec(IntermediateModel intermediateModel, PoetExtensions poetExt, OperationModel operationModel) {
46-
this.intermediateModel = intermediateModel;
4744
this.poetExt = poetExt;
4845
this.operationModel = operationModel;
4946
this.eventStreamShape = EventStreamUtils.getEventStreamInResponse(operationModel.getOutputShape());
5047
this.eventStreamBaseClass = poetExt.getModelClassFromShape(eventStreamShape);
48+
this.eventStreamSpecHelper = new EventStreamSpecHelper(eventStreamShape, intermediateModel);
5149
}
5250

5351
@Override
@@ -126,10 +124,7 @@ protected MethodSpec.Builder applyVisitSubTypeMethodSpecUpdates(TypeSpec.Builder
126124
}
127125

128126
protected String visitorMethodName(MemberModel event) {
129-
if (!excludeNameFromVisitorName(event)) {
130-
return "visit" + CodegenNamingUtils.pascalCase(event.getName());
131-
}
132-
return "visit";
127+
return eventStreamSpecHelper.visitMethodName(event);
133128
}
134129

135130
private MethodSpec createBuilderMethodSpec() {
@@ -141,17 +136,4 @@ private MethodSpec createBuilderMethodSpec() {
141136
.addStatement("return new Default$LVisitorBuilder()", poetExt.getApiName(operationModel))
142137
.build();
143138
}
144-
145-
private boolean excludeNameFromVisitorName(MemberModel event) {
146-
Map<String, List<String>> excludeEventNameFromVisitMethod = intermediateModel.getCustomizationConfig()
147-
.getExcludeEventNameFromVisitMethod();
148-
149-
List<String> targetEvents = excludeEventNameFromVisitMethod.get(eventStreamShape.getC2jName());
150-
151-
if (targetEvents == null) {
152-
return false;
153-
}
154-
155-
return targetEvents.stream().anyMatch(e -> e.equals(event.getC2jName()));
156-
}
157139
}

0 commit comments

Comments
 (0)