/*
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.cli.command.init;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import joptsimple.OptionSet;
import joptsimple.OptionSpec;
import org.springframework.boot.cli.command.Command;
import org.springframework.boot.cli.command.HelpExample;
import org.springframework.boot.cli.command.OptionParsingCommand;
import org.springframework.boot.cli.command.options.OptionHandler;
import org.springframework.boot.cli.command.status.ExitStatus;
import org.springframework.boot.cli.util.Log;
import org.springframework.util.Assert;
/**
* {@link Command} that initializes a project using Spring initializr.
*
* @author Stephane Nicoll
* @author EddĂș MelĂ©ndez
* @since 1.2.0
*/
public class InitCommand extends OptionParsingCommand {
public InitCommand() {
this(new InitOptionHandler(new InitializrService()));
}
public InitCommand(InitOptionHandler handler) {
super("init",
"Initialize a new project using Spring " + "Initializr (start.spring.io)",
handler);
}
@Override
public String getUsageHelp() {
return "[options] [location]";
}
@Override
public Collection<HelpExample> getExamples() {
List<HelpExample> examples = new ArrayList<>();
examples.add(new HelpExample("To list all the capabilities of the service",
"spring init --list"));
examples.add(new HelpExample("To creates a default project", "spring init"));
examples.add(new HelpExample("To create a web my-app.zip",
"spring init -d=web my-app.zip"));
examples.add(new HelpExample("To create a web/data-jpa gradle project unpacked",
"spring init -d=web,jpa --build=gradle my-dir"));
return examples;
}
/**
* {@link OptionHandler} for {@link InitCommand}.
*/
static class InitOptionHandler extends OptionHandler {
private final ServiceCapabilitiesReportGenerator serviceCapabilitiesReport;
private final ProjectGenerator projectGenerator;
private OptionSpec<String> target;
private OptionSpec<Void> listCapabilities;
private OptionSpec<String> groupId;
private OptionSpec<String> artifactId;
private OptionSpec<String> version;
private OptionSpec<String> name;
private OptionSpec<String> description;
private OptionSpec<String> packageName;
private OptionSpec<String> type;
private OptionSpec<String> packaging;
private OptionSpec<String> build;
private OptionSpec<String> format;
private OptionSpec<String> javaVersion;
private OptionSpec<String> language;
private OptionSpec<String> bootVersion;
private OptionSpec<String> dependencies;
private OptionSpec<Void> extract;
private OptionSpec<Void> force;
InitOptionHandler(InitializrService initializrService) {
this.serviceCapabilitiesReport = new ServiceCapabilitiesReportGenerator(
initializrService);
this.projectGenerator = new ProjectGenerator(initializrService);
}
@Override
protected void options() {
this.target = option(Arrays.asList("target"), "URL of the service to use")
.withRequiredArg()
.defaultsTo(ProjectGenerationRequest.DEFAULT_SERVICE_URL);
this.listCapabilities = option(Arrays.asList("list", "l"),
"List the capabilities of the service. Use it to discover the "
+ "dependencies and the types that are available");
projectGenerationOptions();
otherOptions();
}
private void projectGenerationOptions() {
this.groupId = option(Arrays.asList("groupId", "g"),
"Project coordinates (for example 'org.test')").withRequiredArg();
this.artifactId = option(Arrays.asList("artifactId", "a"),
"Project coordinates; infer archive name (for example 'test')")
.withRequiredArg();
this.version = option(Arrays.asList("version", "v"),
"Project version (for example '0.0.1-SNAPSHOT')").withRequiredArg();
this.name = option(Arrays.asList("name", "n"),
"Project name; infer application name").withRequiredArg();
this.description = option("description", "Project description")
.withRequiredArg();
this.packageName = option("package-name", "Package name").withRequiredArg();
this.type = option(Arrays.asList("type", "t"),
"Project type. Not normally needed if you use --build "
+ "and/or --format. Check the capabilities of the service "
+ "(--list) for more details").withRequiredArg();
this.packaging = option(Arrays.asList("packaging", "p"),
"Project packaging (for example 'jar')").withRequiredArg();
this.build = option("build",
"Build system to use (for example 'maven' or 'gradle')")
.withRequiredArg().defaultsTo("maven");
this.format = option("format",
"Format of the generated content (for example 'build' for a build file, "
+ "'project' for a project archive)").withRequiredArg()
.defaultsTo("project");
this.javaVersion = option(Arrays.asList("java-version", "j"),
"Language level (for example '1.8')").withRequiredArg();
this.language = option(Arrays.asList("language", "l"),
"Programming language (for example 'java')").withRequiredArg();
this.bootVersion = option(Arrays.asList("boot-version", "b"),
"Spring Boot version (for example '1.2.0.RELEASE')")
.withRequiredArg();
this.dependencies = option(Arrays.asList("dependencies", "d"),
"Comma-separated list of dependency identifiers to include in the "
+ "generated project").withRequiredArg();
}
private void otherOptions() {
this.extract = option(Arrays.asList("extract", "x"),
"Extract the project archive. Inferred if a location is specified without an extension");
this.force = option(Arrays.asList("force", "f"),
"Force overwrite of existing files");
}
@Override
protected ExitStatus run(OptionSet options) throws Exception {
try {
if (options.has(this.listCapabilities)) {
generateReport(options);
}
else {
generateProject(options);
}
return ExitStatus.OK;
}
catch (ReportableException ex) {
Log.error(ex.getMessage());
return ExitStatus.ERROR;
}
catch (Exception ex) {
Log.error(ex);
return ExitStatus.ERROR;
}
}
private void generateReport(OptionSet options) throws IOException {
Log.info(this.serviceCapabilitiesReport
.generate(options.valueOf(this.target)));
}
protected void generateProject(OptionSet options) throws IOException {
ProjectGenerationRequest request = createProjectGenerationRequest(options);
this.projectGenerator.generateProject(request, options.has(this.force));
}
protected ProjectGenerationRequest createProjectGenerationRequest(
OptionSet options) {
List<?> nonOptionArguments = new ArrayList<Object>(
options.nonOptionArguments());
Assert.isTrue(nonOptionArguments.size() <= 1,
"Only the target location may be specified");
ProjectGenerationRequest request = new ProjectGenerationRequest();
request.setServiceUrl(options.valueOf(this.target));
if (options.has(this.bootVersion)) {
request.setBootVersion(options.valueOf(this.bootVersion));
}
if (options.has(this.dependencies)) {
for (String dep : options.valueOf(this.dependencies).split(",")) {
request.getDependencies().add(dep.trim());
}
}
if (options.has(this.javaVersion)) {
request.setJavaVersion(options.valueOf(this.javaVersion));
}
if (options.has(this.packageName)) {
request.setPackageName(options.valueOf(this.packageName));
}
request.setBuild(options.valueOf(this.build));
request.setFormat(options.valueOf(this.format));
request.setDetectType(options.has(this.build) || options.has(this.format));
if (options.has(this.type)) {
request.setType(options.valueOf(this.type));
}
if (options.has(this.packaging)) {
request.setPackaging(options.valueOf(this.packaging));
}
if (options.has(this.language)) {
request.setLanguage(options.valueOf(this.language));
}
if (options.has(this.groupId)) {
request.setGroupId(options.valueOf(this.groupId));
}
if (options.has(this.artifactId)) {
request.setArtifactId(options.valueOf(this.artifactId));
}
if (options.has(this.name)) {
request.setName(options.valueOf(this.name));
}
if (options.has(this.version)) {
request.setVersion(options.valueOf(this.version));
}
if (options.has(this.description)) {
request.setDescription(options.valueOf(this.description));
}
request.setExtract(options.has(this.extract));
if (nonOptionArguments.size() == 1) {
String output = (String) nonOptionArguments.get(0);
request.setOutput(output);
}
return request;
}
}
}