• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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