001/*
002 * Copyright 2012-2015 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.boot.cli.command.init;
018
019import java.io.IOException;
020import java.util.ArrayList;
021import java.util.Arrays;
022import java.util.Collection;
023import java.util.List;
024
025import joptsimple.OptionSet;
026import joptsimple.OptionSpec;
027
028import org.springframework.boot.cli.command.Command;
029import org.springframework.boot.cli.command.HelpExample;
030import org.springframework.boot.cli.command.OptionParsingCommand;
031import org.springframework.boot.cli.command.options.OptionHandler;
032import org.springframework.boot.cli.command.status.ExitStatus;
033import org.springframework.boot.cli.util.Log;
034import org.springframework.util.Assert;
035
036/**
037 * {@link Command} that initializes a project using Spring initializr.
038 *
039 * @author Stephane Nicoll
040 * @author EddĂș MelĂ©ndez
041 * @since 1.2.0
042 */
043public class InitCommand extends OptionParsingCommand {
044
045        public InitCommand() {
046                this(new InitOptionHandler(new InitializrService()));
047        }
048
049        public InitCommand(InitOptionHandler handler) {
050                super("init",
051                                "Initialize a new project using Spring " + "Initializr (start.spring.io)",
052                                handler);
053        }
054
055        @Override
056        public String getUsageHelp() {
057                return "[options] [location]";
058        }
059
060        @Override
061        public Collection<HelpExample> getExamples() {
062                List<HelpExample> examples = new ArrayList<HelpExample>();
063                examples.add(new HelpExample("To list all the capabilities of the service",
064                                "spring init --list"));
065                examples.add(new HelpExample("To creates a default project", "spring init"));
066                examples.add(new HelpExample("To create a web my-app.zip",
067                                "spring init -d=web my-app.zip"));
068                examples.add(new HelpExample("To create a web/data-jpa gradle project unpacked",
069                                "spring init -d=web,jpa --build=gradle my-dir"));
070                return examples;
071        }
072
073        /**
074         * {@link OptionHandler} for {@link InitCommand}.
075         */
076        static class InitOptionHandler extends OptionHandler {
077
078                private final ServiceCapabilitiesReportGenerator serviceCapabilitiesReport;
079
080                private final ProjectGenerator projectGenerator;
081
082                private OptionSpec<String> target;
083
084                private OptionSpec<Void> listCapabilities;
085
086                private OptionSpec<String> groupId;
087
088                private OptionSpec<String> artifactId;
089
090                private OptionSpec<String> version;
091
092                private OptionSpec<String> name;
093
094                private OptionSpec<String> description;
095
096                private OptionSpec<String> packageName;
097
098                private OptionSpec<String> type;
099
100                private OptionSpec<String> packaging;
101
102                private OptionSpec<String> build;
103
104                private OptionSpec<String> format;
105
106                private OptionSpec<String> javaVersion;
107
108                private OptionSpec<String> language;
109
110                private OptionSpec<String> bootVersion;
111
112                private OptionSpec<String> dependencies;
113
114                private OptionSpec<Void> extract;
115
116                private OptionSpec<Void> force;
117
118                InitOptionHandler(InitializrService initializrService) {
119                        this.serviceCapabilitiesReport = new ServiceCapabilitiesReportGenerator(
120                                        initializrService);
121                        this.projectGenerator = new ProjectGenerator(initializrService);
122
123                }
124
125                @Override
126                protected void options() {
127                        this.target = option(Arrays.asList("target"), "URL of the service to use")
128                                        .withRequiredArg()
129                                        .defaultsTo(ProjectGenerationRequest.DEFAULT_SERVICE_URL);
130                        this.listCapabilities = option(Arrays.asList("list", "l"),
131                                        "List the capabilities of the service. Use it to discover the "
132                                                        + "dependencies and the types that are available");
133                        projectGenerationOptions();
134                        otherOptions();
135                }
136
137                private void projectGenerationOptions() {
138                        this.groupId = option(Arrays.asList("groupId", "g"),
139                                        "Project coordinates (for example 'org.test')").withRequiredArg();
140                        this.artifactId = option(Arrays.asList("artifactId", "a"),
141                                        "Project coordinates; infer archive name (for example 'test')")
142                                                        .withRequiredArg();
143                        this.version = option(Arrays.asList("version", "v"),
144                                        "Project version (for example '0.0.1-SNAPSHOT')").withRequiredArg();
145                        this.name = option(Arrays.asList("name", "n"),
146                                        "Project name; infer application name").withRequiredArg();
147                        this.description = option("description", "Project description")
148                                        .withRequiredArg();
149                        this.packageName = option("package-name", "Package name").withRequiredArg();
150                        this.type = option(Arrays.asList("type", "t"),
151                                        "Project type. Not normally needed if you use --build "
152                                                        + "and/or --format. Check the capabilities of the service "
153                                                        + "(--list) for more details").withRequiredArg();
154                        this.packaging = option(Arrays.asList("packaging", "p"),
155                                        "Project packaging (for example 'jar')").withRequiredArg();
156                        this.build = option("build",
157                                        "Build system to use (for example 'maven' or 'gradle')")
158                                                        .withRequiredArg().defaultsTo("maven");
159                        this.format = option("format",
160                                        "Format of the generated content (for example 'build' for a build file, "
161                                                        + "'project' for a project archive)").withRequiredArg()
162                                                                        .defaultsTo("project");
163                        this.javaVersion = option(Arrays.asList("java-version", "j"),
164                                        "Language level (for example '1.8')").withRequiredArg();
165                        this.language = option(Arrays.asList("language", "l"),
166                                        "Programming language  (for example 'java')").withRequiredArg();
167                        this.bootVersion = option(Arrays.asList("boot-version", "b"),
168                                        "Spring Boot version (for example '1.2.0.RELEASE')")
169                                                        .withRequiredArg();
170                        this.dependencies = option(Arrays.asList("dependencies", "d"),
171                                        "Comma-separated list of dependency identifiers to include in the "
172                                                        + "generated project").withRequiredArg();
173                }
174
175                private void otherOptions() {
176                        this.extract = option(Arrays.asList("extract", "x"),
177                                        "Extract the project archive. Inferred if a location is specified without an extension");
178                        this.force = option(Arrays.asList("force", "f"),
179                                        "Force overwrite of existing files");
180                }
181
182                @Override
183                protected ExitStatus run(OptionSet options) throws Exception {
184                        try {
185                                if (options.has(this.listCapabilities)) {
186                                        generateReport(options);
187                                }
188                                else {
189                                        generateProject(options);
190                                }
191                                return ExitStatus.OK;
192                        }
193                        catch (ReportableException ex) {
194                                Log.error(ex.getMessage());
195                                return ExitStatus.ERROR;
196                        }
197                        catch (Exception ex) {
198                                Log.error(ex);
199                                return ExitStatus.ERROR;
200                        }
201                }
202
203                private void generateReport(OptionSet options) throws IOException {
204                        Log.info(this.serviceCapabilitiesReport
205                                        .generate(options.valueOf(this.target)));
206                }
207
208                protected void generateProject(OptionSet options) throws IOException {
209                        ProjectGenerationRequest request = createProjectGenerationRequest(options);
210                        this.projectGenerator.generateProject(request, options.has(this.force));
211                }
212
213                protected ProjectGenerationRequest createProjectGenerationRequest(
214                                OptionSet options) {
215
216                        List<?> nonOptionArguments = new ArrayList<Object>(
217                                        options.nonOptionArguments());
218                        Assert.isTrue(nonOptionArguments.size() <= 1,
219                                        "Only the target location may be specified");
220
221                        ProjectGenerationRequest request = new ProjectGenerationRequest();
222                        request.setServiceUrl(options.valueOf(this.target));
223                        if (options.has(this.bootVersion)) {
224                                request.setBootVersion(options.valueOf(this.bootVersion));
225                        }
226                        if (options.has(this.dependencies)) {
227                                for (String dep : options.valueOf(this.dependencies).split(",")) {
228                                        request.getDependencies().add(dep.trim());
229                                }
230                        }
231                        if (options.has(this.javaVersion)) {
232                                request.setJavaVersion(options.valueOf(this.javaVersion));
233                        }
234                        if (options.has(this.packageName)) {
235                                request.setPackageName(options.valueOf(this.packageName));
236                        }
237                        request.setBuild(options.valueOf(this.build));
238                        request.setFormat(options.valueOf(this.format));
239                        request.setDetectType(options.has(this.build) || options.has(this.format));
240                        if (options.has(this.type)) {
241                                request.setType(options.valueOf(this.type));
242                        }
243                        if (options.has(this.packaging)) {
244                                request.setPackaging(options.valueOf(this.packaging));
245                        }
246                        if (options.has(this.language)) {
247                                request.setLanguage(options.valueOf(this.language));
248                        }
249                        if (options.has(this.groupId)) {
250                                request.setGroupId(options.valueOf(this.groupId));
251                        }
252                        if (options.has(this.artifactId)) {
253                                request.setArtifactId(options.valueOf(this.artifactId));
254                        }
255                        if (options.has(this.name)) {
256                                request.setName(options.valueOf(this.name));
257                        }
258                        if (options.has(this.version)) {
259                                request.setVersion(options.valueOf(this.version));
260                        }
261                        if (options.has(this.description)) {
262                                request.setDescription(options.valueOf(this.description));
263                        }
264                        request.setExtract(options.has(this.extract));
265                        if (nonOptionArguments.size() == 1) {
266                                String output = (String) nonOptionArguments.get(0);
267                                request.setOutput(output);
268                        }
269                        return request;
270                }
271
272        }
273
274}