1 /* 2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"). 5 * You may not use this file except in compliance with the License. 6 * A copy of the License is located at 7 * 8 * http://aws.amazon.com/apache2.0 9 * 10 * or in the "license" file accompanying this file. This file is distributed 11 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 * express or implied. See the License for the specific language governing 13 * permissions and limitations under the License. 14 */ 15 16 package software.amazon.awssdk.release; 17 18 import static java.nio.charset.StandardCharsets.UTF_8; 19 import static software.amazon.awssdk.release.CreateNewServiceModuleMain.computeInternalDependencies; 20 import static software.amazon.awssdk.release.CreateNewServiceModuleMain.toList; 21 22 import java.io.IOException; 23 import java.nio.file.FileVisitResult; 24 import java.nio.file.Files; 25 import java.nio.file.Path; 26 import java.nio.file.Paths; 27 import java.nio.file.SimpleFileVisitor; 28 import java.nio.file.attribute.BasicFileAttributes; 29 import java.util.Collections; 30 import java.util.LinkedHashSet; 31 import java.util.Set; 32 import java.util.stream.Collectors; 33 import java.util.stream.Stream; 34 import org.apache.commons.cli.CommandLine; 35 import org.apache.commons.io.FileUtils; 36 import org.apache.commons.lang3.StringUtils; 37 import org.w3c.dom.Document; 38 import org.w3c.dom.Node; 39 import software.amazon.awssdk.utils.Validate; 40 import software.amazon.awssdk.utils.internal.CodegenNamingUtils; 41 42 /** 43 * A command line application to create a new, empty service. 44 * 45 * Example usage: 46 * <pre> 47 * mvn exec:java -pl :release-scripts \ 48 * -Dexec.mainClass="software.amazon.awssdk.release.NewServiceMain" \ 49 * -Dexec.args="--maven-project-root /path/to/root 50 * --maven-project-version 2.1.4-SNAPSHOT 51 * --service-id 'Service Id' 52 * --service-module-name service-module-name 53 * --service-protocol json" 54 * </pre> 55 */ 56 public class NewServiceMain extends Cli { 57 58 private static final Set<String> DEFAULT_INTERNAL_DEPENDENCIES = defaultInternalDependencies(); 59 NewServiceMain()60 private NewServiceMain() { 61 super(requiredOption("service-module-name", "The name of the service module to be created."), 62 requiredOption("service-id", "The service ID of the service module to be created."), 63 requiredOption("service-protocol", "The protocol of the service module to be created."), 64 requiredOption("maven-project-root", "The root directory for the maven project."), 65 requiredOption("maven-project-version", "The maven version of the service module to be created."), 66 optionalMultiValueOption("include-internal-dependency", "Includes an internal dependency from new service pom."), 67 optionalMultiValueOption("exclude-internal-dependency", "Excludes an internal dependency from new service pom.")); 68 } 69 main(String[] args)70 public static void main(String[] args) { 71 new NewServiceMain().run(args); 72 } 73 defaultInternalDependencies()74 private static Set<String> defaultInternalDependencies() { 75 Set<String> defaultInternalDependencies = new LinkedHashSet<>(); 76 defaultInternalDependencies.add("http-auth-aws"); 77 return Collections.unmodifiableSet(defaultInternalDependencies); 78 } 79 80 @Override run(CommandLine commandLine)81 protected void run(CommandLine commandLine) throws Exception { 82 new NewServiceCreator(commandLine).run(); 83 } 84 85 private static class NewServiceCreator { 86 private final Path mavenProjectRoot; 87 private final String mavenProjectVersion; 88 private final String serviceModuleName; 89 private final String serviceId; 90 private final String serviceProtocol; 91 private final Set<String> internalDependencies; 92 NewServiceCreator(CommandLine commandLine)93 private NewServiceCreator(CommandLine commandLine) { 94 this.mavenProjectRoot = Paths.get(commandLine.getOptionValue("maven-project-root").trim()); 95 this.mavenProjectVersion = commandLine.getOptionValue("maven-project-version").trim(); 96 this.serviceModuleName = commandLine.getOptionValue("service-module-name").trim(); 97 this.serviceId = commandLine.getOptionValue("service-id").trim(); 98 this.serviceProtocol = transformSpecialProtocols(commandLine.getOptionValue("service-protocol").trim()); 99 this.internalDependencies = computeInternalDependencies(toList(commandLine 100 .getOptionValues("include-internal-dependency")), 101 toList(commandLine 102 .getOptionValues("exclude-internal-dependency"))); 103 Validate.isTrue(Files.exists(mavenProjectRoot), "Project root does not exist: " + mavenProjectRoot); 104 } 105 transformSpecialProtocols(String protocol)106 private String transformSpecialProtocols(String protocol) { 107 switch (protocol) { 108 case "ec2": return "query"; 109 case "rest-xml": return "xml"; 110 case "rest-json": return "json"; 111 default: return protocol; 112 } 113 } 114 run()115 public void run() throws Exception { 116 Path servicesRoot = mavenProjectRoot.resolve("services"); 117 Path templateModulePath = servicesRoot.resolve("new-service-template"); 118 Path newServiceModulePath = servicesRoot.resolve(serviceModuleName); 119 120 createNewModuleFromTemplate(templateModulePath, newServiceModulePath); 121 replaceTemplatePlaceholders(newServiceModulePath); 122 123 124 Path servicesPomPath = mavenProjectRoot.resolve("services").resolve("pom.xml"); 125 Path aggregatePomPath = mavenProjectRoot.resolve("aws-sdk-java").resolve("pom.xml"); 126 Path bomPomPath = mavenProjectRoot.resolve("bom").resolve("pom.xml"); 127 128 new AddSubmoduleTransformer().transform(servicesPomPath); 129 new AddDependencyTransformer().transform(aggregatePomPath); 130 new AddDependencyManagementDependencyTransformer().transform(bomPomPath); 131 132 Path newServicePom = newServiceModulePath.resolve("pom.xml"); 133 new CreateNewServiceModuleMain.AddInternalDependenciesTransformer(internalDependencies).transform(newServicePom); 134 } 135 createNewModuleFromTemplate(Path templateModulePath, Path newServiceModule)136 private void createNewModuleFromTemplate(Path templateModulePath, Path newServiceModule) throws IOException { 137 FileUtils.copyDirectory(templateModulePath.toFile(), newServiceModule.toFile()); 138 } 139 replaceTemplatePlaceholders(Path newServiceModule)140 private void replaceTemplatePlaceholders(Path newServiceModule) throws IOException { 141 Files.walkFileTree(newServiceModule, new SimpleFileVisitor<Path>() { 142 @Override 143 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { 144 replacePlaceholdersInFile(file); 145 return FileVisitResult.CONTINUE; 146 } 147 }); 148 } 149 replacePlaceholdersInFile(Path file)150 private void replacePlaceholdersInFile(Path file) throws IOException { 151 String fileContents = new String(Files.readAllBytes(file), UTF_8); 152 String newFileContents = replacePlaceholders(fileContents); 153 Files.write(file, newFileContents.getBytes(UTF_8)); 154 } 155 replacePlaceholders(String line)156 private String replacePlaceholders(String line) { 157 String[] searchList = { 158 "{{MVN_ARTIFACT_ID}}", 159 "{{MVN_NAME}}", 160 "{{MVN_VERSION}}", 161 "{{PROTOCOL}}" 162 }; 163 String[] replaceList = { 164 serviceModuleName, 165 mavenName(serviceId), 166 mavenProjectVersion, 167 serviceProtocol 168 }; 169 return StringUtils.replaceEach(line, searchList, replaceList); 170 } 171 mavenName(String serviceId)172 private String mavenName(String serviceId) { 173 return Stream.of(CodegenNamingUtils.splitOnWordBoundaries(serviceId)) 174 .map(StringUtils::capitalize) 175 .collect(Collectors.joining(" ")); 176 } 177 178 private class AddSubmoduleTransformer extends PomTransformer { 179 @Override updateDocument(Document doc)180 protected void updateDocument(Document doc) { 181 Node project = findChild(doc, "project"); 182 Node modules = findChild(project, "modules"); 183 184 modules.appendChild(textElement(doc, "module", serviceModuleName)); 185 } 186 } 187 188 private class AddDependencyTransformer extends PomTransformer { 189 @Override updateDocument(Document doc)190 protected void updateDocument(Document doc) { 191 Node project = findChild(doc, "project"); 192 Node dependencies = findChild(project, "dependencies"); 193 194 dependencies.appendChild(sdkDependencyElement(doc, serviceModuleName)); 195 } 196 } 197 198 private class AddDependencyManagementDependencyTransformer extends PomTransformer { 199 @Override updateDocument(Document doc)200 protected void updateDocument(Document doc) { 201 Node project = findChild(doc, "project"); 202 Node dependencyManagement = findChild(project, "dependencyManagement"); 203 Node dependencies = findChild(dependencyManagement, "dependencies"); 204 205 dependencies.appendChild(sdkDependencyElement(doc, serviceModuleName)); 206 } 207 } 208 } 209 } 210