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}