diff --git a/.changes/next-release/feature-CodeGeneratorMavenPlugin-64c91e8.json b/.changes/next-release/feature-CodeGeneratorMavenPlugin-64c91e8.json
new file mode 100644
index 000000000000..91090c74555f
--- /dev/null
+++ b/.changes/next-release/feature-CodeGeneratorMavenPlugin-64c91e8.json
@@ -0,0 +1,6 @@
+{
+ "type": "feature",
+ "category": "Code Generator Maven Plugin",
+ "contributor": "",
+ "description": "Update the generator plugin to support model validation during code generation. In addition, this adds the `writeValidationReport` flag to support writing the validation report to disk."
+}
diff --git a/codegen-maven-plugin/pom.xml b/codegen-maven-plugin/pom.xml
index af92ffb4edb8..0533043d5e7f 100644
--- a/codegen-maven-plugin/pom.xml
+++ b/codegen-maven-plugin/pom.xml
@@ -57,6 +57,11 @@
software.amazon.awssdk
${awsjavasdk.version}
+
+ utils
+ software.amazon.awssdk
+ ${awsjavasdk.version}
+
org.junit.jupiter
junit-jupiter
diff --git a/codegen-maven-plugin/src/main/java/software/amazon/awssdk/codegen/maven/plugin/GenerationMojo.java b/codegen-maven-plugin/src/main/java/software/amazon/awssdk/codegen/maven/plugin/GenerationMojo.java
index 4ce4e7be116b..8b088846b055 100644
--- a/codegen-maven-plugin/src/main/java/software/amazon/awssdk/codegen/maven/plugin/GenerationMojo.java
+++ b/codegen-maven-plugin/src/main/java/software/amazon/awssdk/codegen/maven/plugin/GenerationMojo.java
@@ -21,7 +21,11 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import java.util.Optional;
+import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
@@ -30,21 +34,23 @@
import org.apache.maven.project.MavenProject;
import software.amazon.awssdk.codegen.C2jModels;
import software.amazon.awssdk.codegen.CodeGenerator;
+import software.amazon.awssdk.codegen.IntermediateModelBuilder;
import software.amazon.awssdk.codegen.internal.Utils;
import software.amazon.awssdk.codegen.model.config.customization.CustomizationConfig;
+import software.amazon.awssdk.codegen.model.intermediate.IntermediateModel;
import software.amazon.awssdk.codegen.model.rules.endpoints.EndpointTestSuiteModel;
import software.amazon.awssdk.codegen.model.service.EndpointRuleSetModel;
import software.amazon.awssdk.codegen.model.service.Paginators;
import software.amazon.awssdk.codegen.model.service.ServiceModel;
import software.amazon.awssdk.codegen.model.service.Waiters;
import software.amazon.awssdk.codegen.utils.ModelLoaderUtils;
+import software.amazon.awssdk.utils.StringUtils;
/**
* The Maven mojo to generate Java client code using software.amazon.awssdk:codegen module.
*/
@Mojo(name = "generate")
public class GenerationMojo extends AbstractMojo {
-
private static final String MODEL_FILE = "service-2.json";
private static final String CUSTOMIZATION_CONFIG_FILE = "customization.config";
private static final String WAITERS_FILE = "waiters-2.json";
@@ -62,6 +68,8 @@ public class GenerationMojo extends AbstractMojo {
@Parameter(property = "writeIntermediateModel", defaultValue = "false")
private boolean writeIntermediateModel;
+ @Parameter(property = "writeValidationReport", defaultValue = "false")
+ private boolean writeValidationReport;
@Parameter(defaultValue = "${project}", readonly = true)
private MavenProject project;
@@ -76,22 +84,59 @@ public void execute() throws MojoExecutionException {
this.resourcesDirectory = Paths.get(outputDirectory).resolve("generated-resources").resolve("sdk-resources");
this.testsDirectory = Paths.get(outputDirectory).resolve("generated-test-sources").resolve("sdk-tests");
- findModelRoots().forEach(p -> {
- Path modelRootPath = p.modelRoot;
- getLog().info("Loading from: " + modelRootPath.toString());
- generateCode(C2jModels.builder()
- .customizationConfig(p.customizationConfig)
- .serviceModel(loadServiceModel(modelRootPath))
- .waitersModel(loadWaiterModel(modelRootPath))
- .paginatorsModel(loadPaginatorModel(modelRootPath))
- .endpointRuleSetModel(loadEndpointRuleSetModel(modelRootPath))
- .endpointTestSuiteModel(loadEndpointTestSuiteModel(modelRootPath))
- .build());
+ List generationParams = initGenerationParams();
+
+ Map serviceNameToModelMap = new HashMap<>();
+
+ generationParams.forEach(
+ params -> {
+ IntermediateModel model = params.intermediateModel;
+ String lowercaseServiceName = StringUtils.lowerCase(model.getMetadata().getServiceName());
+ IntermediateModel previous = serviceNameToModelMap.put(lowercaseServiceName, model);
+ if (previous != null) {
+ String warning = String.format("Multiple service models found with service name %s. Model validation "
+ + "will likely be incorrect", lowercaseServiceName);
+ getLog().warn(warning);
+ }
+ });
+
+ // Update each param with the intermediate model it shares models with, if any
+ generationParams.forEach(params -> {
+ CustomizationConfig customizationConfig = params.intermediateModel.getCustomizationConfig();
+
+ if (customizationConfig.getShareModelConfig() != null) {
+ String shareModelWithName = customizationConfig.getShareModelConfig().getShareModelWith();
+ params.withShareModelsTarget(serviceNameToModelMap.get(shareModelWithName));
+ }
});
+
+ generationParams.forEach(this::generateCode);
+
project.addCompileSourceRoot(sourcesDirectory.toFile().getAbsolutePath());
project.addTestCompileSourceRoot(testsDirectory.toFile().getAbsolutePath());
}
+ private List initGenerationParams() throws MojoExecutionException {
+ List modelRoots = findModelRoots().collect(Collectors.toList());
+
+ return modelRoots.stream().map(r -> {
+ Path modelRootPath = r.modelRoot;
+ getLog().info("Loading from: " + modelRootPath.toString());
+ C2jModels c2jModels = C2jModels.builder()
+ .customizationConfig(r.customizationConfig)
+ .serviceModel(loadServiceModel(modelRootPath))
+ .waitersModel(loadWaiterModel(modelRootPath))
+ .paginatorsModel(loadPaginatorModel(modelRootPath))
+ .endpointRuleSetModel(loadEndpointRuleSetModel(modelRootPath))
+ .endpointTestSuiteModel(loadEndpointTestSuiteModel(modelRootPath))
+ .build();
+ String intermediateModelFileNamePrefix = intermediateModelFileNamePrefix(c2jModels);
+ IntermediateModel intermediateModel = new IntermediateModelBuilder(c2jModels).build();
+ return new GenerationParams().withIntermediateModel(intermediateModel)
+ .withIntermediateModelFileNamePrefix(intermediateModelFileNamePrefix);
+ }).collect(Collectors.toList());
+ }
+
private Stream findModelRoots() throws MojoExecutionException {
try {
return Files.find(codeGenResources.toPath(), 10, this::isModelFile)
@@ -111,13 +156,15 @@ private boolean isModelFile(Path p, BasicFileAttributes a) {
return p.toString().endsWith(MODEL_FILE);
}
- private void generateCode(C2jModels models) {
+ private void generateCode(GenerationParams params) {
CodeGenerator.builder()
- .models(models)
+ .intermediateModel(params.intermediateModel)
+ .shareModelsTarget(params.shareModelsTarget)
.sourcesDirectory(sourcesDirectory.toFile().getAbsolutePath())
.resourcesDirectory(resourcesDirectory.toFile().getAbsolutePath())
.testsDirectory(testsDirectory.toFile().getAbsolutePath())
- .intermediateModelFileNamePrefix(intermediateModelFileNamePrefix(models))
+ .intermediateModelFileNamePrefix(params.intermediateModelFileNamePrefix)
+ .emitValidationReport(writeValidationReport)
.build()
.execute();
}
@@ -178,4 +225,25 @@ private ModelRoot(Path modelRoot, CustomizationConfig customizationConfig) {
this.customizationConfig = customizationConfig;
}
}
+
+ private static class GenerationParams {
+ private IntermediateModel intermediateModel;
+ private IntermediateModel shareModelsTarget;
+ private String intermediateModelFileNamePrefix;
+
+ public GenerationParams withIntermediateModel(IntermediateModel intermediateModel) {
+ this.intermediateModel = intermediateModel;
+ return this;
+ }
+
+ public GenerationParams withShareModelsTarget(IntermediateModel shareModelsTarget) {
+ this.shareModelsTarget = shareModelsTarget;
+ return this;
+ }
+
+ public GenerationParams withIntermediateModelFileNamePrefix(String intermediateModelFileNamePrefix) {
+ this.intermediateModelFileNamePrefix = intermediateModelFileNamePrefix;
+ return this;
+ }
+ }
}