Commit 830ce808 authored by Phillip Webb's avatar Phillip Webb

Polish CLI init command

Rename a few classes and methods and extract some logic into helper
classes. Also change 2 char shortcuts to a single char.

Closes gh-1751
parent b89e5e0a
...@@ -22,36 +22,30 @@ package org.springframework.boot.cli.command.init; ...@@ -22,36 +22,30 @@ package org.springframework.boot.cli.command.init;
* @author Stephane Nicoll * @author Stephane Nicoll
* @since 1.2.0 * @since 1.2.0
*/ */
class Dependency { final class Dependency {
private String id; private final String id;
private String name; private final String name;
private String description; private final String description;
public String getId() { public Dependency(String id, String name, String description) {
return this.id; this.id = id;
this.name = name;
this.description = description;
} }
public void setId(String id) { public String getId() {
this.id = id; return this.id;
} }
public String getName() { public String getName() {
return this.name; return this.name;
} }
public void setName(String name) {
this.name = name;
}
public String getDescription() { public String getDescription() {
return this.description; return this.description;
} }
public void setDescription(String description) {
this.description = description;
}
} }
...@@ -16,9 +16,18 @@ ...@@ -16,9 +16,18 @@
package org.springframework.boot.cli.command.init; package org.springframework.boot.cli.command.init;
import java.io.IOException;
import java.util.Arrays;
import joptsimple.OptionSet;
import joptsimple.OptionSpec;
import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.client.HttpClientBuilder;
import org.springframework.boot.cli.command.Command; import org.springframework.boot.cli.command.Command;
import org.springframework.boot.cli.command.OptionParsingCommand; 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;
/** /**
* {@link Command} that initializes a project using Spring initializr. * {@link Command} that initializes a project using Spring initializr.
...@@ -28,13 +37,168 @@ import org.springframework.boot.cli.command.OptionParsingCommand; ...@@ -28,13 +37,168 @@ import org.springframework.boot.cli.command.OptionParsingCommand;
*/ */
public class InitCommand extends OptionParsingCommand { public class InitCommand extends OptionParsingCommand {
InitCommand(InitCommandOptionHandler handler) { public InitCommand() {
super("init", "Initialize a new project structure from Spring Initializr", this(new InitOptionHandler(getInitializrService()));
handler);
} }
public InitCommand() { public InitCommand(InitOptionHandler handler) {
this(new InitCommandOptionHandler(HttpClientBuilder.create().build())); super("init", "Initialize a new project using Spring "
+ "Initialzr (start.spring.io)", handler);
}
private static InitializrService getInitializrService() {
return new InitializrService(HttpClientBuilder.create().build());
}
static class InitOptionHandler extends OptionHandler {
private final ServiceCapabilitiesReportGenerator serviceCapabilitiesReport;
private final ProjectGenerator projectGenerator;
private OptionSpec<String> target;
private OptionSpec<Void> listCapabilities;
private OptionSpec<String> bootVersion;
private OptionSpec<String> dependencies;
private OptionSpec<String> javaVersion;
private OptionSpec<String> packaging;
private OptionSpec<String> build;
private OptionSpec<String> format;
private OptionSpec<String> type;
private OptionSpec<Void> extract;
private OptionSpec<Void> force;
private OptionSpec<String> output;
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.bootVersion = option(Arrays.asList("boot-version", "b"),
"Spring Boot version to use (for example '1.2.0.RELEASE')")
.withRequiredArg();
this.dependencies = option(
Arrays.asList("dependencies", "d"),
"Comma separated list of dependencies to include in the "
+ "generated project").withRequiredArg();
this.javaVersion = option(Arrays.asList("java-version", "j"),
"Java version to use (for example '1.8')").withRequiredArg();
this.packaging = option(Arrays.asList("packaging", "p"),
"Packaging type to use (for example 'jar')").withRequiredArg();
this.build = option("build",
"The build system to use (for example 'maven' or 'gradle')")
.withRequiredArg().defaultsTo("maven");
this.format = option(
"format",
"The format of the generated content (for example 'build' for a build file, "
+ "'project' for a project archive)").withRequiredArg()
.defaultsTo("project");
this.type = option(
Arrays.asList("type", "t"),
"The project type to use. Not normally needed if you use --build "
+ "and/or --format. Check the capabilities of the service "
+ "(--list) for more details").withRequiredArg();
}
private void otherOptions() {
this.extract = option(Arrays.asList("extract", "x"),
"Extract the project archive");
this.force = option(Arrays.asList("force", "f"),
"Force overwrite of existing files");
this.output = option(
Arrays.asList("output", "o"),
"Location of the generated project. Can be an absolute or a "
+ "relative reference and should refer to a directory when "
+ "--extract is used").withRequiredArg();
}
@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),
options.has(this.extract), options.valueOf(this.output));
}
protected ProjectGenerationRequest createProjectGenerationRequest(
OptionSet options) {
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.packaging)) {
request.setPackaging(options.valueOf(this.packaging));
}
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.output)) {
request.setOutput(options.valueOf(this.output));
}
return request;
}
} }
} }
/*
* Copyright 2012-2014 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.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import joptsimple.OptionSet;
import joptsimple.OptionSpec;
import org.apache.http.impl.client.CloseableHttpClient;
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.StreamUtils;
/**
* The {@link OptionHandler} implementation for the init command.
*
* @author Stephane Nicoll
* @since 1.2.0
*/
public class InitCommandOptionHandler extends OptionHandler {
private final CloseableHttpClient httpClient;
private OptionSpec<String> target;
private OptionSpec<Void> listMetadata;
// Project generation options
private OptionSpec<String> bootVersion;
private OptionSpec<String> dependencies;
private OptionSpec<String> javaVersion;
private OptionSpec<String> packaging;
private OptionSpec<String> build;
private OptionSpec<String> format;
private OptionSpec<String> type;
// Other options
private OptionSpec<Void> extract;
private OptionSpec<Void> force;
private OptionSpec<String> output;
InitCommandOptionHandler(CloseableHttpClient httpClient) {
this.httpClient = httpClient;
}
@Override
protected void options() {
this.target = option(Arrays.asList("target"), "URL of the service to use")
.withRequiredArg().defaultsTo(
ProjectGenerationRequest.DEFAULT_SERVICE_URL);
this.listMetadata = option(Arrays.asList("list", "l"),
"List the capabilities of the service. Use it to "
+ "discover the dependencies and the types that are available.");
// Project generation settings
this.bootVersion = option(Arrays.asList("boot-version", "bv"),
"Spring Boot version to use (e.g. 1.2.0.RELEASE)").withRequiredArg();
this.dependencies = option(Arrays.asList("dependencies", "d"),
"Comma separated list of dependencies to include in the generated project")
.withRequiredArg();
this.javaVersion = option(Arrays.asList("java-version", "jv"),
"Java version to use (e.g. 1.8)").withRequiredArg();
this.packaging = option(Arrays.asList("packaging", "p"),
"Packaging type to use (e.g. jar)").withRequiredArg();
this.build = option(
"build",
"The build system to use (e.g. maven, gradle). To be used alongside "
+ "--format to uniquely identify one type that is supported by the service. "
+ "Use --type in case of conflict").withRequiredArg().defaultsTo(
"maven");
this.format = option(
"format",
"The format of the generated content (e.g. build for a build file, "
+ "project for a project archive). To be used alongside --build to uniquely identify one type "
+ "that is supported by the service. Use --type in case of conflict")
.withRequiredArg().defaultsTo("project");
this.type = option(
Arrays.asList("type", "t"),
"The project type to use. Not normally needed if you "
+ "use --build and/or --format. Check the capabilities of the service (--list) for "
+ "more details.").withRequiredArg();
// Others
this.extract = option(Arrays.asList("extract", "x"),
"Extract the project archive");
this.force = option(Arrays.asList("force", "f"),
"Force overwrite of existing files");
this.output = option(
Arrays.asList("output", "o"),
"Location of the generated project. Can be an absolute or a relative reference and "
+ "should refer to a directory when --extract is used.")
.withRequiredArg();
}
@Override
protected ExitStatus run(OptionSet options) throws Exception {
if (options.has(this.listMetadata)) {
return listServiceCapabilities(options, this.httpClient);
}
else {
return generateProject(options, this.httpClient);
}
}
public ProjectGenerationRequest createProjectGenerationRequest(OptionSet options) {
ProjectGenerationRequest request = new ProjectGenerationRequest();
request.setServiceUrl(determineServiceUrl(options));
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.packaging)) {
request.setPackaging(options.valueOf(this.packaging));
}
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.output)) {
request.setOutput(options.valueOf(this.output));
}
return request;
}
protected ExitStatus listServiceCapabilities(OptionSet options,
CloseableHttpClient httpClient) throws IOException {
ListMetadataCommand command = new ListMetadataCommand(httpClient);
Log.info(command.generateReport(determineServiceUrl(options)));
return ExitStatus.OK;
}
protected ExitStatus generateProject(OptionSet options, CloseableHttpClient httpClient) {
ProjectGenerationRequest request = createProjectGenerationRequest(options);
boolean forceValue = options.has(this.force);
try {
ProjectGenerationResponse entity = new InitializrServiceHttpInvoker(
httpClient).generate(request);
if (options.has(this.extract)) {
if (isZipArchive(entity)) {
return extractProject(entity, options.valueOf(this.output),
forceValue);
}
else {
Log.info("Could not extract '" + entity.getContentType() + "'");
}
}
String outputFileName = entity.getFileName() != null ? entity.getFileName()
: options.valueOf(this.output);
if (outputFileName == null) {
Log.error("Could not save the project, the server did not set a preferred "
+ "file name. Use --output to specify the output location for the project.");
return ExitStatus.ERROR;
}
return writeProject(entity, outputFileName, forceValue);
}
catch (ProjectGenerationException ex) {
Log.error(ex.getMessage());
return ExitStatus.ERROR;
}
catch (Exception ex) {
Log.error(ex);
return ExitStatus.ERROR;
}
}
private String determineServiceUrl(OptionSet options) {
return options.valueOf(this.target);
}
private ExitStatus writeProject(ProjectGenerationResponse entity,
String outputFileName, boolean overwrite) throws IOException {
File f = new File(outputFileName);
if (f.exists()) {
if (overwrite) {
if (!f.delete()) {
throw new IllegalStateException("Failed to delete existing file "
+ f.getPath());
}
}
else {
Log.error("File '" + f.getName()
+ "' already exists. Use --force if you want to "
+ "overwrite or --output to specify an alternate location.");
return ExitStatus.ERROR;
}
}
FileOutputStream stream = new FileOutputStream(f);
try {
StreamUtils.copy(entity.getContent(), stream);
Log.info("Content saved to '" + outputFileName + "'");
return ExitStatus.OK;
}
finally {
stream.close();
}
}
private boolean isZipArchive(ProjectGenerationResponse entity) {
if (entity.getContentType() == null) {
return false;
}
try {
return "application/zip".equals(entity.getContentType().getMimeType());
}
catch (Exception e) {
return false;
}
}
private ExitStatus extractProject(ProjectGenerationResponse entity,
String outputValue, boolean overwrite) throws IOException {
File output = outputValue != null ? new File(outputValue) : new File(
System.getProperty("user.dir"));
if (!output.exists()) {
output.mkdirs();
}
ZipInputStream zipIn = new ZipInputStream(new ByteArrayInputStream(
entity.getContent()));
try {
ZipEntry entry = zipIn.getNextEntry();
while (entry != null) {
File f = new File(output, entry.getName());
if (f.exists() && !overwrite) {
StringBuilder sb = new StringBuilder();
sb.append(f.isDirectory() ? "Directory" : "File")
.append(" '")
.append(f.getName())
.append("' already exists. Use --force if you want to "
+ "overwrite or --output to specify an alternate location.");
Log.error(sb.toString());
return ExitStatus.ERROR;
}
if (!entry.isDirectory()) {
extractZipEntry(zipIn, f);
}
else {
f.mkdir();
}
zipIn.closeEntry();
entry = zipIn.getNextEntry();
}
Log.info("Project extracted to '" + output.getAbsolutePath() + "'");
return ExitStatus.OK;
}
finally {
zipIn.close();
}
}
private void extractZipEntry(ZipInputStream in, File outputFile) throws IOException {
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(
outputFile));
try {
StreamUtils.copy(in, out);
}
finally {
out.close();
}
}
}
...@@ -59,13 +59,13 @@ class InitializrServiceMetadata { ...@@ -59,13 +59,13 @@ class InitializrServiceMetadata {
/** /**
* Creates a new instance using the specified root {@link JSONObject}. * Creates a new instance using the specified root {@link JSONObject}.
*/ */
InitializrServiceMetadata(JSONObject root) { public InitializrServiceMetadata(JSONObject root) {
this.dependencies = parseDependencies(root); this.dependencies = parseDependencies(root);
this.projectTypes = parseProjectTypes(root); this.projectTypes = parseProjectTypes(root);
this.defaults = Collections.unmodifiableMap(parseDefaults(root)); this.defaults = Collections.unmodifiableMap(parseDefaults(root));
} }
InitializrServiceMetadata(ProjectType defaultProjectType) { public InitializrServiceMetadata(ProjectType defaultProjectType) {
this.dependencies = new HashMap<String, Dependency>(); this.dependencies = new HashMap<String, Dependency>();
this.projectTypes = new MetadataHolder<String, ProjectType>(); this.projectTypes = new MetadataHolder<String, ProjectType>();
this.projectTypes.getContent() this.projectTypes.getContent()
...@@ -169,11 +169,10 @@ class InitializrServiceMetadata { ...@@ -169,11 +169,10 @@ class InitializrServiceMetadata {
} }
private Dependency parseDependency(JSONObject object) { private Dependency parseDependency(JSONObject object) {
Dependency dependency = new Dependency(); String id = getStringValue(object, ID_ATTRIBUTE, null);
dependency.setName(getStringValue(object, NAME_ATTRIBUTE, null)); String name = getStringValue(object, NAME_ATTRIBUTE, null);
dependency.setId(getStringValue(object, ID_ATTRIBUTE, null)); String description = getStringValue(object, DESCRIPTION_ATTRIBUTE, null);
dependency.setDescription(getStringValue(object, DESCRIPTION_ATTRIBUTE, null)); return new Dependency(id, name, description);
return dependency;
} }
private ProjectType parseType(JSONObject object) { private ProjectType parseType(JSONObject object) {
...@@ -230,6 +229,7 @@ class InitializrServiceMetadata { ...@@ -230,6 +229,7 @@ class InitializrServiceMetadata {
public void setDefaultItem(T defaultItem) { public void setDefaultItem(T defaultItem) {
this.defaultItem = defaultItem; this.defaultItem = defaultItem;
} }
} }
} }
...@@ -201,7 +201,7 @@ class ProjectGenerationRequest { ...@@ -201,7 +201,7 @@ class ProjectGenerationRequest {
return builder.build(); return builder.build();
} }
catch (URISyntaxException e) { catch (URISyntaxException e) {
throw new ProjectGenerationException("Invalid service URL (" + e.getMessage() throw new ReportableException("Invalid service URL (" + e.getMessage()
+ ")"); + ")");
} }
} }
...@@ -210,7 +210,7 @@ class ProjectGenerationRequest { ...@@ -210,7 +210,7 @@ class ProjectGenerationRequest {
if (this.type != null) { if (this.type != null) {
ProjectType result = metadata.getProjectTypes().get(this.type); ProjectType result = metadata.getProjectTypes().get(this.type);
if (result == null) { if (result == null) {
throw new ProjectGenerationException(("No project type with id '" throw new ReportableException(("No project type with id '"
+ this.type + "' - check the service capabilities (--list)")); + this.type + "' - check the service capabilities (--list)"));
} }
} }
...@@ -227,19 +227,19 @@ class ProjectGenerationRequest { ...@@ -227,19 +227,19 @@ class ProjectGenerationRequest {
return types.values().iterator().next(); return types.values().iterator().next();
} }
else if (types.size() == 0) { else if (types.size() == 0) {
throw new ProjectGenerationException("No type found with build '" throw new ReportableException("No type found with build '"
+ this.build + "' and format '" + this.format + this.build + "' and format '" + this.format
+ "' check the service capabilities (--list)"); + "' check the service capabilities (--list)");
} }
else { else {
throw new ProjectGenerationException("Multiple types found with build '" throw new ReportableException("Multiple types found with build '"
+ this.build + "' and format '" + this.format + this.build + "' and format '" + this.format
+ "' use --type with a more specific value " + types.keySet()); + "' use --type with a more specific value " + types.keySet());
} }
} }
ProjectType defaultType = metadata.getDefaultType(); ProjectType defaultType = metadata.getDefaultType();
if (defaultType == null) { if (defaultType == null) {
throw new ProjectGenerationException( throw new ReportableException(
("No project type is set and no default is defined. " ("No project type is set and no default is defined. "
+ "Check the service capabilities (--list)")); + "Check the service capabilities (--list)"));
} }
......
...@@ -26,13 +26,14 @@ import org.apache.http.entity.ContentType; ...@@ -26,13 +26,14 @@ import org.apache.http.entity.ContentType;
*/ */
class ProjectGenerationResponse { class ProjectGenerationResponse {
private ContentType contentType; private final ContentType contentType;
private byte[] content; private byte[] content;
private String fileName; private String fileName;
ProjectGenerationResponse() { public ProjectGenerationResponse(ContentType contentType) {
this.contentType = contentType;
} }
/** /**
...@@ -42,10 +43,6 @@ class ProjectGenerationResponse { ...@@ -42,10 +43,6 @@ class ProjectGenerationResponse {
return this.contentType; return this.contentType;
} }
public void setContentType(ContentType contentType) {
this.contentType = contentType;
}
/** /**
* The generated project archive or file. * The generated project archive or file.
*/ */
......
/*
* Copyright 2012-2014 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.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import org.springframework.boot.cli.util.Log;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.StreamUtils;
/**
* @author Stephane Nicoll
* @since 1.2.0
*/
public class ProjectGenerator {
private static final String ZIP_MIME_TYPE = "application/zip";
private final InitializrService initializrService;
public ProjectGenerator(InitializrService initializrService) {
this.initializrService = initializrService;
}
public void generateProject(ProjectGenerationRequest request, boolean force,
boolean extract, String output) throws IOException {
ProjectGenerationResponse response = this.initializrService.generate(request);
if (extract) {
if (isZipArchive(response)) {
extractProject(response, output, force);
return;
}
else {
Log.info("Could not extract '" + response.getContentType() + "'");
}
}
String fileName = response.getFileName();
fileName = (fileName != null ? fileName : output);
if (fileName == null) {
throw new ReportableException(
"Could not save the project, the server did not set a preferred "
+ "file name. Use --output to specify the output location "
+ "for the project.");
}
writeProject(response, fileName, force);
}
private boolean isZipArchive(ProjectGenerationResponse entity) {
if (entity.getContentType() != null) {
try {
return ZIP_MIME_TYPE.equals(entity.getContentType().getMimeType());
}
catch (Exception ex) {
}
}
return false;
}
private void extractProject(ProjectGenerationResponse entity, String output,
boolean overwrite) throws IOException {
File outputFolder = (output != null ? new File(output) : new File(
System.getProperty("user.dir")));
if (!outputFolder.exists()) {
outputFolder.mkdirs();
}
ZipInputStream zipStream = new ZipInputStream(new ByteArrayInputStream(
entity.getContent()));
try {
extractFromStream(zipStream, overwrite, outputFolder);
Log.info("Project extracted to '" + outputFolder.getAbsolutePath() + "'");
}
finally {
zipStream.close();
}
}
private void extractFromStream(ZipInputStream zipStream, boolean overwrite,
File outputFolder) throws IOException {
ZipEntry entry = zipStream.getNextEntry();
while (entry != null) {
File file = new File(outputFolder, entry.getName());
if (file.exists() && !overwrite) {
throw new ReportableException(file.isDirectory() ? "Directory" : "File"
+ " '" + file.getName()
+ "' already exists. Use --force if you want to overwrite or "
+ "--output to specify an alternate location.");
}
if (!entry.isDirectory()) {
FileCopyUtils.copy(StreamUtils.nonClosing(zipStream),
new FileOutputStream(file));
}
else {
file.mkdir();
}
zipStream.closeEntry();
entry = zipStream.getNextEntry();
}
}
private void writeProject(ProjectGenerationResponse entity, String output,
boolean overwrite) throws IOException {
File outputFile = new File(output);
if (outputFile.exists()) {
if (!overwrite) {
throw new ReportableException("File '" + outputFile.getName()
+ "' already exists. Use --force if you want to "
+ "overwrite or --output to specify an alternate location.");
}
if (!outputFile.delete()) {
throw new ReportableException("Failed to delete existing file "
+ outputFile.getPath());
}
}
FileCopyUtils.copy(entity.getContent(), outputFile);
Log.info("Content saved to '" + output + "'");
}
}
...@@ -68,4 +68,5 @@ class ProjectType { ...@@ -68,4 +68,5 @@ class ProjectType {
public Map<String, String> getTags() { public Map<String, String> getTags() {
return Collections.unmodifiableMap(this.tags); return Collections.unmodifiableMap(this.tags);
} }
} }
...@@ -17,15 +17,19 @@ ...@@ -17,15 +17,19 @@
package org.springframework.boot.cli.command.init; package org.springframework.boot.cli.command.init;
/** /**
* Thrown when a project could not be generated. * Exception with a message that can be reported to the user.
* *
* @author Stephane Nicoll * @author Stephane Nicoll
* @since 1.2.0 * @since 1.2.0
*/ */
public class ProjectGenerationException extends RuntimeException { public class ReportableException extends RuntimeException {
public ProjectGenerationException(String message) { public ReportableException(String message) {
super(message); super(message);
} }
public ReportableException(String message, Throwable cause) {
super(message, cause);
}
} }
...@@ -28,41 +28,60 @@ import org.apache.http.impl.client.CloseableHttpClient; ...@@ -28,41 +28,60 @@ import org.apache.http.impl.client.CloseableHttpClient;
import org.codehaus.plexus.util.StringUtils; import org.codehaus.plexus.util.StringUtils;
/** /**
* A helper class generating a report from the metadata of a particular service. * A helper class generating a report from the meta-data of a particular service.
* *
* @author Stephane Nicoll * @author Stephane Nicoll
* @since 1.2.0 * @since 1.2.0
*/ */
class ListMetadataCommand { class ServiceCapabilitiesReportGenerator {
private static final String NEW_LINE = System.getProperty("line.separator"); private static final String NEW_LINE = System.getProperty("line.separator");
private final InitializrServiceHttpInvoker initializrServiceInvoker; private final InitializrService initializrService;
/** /**
* Creates an instance using the specified {@link CloseableHttpClient}. * Creates an instance using the specified {@link CloseableHttpClient}.
*/ */
ListMetadataCommand(CloseableHttpClient httpClient) { ServiceCapabilitiesReportGenerator(InitializrService initializrService) {
this.initializrServiceInvoker = new InitializrServiceHttpInvoker(httpClient); this.initializrService = initializrService;
} }
/** /**
* Generate a report for the specified service. The report contains the available * Generate a report for the specified service. The report contains the available
* capabilities as advertized by the root endpoint. * capabilities as advertized by the root endpoint.
*/ */
String generateReport(String serviceUrl) throws IOException { public String generate(String url) throws IOException {
InitializrServiceMetadata metadata = this.initializrServiceInvoker InitializrServiceMetadata metadata = this.initializrService.loadMetadata(url);
.loadMetadata(serviceUrl); String header = "Capabilities of " + url;
String header = "Capabilities of " + serviceUrl; StringBuilder report = new StringBuilder();
int size = header.length(); report.append(StringUtils.repeat("=", header.length()) + NEW_LINE);
report.append(header + NEW_LINE);
report.append(StringUtils.repeat("=", header.length()) + NEW_LINE);
report.append(NEW_LINE);
reportAvailableDependencies(metadata, report);
report.append(NEW_LINE);
reportAvilableProjectTypes(metadata, report);
report.append(NEW_LINE);
z(metadata, report);
return report.toString();
}
StringBuilder sb = new StringBuilder(); private void reportAvailableDependencies(InitializrServiceMetadata metadata,
sb.append(StringUtils.repeat("=", size)).append(NEW_LINE).append(header) StringBuilder report) {
.append(NEW_LINE).append(StringUtils.repeat("=", size)).append(NEW_LINE) report.append("Available dependencies:" + NEW_LINE);
.append(NEW_LINE).append("Available dependencies:").append(NEW_LINE) report.append("-----------------------" + NEW_LINE);
.append("-----------------------").append(NEW_LINE); List<Dependency> dependencies = getSortedDependencies(metadata);
for (Dependency dependency : dependencies) {
report.append(dependency.getId() + " - " + dependency.getName());
if (dependency.getDescription() != null) {
report.append(": " + dependency.getDescription());
}
report.append(NEW_LINE);
}
}
List<Dependency> dependencies = new ArrayList<Dependency>( private List<Dependency> getSortedDependencies(InitializrServiceMetadata metadata) {
ArrayList<Dependency> dependencies = new ArrayList<Dependency>(
metadata.getDependencies()); metadata.getDependencies());
Collections.sort(dependencies, new Comparator<Dependency>() { Collections.sort(dependencies, new Comparator<Dependency>() {
@Override @Override
...@@ -70,50 +89,51 @@ class ListMetadataCommand { ...@@ -70,50 +89,51 @@ class ListMetadataCommand {
return o1.getId().compareTo(o2.getId()); return o1.getId().compareTo(o2.getId());
} }
}); });
for (Dependency dependency : dependencies) { return dependencies;
sb.append(dependency.getId()).append(" - ").append(dependency.getName()); }
if (dependency.getDescription() != null) {
sb.append(": ").append(dependency.getDescription());
}
sb.append(NEW_LINE);
}
sb.append(NEW_LINE).append("Available project types:").append(NEW_LINE) private void reportAvilableProjectTypes(InitializrServiceMetadata metadata,
.append("------------------------").append(NEW_LINE); StringBuilder report) {
report.append("Available project types:" + NEW_LINE);
report.append("------------------------" + NEW_LINE);
List<String> typeIds = new ArrayList<String>(metadata.getProjectTypes().keySet()); List<String> typeIds = new ArrayList<String>(metadata.getProjectTypes().keySet());
Collections.sort(typeIds); Collections.sort(typeIds);
for (String typeId : typeIds) { for (String typeId : typeIds) {
ProjectType type = metadata.getProjectTypes().get(typeId); ProjectType type = metadata.getProjectTypes().get(typeId);
sb.append(typeId).append(" - ").append(type.getName()); report.append(typeId + " - " + type.getName());
if (!type.getTags().isEmpty()) { if (!type.getTags().isEmpty()) {
sb.append(" ["); reportTags(report, type);
Iterator<Map.Entry<String, String>> it = type.getTags().entrySet()
.iterator();
while (it.hasNext()) {
Map.Entry<String, String> entry = it.next();
sb.append(entry.getKey()).append(":").append(entry.getValue());
if (it.hasNext()) {
sb.append(", ");
}
}
sb.append("]");
} }
if (type.isDefaultType()) { if (type.isDefaultType()) {
sb.append(" (default)"); report.append(" (default)");
} }
sb.append(NEW_LINE); report.append(NEW_LINE);
} }
}
sb.append(NEW_LINE).append("Defaults:").append(NEW_LINE).append("---------") private void reportTags(StringBuilder report, ProjectType type) {
.append(NEW_LINE); Map<String, String> tags = type.getTags();
Iterator<Map.Entry<String, String>> iterator = tags.entrySet().iterator();
report.append(" [");
while (iterator.hasNext()) {
Map.Entry<String, String> entry = iterator.next();
report.append(entry.getKey() + ":" + entry.getValue());
if (iterator.hasNext()) {
report.append(", ");
}
}
report.append("]");
}
private void z(InitializrServiceMetadata metadata, StringBuilder report) {
report.append("Defaults:" + NEW_LINE);
report.append("---------" + NEW_LINE);
List<String> defaultsKeys = new ArrayList<String>(metadata.getDefaults().keySet()); List<String> defaultsKeys = new ArrayList<String>(metadata.getDefaults().keySet());
Collections.sort(defaultsKeys); Collections.sort(defaultsKeys);
for (String defaultsKey : defaultsKeys) { for (String defaultsKey : defaultsKeys) {
sb.append(defaultsKey).append(": ") String defaultsValue = metadata.getDefaults().get(defaultsKey);
.append(metadata.getDefaults().get(defaultsKey)).append(NEW_LINE); report.append(defaultsKey + ": " + defaultsValue + NEW_LINE);
} }
return sb.toString();
} }
} }
...@@ -95,4 +95,9 @@ public final class ExitStatus { ...@@ -95,4 +95,9 @@ public final class ExitStatus {
return new ExitStatus(this.code, this.name, true); return new ExitStatus(this.code, this.name, true);
} }
@Override
public String toString() {
return getName() + ":" + getCode();
}
} }
...@@ -34,18 +34,19 @@ import org.springframework.core.io.ClassPathResource; ...@@ -34,18 +34,19 @@ import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.util.StreamUtils; import org.springframework.util.StreamUtils;
import static org.mockito.BDDMockito.given;
import static org.mockito.Matchers.argThat; import static org.mockito.Matchers.argThat;
import static org.mockito.Matchers.isA; import static org.mockito.Matchers.isA;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/** /**
* Abstract base class for tests that use a mock {@link CloseableHttpClient}.
* *
* @author Stephane Nicoll * @author Stephane Nicoll
*/ */
public abstract class AbstractHttpClientMockTests { public abstract class AbstractHttpClientMockTests {
protected final CloseableHttpClient httpClient = mock(CloseableHttpClient.class); protected final CloseableHttpClient http = mock(CloseableHttpClient.class);
protected void mockSuccessfulMetadataGet() throws IOException { protected void mockSuccessfulMetadataGet() throws IOException {
mockSuccessfulMetadataGet("1.1.0"); mockSuccessfulMetadataGet("1.1.0");
...@@ -58,34 +59,31 @@ public abstract class AbstractHttpClientMockTests { ...@@ -58,34 +59,31 @@ public abstract class AbstractHttpClientMockTests {
byte[] content = StreamUtils.copyToByteArray(resource.getInputStream()); byte[] content = StreamUtils.copyToByteArray(resource.getInputStream());
mockHttpEntity(response, content, "application/json"); mockHttpEntity(response, content, "application/json");
mockStatus(response, 200); mockStatus(response, 200);
when(this.httpClient.execute(argThat(getForJsonData()))).thenReturn(response); given(this.http.execute(argThat(getForJsonData()))).willReturn(response);
} }
protected void mockSuccessfulProjectGeneration( protected void mockSuccessfulProjectGeneration(
MockHttpProjectGenerationRequest request) throws IOException { MockHttpProjectGenerationRequest request) throws IOException {
// Required for project generation as the metadata is read first // Required for project generation as the metadata is read first
mockSuccessfulMetadataGet(); mockSuccessfulMetadataGet();
CloseableHttpResponse response = mock(CloseableHttpResponse.class); CloseableHttpResponse response = mock(CloseableHttpResponse.class);
mockHttpEntity(response, request.content, request.contentType); mockHttpEntity(response, request.content, request.contentType);
mockStatus(response, 200); mockStatus(response, 200);
String header = (request.fileName != null ? contentDispositionValue(request.fileName)
String header = request.fileName != null ? contentDispositionValue(request.fileName) : null);
: null;
mockHttpHeader(response, "Content-Disposition", header); mockHttpHeader(response, "Content-Disposition", header);
when(this.httpClient.execute(argThat(getForNonJsonData()))).thenReturn(response); given(this.http.execute(argThat(getForNonJsonData()))).willReturn(response);
} }
protected void mockProjectGenerationError(int status, String message) protected void mockProjectGenerationError(int status, String message)
throws IOException { throws IOException {
// Required for project generation as the metadata is read first // Required for project generation as the metadata is read first
mockSuccessfulMetadataGet(); mockSuccessfulMetadataGet();
CloseableHttpResponse response = mock(CloseableHttpResponse.class); CloseableHttpResponse response = mock(CloseableHttpResponse.class);
mockHttpEntity(response, createJsonError(status, message).getBytes(), mockHttpEntity(response, createJsonError(status, message).getBytes(),
"application/json"); "application/json");
mockStatus(response, status); mockStatus(response, status);
when(this.httpClient.execute(isA(HttpGet.class))).thenReturn(response); given(this.http.execute(isA(HttpGet.class))).willReturn(response);
} }
protected void mockMetadataGetError(int status, String message) throws IOException { protected void mockMetadataGetError(int status, String message) throws IOException {
...@@ -93,35 +91,35 @@ public abstract class AbstractHttpClientMockTests { ...@@ -93,35 +91,35 @@ public abstract class AbstractHttpClientMockTests {
mockHttpEntity(response, createJsonError(status, message).getBytes(), mockHttpEntity(response, createJsonError(status, message).getBytes(),
"application/json"); "application/json");
mockStatus(response, status); mockStatus(response, status);
when(this.httpClient.execute(isA(HttpGet.class))).thenReturn(response); given(this.http.execute(isA(HttpGet.class))).willReturn(response);
} }
protected HttpEntity mockHttpEntity(CloseableHttpResponse response, byte[] content, protected HttpEntity mockHttpEntity(CloseableHttpResponse response, byte[] content,
String contentType) { String contentType) {
try { try {
HttpEntity entity = mock(HttpEntity.class); HttpEntity entity = mock(HttpEntity.class);
when(entity.getContent()).thenReturn(new ByteArrayInputStream(content)); given(entity.getContent()).willReturn(new ByteArrayInputStream(content));
Header contentTypeHeader = contentType != null ? new BasicHeader( Header contentTypeHeader = contentType != null ? new BasicHeader(
"Content-Type", contentType) : null; "Content-Type", contentType) : null;
when(entity.getContentType()).thenReturn(contentTypeHeader); given(entity.getContentType()).willReturn(contentTypeHeader);
when(response.getEntity()).thenReturn(entity); given(response.getEntity()).willReturn(entity);
return entity; return entity;
} }
catch (IOException e) { catch (IOException ex) {
throw new IllegalStateException("Should not happen", e); throw new IllegalStateException("Should not happen", ex);
} }
} }
protected void mockStatus(CloseableHttpResponse response, int status) { protected void mockStatus(CloseableHttpResponse response, int status) {
StatusLine statusLine = mock(StatusLine.class); StatusLine statusLine = mock(StatusLine.class);
when(statusLine.getStatusCode()).thenReturn(status); given(statusLine.getStatusCode()).willReturn(status);
when(response.getStatusLine()).thenReturn(statusLine); given(response.getStatusLine()).willReturn(statusLine);
} }
protected void mockHttpHeader(CloseableHttpResponse response, String headerName, protected void mockHttpHeader(CloseableHttpResponse response, String headerName,
String value) { String value) {
Header header = value != null ? new BasicHeader(headerName, value) : null; Header header = value != null ? new BasicHeader(headerName, value) : null;
when(response.getFirstHeader(headerName)).thenReturn(header); given(response.getFirstHeader(headerName)).willReturn(header);
} }
protected Matcher<HttpGet> getForJsonData() { protected Matcher<HttpGet> getForJsonData() {
...@@ -153,6 +151,10 @@ public abstract class AbstractHttpClientMockTests { ...@@ -153,6 +151,10 @@ public abstract class AbstractHttpClientMockTests {
byte[] content = new byte[] { 0, 0, 0, 0 }; byte[] content = new byte[] { 0, 0, 0, 0 };
public MockHttpProjectGenerationRequest(String contentType, String fileName) {
this(contentType, fileName, new byte[] { 0, 0, 0, 0 });
}
public MockHttpProjectGenerationRequest(String contentType, String fileName, public MockHttpProjectGenerationRequest(String contentType, String fileName,
byte[] content) { byte[] content) {
this.contentType = contentType; this.contentType = contentType;
...@@ -160,9 +162,6 @@ public abstract class AbstractHttpClientMockTests { ...@@ -160,9 +162,6 @@ public abstract class AbstractHttpClientMockTests {
this.content = content; this.content = content;
} }
public MockHttpProjectGenerationRequest(String contentType, String fileName) {
this(contentType, fileName, new byte[] { 0, 0, 0, 0 });
}
} }
private static class HasAcceptHeader extends ArgumentMatcher<HttpGet> { private static class HasAcceptHeader extends ArgumentMatcher<HttpGet> {
...@@ -186,10 +185,7 @@ public abstract class AbstractHttpClientMockTests { ...@@ -186,10 +185,7 @@ public abstract class AbstractHttpClientMockTests {
if (this.shouldMatch) { if (this.shouldMatch) {
return acceptHeader != null && this.value.equals(acceptHeader.getValue()); return acceptHeader != null && this.value.equals(acceptHeader.getValue());
} }
else { return acceptHeader == null || !this.value.equals(acceptHeader.getValue());
return acceptHeader == null
|| !this.value.equals(acceptHeader.getValue());
}
} }
} }
......
...@@ -80,8 +80,8 @@ public class InitializrServiceMetadataTests { ...@@ -80,8 +80,8 @@ public class InitializrServiceMetadataTests {
try { try {
return new InitializrServiceMetadata(readJson(version)); return new InitializrServiceMetadata(readJson(version));
} }
catch (IOException e) { catch (IOException ex) {
throw new IllegalStateException("Failed to read json", e); throw new IllegalStateException("Failed to read json", ex);
} }
} }
...@@ -90,8 +90,8 @@ public class InitializrServiceMetadataTests { ...@@ -90,8 +90,8 @@ public class InitializrServiceMetadataTests {
+ ".json"); + ".json");
InputStream stream = resource.getInputStream(); InputStream stream = resource.getInputStream();
try { try {
String json = StreamUtils.copyToString(stream, Charset.forName("UTF-8")); return new JSONObject(StreamUtils.copyToString(stream,
return new JSONObject(json); Charset.forName("UTF-8")));
} }
finally { finally {
stream.close(); stream.close();
......
...@@ -33,17 +33,16 @@ import static org.mockito.Mockito.mock; ...@@ -33,17 +33,16 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
/** /**
* Tests for {@link InitializrServiceHttpInvoker} * Tests for {@link InitializrService}
* *
* @author Stephane Nicoll * @author Stephane Nicoll
*/ */
public class InitializrServiceHttpInvokerTests extends AbstractHttpClientMockTests { public class InitializrServiceTests extends AbstractHttpClientMockTests {
@Rule @Rule
public final ExpectedException thrown = ExpectedException.none(); public final ExpectedException thrown = ExpectedException.none();
private final InitializrServiceHttpInvoker invoker = new InitializrServiceHttpInvoker( private final InitializrService invoker = new InitializrService(this.http);
this.httpClient);
@Test @Test
public void loadMetadata() throws IOException { public void loadMetadata() throws IOException {
...@@ -86,8 +85,7 @@ public class InitializrServiceHttpInvokerTests extends AbstractHttpClientMockTes ...@@ -86,8 +85,7 @@ public class InitializrServiceHttpInvokerTests extends AbstractHttpClientMockTes
mockProjectGenerationError(400, jsonMessage); mockProjectGenerationError(400, jsonMessage);
ProjectGenerationRequest request = new ProjectGenerationRequest(); ProjectGenerationRequest request = new ProjectGenerationRequest();
request.getDependencies().add("foo:bar"); request.getDependencies().add("foo:bar");
this.thrown.expect(ReportableException.class);
this.thrown.expect(ProjectGenerationException.class);
this.thrown.expectMessage(jsonMessage); this.thrown.expectMessage(jsonMessage);
this.invoker.generate(request); this.invoker.generate(request);
} }
...@@ -95,9 +93,8 @@ public class InitializrServiceHttpInvokerTests extends AbstractHttpClientMockTes ...@@ -95,9 +93,8 @@ public class InitializrServiceHttpInvokerTests extends AbstractHttpClientMockTes
@Test @Test
public void generateProjectBadRequestNoExtraMessage() throws IOException { public void generateProjectBadRequestNoExtraMessage() throws IOException {
mockProjectGenerationError(400, null); mockProjectGenerationError(400, null);
ProjectGenerationRequest request = new ProjectGenerationRequest(); ProjectGenerationRequest request = new ProjectGenerationRequest();
this.thrown.expect(ProjectGenerationException.class); this.thrown.expect(ReportableException.class);
this.thrown.expectMessage("unexpected 400 error"); this.thrown.expectMessage("unexpected 400 error");
this.invoker.generate(request); this.invoker.generate(request);
} }
...@@ -105,14 +102,11 @@ public class InitializrServiceHttpInvokerTests extends AbstractHttpClientMockTes ...@@ -105,14 +102,11 @@ public class InitializrServiceHttpInvokerTests extends AbstractHttpClientMockTes
@Test @Test
public void generateProjectNoContent() throws IOException { public void generateProjectNoContent() throws IOException {
mockSuccessfulMetadataGet(); mockSuccessfulMetadataGet();
CloseableHttpResponse response = mock(CloseableHttpResponse.class); CloseableHttpResponse response = mock(CloseableHttpResponse.class);
mockStatus(response, 500); mockStatus(response, 500);
when(this.httpClient.execute(isA(HttpGet.class))).thenReturn(response); when(this.http.execute(isA(HttpGet.class))).thenReturn(response);
ProjectGenerationRequest request = new ProjectGenerationRequest(); ProjectGenerationRequest request = new ProjectGenerationRequest();
this.thrown.expect(ReportableException.class);
this.thrown.expect(ProjectGenerationException.class);
this.thrown.expectMessage("No content received from server"); this.thrown.expectMessage("No content received from server");
this.invoker.generate(request); this.invoker.generate(request);
} }
...@@ -122,8 +116,7 @@ public class InitializrServiceHttpInvokerTests extends AbstractHttpClientMockTes ...@@ -122,8 +116,7 @@ public class InitializrServiceHttpInvokerTests extends AbstractHttpClientMockTes
String jsonMessage = "whatever error on the server"; String jsonMessage = "whatever error on the server";
mockMetadataGetError(500, jsonMessage); mockMetadataGetError(500, jsonMessage);
ProjectGenerationRequest request = new ProjectGenerationRequest(); ProjectGenerationRequest request = new ProjectGenerationRequest();
this.thrown.expect(ReportableException.class);
this.thrown.expect(ProjectGenerationException.class);
this.thrown.expectMessage(jsonMessage); this.thrown.expectMessage(jsonMessage);
this.invoker.generate(request); this.invoker.generate(request);
} }
...@@ -133,10 +126,9 @@ public class InitializrServiceHttpInvokerTests extends AbstractHttpClientMockTes ...@@ -133,10 +126,9 @@ public class InitializrServiceHttpInvokerTests extends AbstractHttpClientMockTes
CloseableHttpResponse response = mock(CloseableHttpResponse.class); CloseableHttpResponse response = mock(CloseableHttpResponse.class);
mockHttpEntity(response, "Foo-Bar-Not-JSON".getBytes(), "application/json"); mockHttpEntity(response, "Foo-Bar-Not-JSON".getBytes(), "application/json");
mockStatus(response, 200); mockStatus(response, 200);
when(this.httpClient.execute(isA(HttpGet.class))).thenReturn(response); when(this.http.execute(isA(HttpGet.class))).thenReturn(response);
ProjectGenerationRequest request = new ProjectGenerationRequest(); ProjectGenerationRequest request = new ProjectGenerationRequest();
this.thrown.expect(ProjectGenerationException.class); this.thrown.expect(ReportableException.class);
this.thrown.expectMessage("Invalid content received from server"); this.thrown.expectMessage("Invalid content received from server");
this.invoker.generate(request); this.invoker.generate(request);
} }
...@@ -145,11 +137,9 @@ public class InitializrServiceHttpInvokerTests extends AbstractHttpClientMockTes ...@@ -145,11 +137,9 @@ public class InitializrServiceHttpInvokerTests extends AbstractHttpClientMockTes
public void loadMetadataNoContent() throws IOException { public void loadMetadataNoContent() throws IOException {
CloseableHttpResponse response = mock(CloseableHttpResponse.class); CloseableHttpResponse response = mock(CloseableHttpResponse.class);
mockStatus(response, 500); mockStatus(response, 500);
when(this.httpClient.execute(isA(HttpGet.class))).thenReturn(response); when(this.http.execute(isA(HttpGet.class))).thenReturn(response);
ProjectGenerationRequest request = new ProjectGenerationRequest(); ProjectGenerationRequest request = new ProjectGenerationRequest();
this.thrown.expect(ReportableException.class);
this.thrown.expect(ProjectGenerationException.class);
this.thrown.expectMessage("No content received from server"); this.thrown.expectMessage("No content received from server");
this.invoker.generate(request); this.invoker.generate(request);
} }
......
...@@ -105,7 +105,6 @@ public class ProjectGenerationRequestTests { ...@@ -105,7 +105,6 @@ public class ProjectGenerationRequestTests {
ProjectType projectType = new ProjectType("custom", "Custom Type", "/foo", true, ProjectType projectType = new ProjectType("custom", "Custom Type", "/foo", true,
EMPTY_TAGS); EMPTY_TAGS);
InitializrServiceMetadata metadata = new InitializrServiceMetadata(projectType); InitializrServiceMetadata metadata = new InitializrServiceMetadata(projectType);
this.request.setType("custom"); this.request.setType("custom");
this.request.getDependencies().add("data-rest"); this.request.getDependencies().add("data-rest");
assertEquals(new URI(ProjectGenerationRequest.DEFAULT_SERVICE_URL assertEquals(new URI(ProjectGenerationRequest.DEFAULT_SERVICE_URL
...@@ -116,8 +115,7 @@ public class ProjectGenerationRequestTests { ...@@ -116,8 +115,7 @@ public class ProjectGenerationRequestTests {
public void buildNoMatch() { public void buildNoMatch() {
InitializrServiceMetadata metadata = readMetadata(); InitializrServiceMetadata metadata = readMetadata();
setBuildAndFormat("does-not-exist", null); setBuildAndFormat("does-not-exist", null);
this.thrown.expect(ReportableException.class);
this.thrown.expect(ProjectGenerationException.class);
this.thrown.expectMessage("does-not-exist"); this.thrown.expectMessage("does-not-exist");
this.request.generateUrl(metadata); this.request.generateUrl(metadata);
} }
...@@ -126,8 +124,7 @@ public class ProjectGenerationRequestTests { ...@@ -126,8 +124,7 @@ public class ProjectGenerationRequestTests {
public void buildMultipleMatch() { public void buildMultipleMatch() {
InitializrServiceMetadata metadata = readMetadata("types-conflict"); InitializrServiceMetadata metadata = readMetadata("types-conflict");
setBuildAndFormat("gradle", null); setBuildAndFormat("gradle", null);
this.thrown.expect(ReportableException.class);
this.thrown.expect(ProjectGenerationException.class);
this.thrown.expectMessage("gradle-project"); this.thrown.expectMessage("gradle-project");
this.thrown.expectMessage("gradle-project-2"); this.thrown.expectMessage("gradle-project-2");
this.request.generateUrl(metadata); this.request.generateUrl(metadata);
...@@ -137,7 +134,6 @@ public class ProjectGenerationRequestTests { ...@@ -137,7 +134,6 @@ public class ProjectGenerationRequestTests {
public void buildOneMatch() { public void buildOneMatch() {
InitializrServiceMetadata metadata = readMetadata(); InitializrServiceMetadata metadata = readMetadata();
setBuildAndFormat("gradle", null); setBuildAndFormat("gradle", null);
assertEquals(createDefaultUrl("?type=gradle-project"), assertEquals(createDefaultUrl("?type=gradle-project"),
this.request.generateUrl(metadata)); this.request.generateUrl(metadata));
} }
...@@ -145,15 +141,13 @@ public class ProjectGenerationRequestTests { ...@@ -145,15 +141,13 @@ public class ProjectGenerationRequestTests {
@Test @Test
public void invalidType() throws URISyntaxException { public void invalidType() throws URISyntaxException {
this.request.setType("does-not-exist"); this.request.setType("does-not-exist");
this.thrown.expect(ReportableException.class);
this.thrown.expect(ProjectGenerationException.class);
this.request.generateUrl(createDefaultMetadata()); this.request.generateUrl(createDefaultMetadata());
} }
@Test @Test
public void noTypeAndNoDefault() throws URISyntaxException { public void noTypeAndNoDefault() throws URISyntaxException {
this.thrown.expect(ReportableException.class);
this.thrown.expect(ProjectGenerationException.class);
this.thrown.expectMessage("no default is defined"); this.thrown.expectMessage("no default is defined");
this.request.generateUrl(readMetadata("types-conflict")); this.request.generateUrl(readMetadata("types-conflict"));
} }
...@@ -163,8 +157,8 @@ public class ProjectGenerationRequestTests { ...@@ -163,8 +157,8 @@ public class ProjectGenerationRequestTests {
return new URI(ProjectGenerationRequest.DEFAULT_SERVICE_URL + "/starter.zip" return new URI(ProjectGenerationRequest.DEFAULT_SERVICE_URL + "/starter.zip"
+ param); + param);
} }
catch (URISyntaxException e) { catch (URISyntaxException ex) {
throw new IllegalStateException(e); throw new IllegalStateException(ex);
} }
} }
...@@ -193,8 +187,8 @@ public class ProjectGenerationRequestTests { ...@@ -193,8 +187,8 @@ public class ProjectGenerationRequestTests {
JSONObject json = new JSONObject(content); JSONObject json = new JSONObject(content);
return new InitializrServiceMetadata(json); return new InitializrServiceMetadata(json);
} }
catch (IOException e) { catch (IOException ex) {
throw new IllegalStateException("Failed to read metadata", e); throw new IllegalStateException("Failed to read metadata", ex);
} }
} }
......
...@@ -23,19 +23,19 @@ import org.junit.Test; ...@@ -23,19 +23,19 @@ import org.junit.Test;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
/** /**
* Tests for {@link ListMetadataCommand} * Tests for {@link ServiceCapabilitiesReportGenerator}
* *
* @author Stephane Nicoll * @author Stephane Nicoll
*/ */
public class ListMetadataCommandTests extends AbstractHttpClientMockTests { public class ServiceCapabilitiesReportGeneratorTests extends AbstractHttpClientMockTests {
private final ListMetadataCommand command = new ListMetadataCommand(this.httpClient); private final ServiceCapabilitiesReportGenerator command = new ServiceCapabilitiesReportGenerator(
new InitializrService(this.http));
@Test @Test
public void listMetadata() throws IOException { public void listMetadata() throws IOException {
mockSuccessfulMetadataGet(); mockSuccessfulMetadataGet();
String content = this.command.generateReport("http://localhost"); String content = this.command.generate("http://localhost");
assertTrue(content.contains("aop - AOP")); assertTrue(content.contains("aop - AOP"));
assertTrue(content.contains("security - Security: Security description")); assertTrue(content.contains("security - Security: Security description"));
assertTrue(content.contains("type: maven-project")); assertTrue(content.contains("type: maven-project"));
......
...@@ -89,7 +89,6 @@ ...@@ -89,7 +89,6 @@
<jersey.version>2.13</jersey.version> <jersey.version>2.13</jersey.version>
<joda-time.version>2.4</joda-time.version> <joda-time.version>2.4</joda-time.version>
<jolokia.version>1.2.2</jolokia.version> <jolokia.version>1.2.2</jolokia.version>
<json.version>20140107</json.version>
<json-path.version>0.9.1</json-path.version> <json-path.version>0.9.1</json-path.version>
<jstl.version>1.2</jstl.version> <jstl.version>1.2</jstl.version>
<junit.version>4.11</junit.version> <junit.version>4.11</junit.version>
...@@ -986,11 +985,6 @@ ...@@ -986,11 +985,6 @@
<artifactId>jdom2</artifactId> <artifactId>jdom2</artifactId>
<version>${jdom2.version}</version> <version>${jdom2.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>${json.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.liquibase</groupId> <groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId> <artifactId>liquibase-core</artifactId>
...@@ -1504,4 +1498,4 @@ ...@@ -1504,4 +1498,4 @@
<id>integration-test</id> <id>integration-test</id>
</profile> </profile>
</profiles> </profiles>
</project> </project>
\ No newline at end of file
...@@ -18,10 +18,11 @@ ...@@ -18,10 +18,11 @@
</organization> </organization>
<properties> <properties>
<main.basedir>..</main.basedir> <main.basedir>..</main.basedir>
<aether.version>0.9.1.v20140329</aether.version>
<java.version>1.6</java.version> <java.version>1.6</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<aether.version>0.9.1.v20140329</aether.version>
<json.version>20140107</json.version>
<maven.version>3.1.1</maven.version> <maven.version>3.1.1</maven.version>
</properties> </properties>
<prerequisites> <prerequisites>
...@@ -184,6 +185,11 @@ ...@@ -184,6 +185,11 @@
<artifactId>gradle-plugins</artifactId> <artifactId>gradle-plugins</artifactId>
<version>${gradle.version}</version> <version>${gradle.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>${json.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.zeroturnaround</groupId> <groupId>org.zeroturnaround</groupId>
<artifactId>zt-zip</artifactId> <artifactId>zt-zip</artifactId>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment