Browse Source
Rename a few classes and methods and extract some logic into helper classes. Also change 2 char shortcuts to a single char. Closes gh-1751pull/1815/head
22 changed files with 811 additions and 861 deletions
@ -1,306 +0,0 @@ |
|||||||
/* |
|
||||||
* 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(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
@ -0,0 +1,195 @@ |
|||||||
|
/* |
||||||
|
* 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.IOException; |
||||||
|
import java.net.URI; |
||||||
|
import java.nio.charset.Charset; |
||||||
|
|
||||||
|
import org.apache.http.Header; |
||||||
|
import org.apache.http.HttpEntity; |
||||||
|
import org.apache.http.HttpHeaders; |
||||||
|
import org.apache.http.client.methods.CloseableHttpResponse; |
||||||
|
import org.apache.http.client.methods.HttpGet; |
||||||
|
import org.apache.http.client.methods.HttpUriRequest; |
||||||
|
import org.apache.http.entity.ContentType; |
||||||
|
import org.apache.http.impl.client.CloseableHttpClient; |
||||||
|
import org.apache.http.message.BasicHeader; |
||||||
|
import org.json.JSONException; |
||||||
|
import org.json.JSONObject; |
||||||
|
import org.springframework.boot.cli.util.Log; |
||||||
|
import org.springframework.util.FileCopyUtils; |
||||||
|
import org.springframework.util.StringUtils; |
||||||
|
|
||||||
|
/** |
||||||
|
* Invokes the initializr service over HTTP. |
||||||
|
* |
||||||
|
* @author Stephane Nicoll |
||||||
|
* @since 1.2.0 |
||||||
|
*/ |
||||||
|
class InitializrService { |
||||||
|
|
||||||
|
private static final String FILENAME_HEADER_PREFIX = "filename=\""; |
||||||
|
|
||||||
|
private static final Charset UTF_8 = Charset.forName("UTF-8"); |
||||||
|
|
||||||
|
private final CloseableHttpClient http; |
||||||
|
|
||||||
|
/** |
||||||
|
* Create a new instance with the given {@link CloseableHttpClient HTTP client}. |
||||||
|
*/ |
||||||
|
public InitializrService(CloseableHttpClient http) { |
||||||
|
this.http = http; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generate a project based on the specified {@link ProjectGenerationRequest} |
||||||
|
* @return an entity defining the project |
||||||
|
*/ |
||||||
|
public ProjectGenerationResponse generate(ProjectGenerationRequest request) |
||||||
|
throws IOException { |
||||||
|
Log.info("Using service at " + request.getServiceUrl()); |
||||||
|
InitializrServiceMetadata metadata = loadMetadata(request.getServiceUrl()); |
||||||
|
URI url = request.generateUrl(metadata); |
||||||
|
CloseableHttpResponse httpResponse = executeProjectGenerationRequest(url); |
||||||
|
HttpEntity httpEntity = httpResponse.getEntity(); |
||||||
|
if (httpEntity == null) { |
||||||
|
throw new ReportableException("No content received from server '" + url + "'"); |
||||||
|
} |
||||||
|
if (httpResponse.getStatusLine().getStatusCode() != 200) { |
||||||
|
throw createException(request.getServiceUrl(), httpResponse); |
||||||
|
} |
||||||
|
return createResponse(httpResponse, httpEntity); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Load the {@link InitializrServiceMetadata} at the specified url. |
||||||
|
*/ |
||||||
|
public InitializrServiceMetadata loadMetadata(String serviceUrl) throws IOException { |
||||||
|
CloseableHttpResponse httpResponse = executeInitializrMetadataRetrieval(serviceUrl); |
||||||
|
if (httpResponse.getEntity() == null) { |
||||||
|
throw new ReportableException("No content received from server '" |
||||||
|
+ serviceUrl + "'"); |
||||||
|
} |
||||||
|
if (httpResponse.getStatusLine().getStatusCode() != 200) { |
||||||
|
throw createException(serviceUrl, httpResponse); |
||||||
|
} |
||||||
|
try { |
||||||
|
HttpEntity httpEntity = httpResponse.getEntity(); |
||||||
|
return new InitializrServiceMetadata(getContentAsJson(httpEntity)); |
||||||
|
} |
||||||
|
catch (JSONException ex) { |
||||||
|
throw new ReportableException("Invalid content received from server (" |
||||||
|
+ ex.getMessage() + ")", ex); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private ProjectGenerationResponse createResponse(CloseableHttpResponse httpResponse, |
||||||
|
HttpEntity httpEntity) throws IOException { |
||||||
|
ProjectGenerationResponse response = new ProjectGenerationResponse( |
||||||
|
ContentType.getOrDefault(httpEntity)); |
||||||
|
response.setContent(FileCopyUtils.copyToByteArray(httpEntity.getContent())); |
||||||
|
String fileName = extractFileName(httpResponse |
||||||
|
.getFirstHeader("Content-Disposition")); |
||||||
|
if (fileName != null) { |
||||||
|
response.setFileName(fileName); |
||||||
|
} |
||||||
|
return response; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Request the creation of the project using the specified URL |
||||||
|
*/ |
||||||
|
private CloseableHttpResponse executeProjectGenerationRequest(URI url) { |
||||||
|
return execute(new HttpGet(url), url, "generate project"); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Retrieves the meta-data of the service at the specified URL |
||||||
|
*/ |
||||||
|
private CloseableHttpResponse executeInitializrMetadataRetrieval(String url) { |
||||||
|
HttpGet request = new HttpGet(url); |
||||||
|
request.setHeader(new BasicHeader(HttpHeaders.ACCEPT, "application/json")); |
||||||
|
return execute(request, url, "retrieve metadata"); |
||||||
|
} |
||||||
|
|
||||||
|
private CloseableHttpResponse execute(HttpUriRequest request, Object url, |
||||||
|
String description) { |
||||||
|
try { |
||||||
|
return this.http.execute(request); |
||||||
|
} |
||||||
|
catch (IOException ex) { |
||||||
|
throw new ReportableException("Failed to " + description |
||||||
|
+ " from service at '" + url + "' (" + ex.getMessage() + ")"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private ReportableException createException(String url, |
||||||
|
CloseableHttpResponse httpResponse) { |
||||||
|
String message = "Initializr service call failed using '" + url |
||||||
|
+ "' - service returned " |
||||||
|
+ httpResponse.getStatusLine().getReasonPhrase(); |
||||||
|
String error = extractMessage(httpResponse.getEntity()); |
||||||
|
if (StringUtils.hasText(error)) { |
||||||
|
message += ": '" + error + "'"; |
||||||
|
} |
||||||
|
else { |
||||||
|
int statusCode = httpResponse.getStatusLine().getStatusCode(); |
||||||
|
message += " (unexpected " + statusCode + " error)"; |
||||||
|
} |
||||||
|
throw new ReportableException(message.toString()); |
||||||
|
} |
||||||
|
|
||||||
|
private String extractMessage(HttpEntity entity) { |
||||||
|
if (entity != null) { |
||||||
|
try { |
||||||
|
JSONObject error = getContentAsJson(entity); |
||||||
|
if (error.has("message")) { |
||||||
|
return error.getString("message"); |
||||||
|
} |
||||||
|
} |
||||||
|
catch (Exception ex) { |
||||||
|
} |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
private JSONObject getContentAsJson(HttpEntity entity) throws IOException { |
||||||
|
ContentType contentType = ContentType.getOrDefault(entity); |
||||||
|
Charset charset = contentType.getCharset(); |
||||||
|
charset = (charset != null ? charset : UTF_8); |
||||||
|
byte[] content = FileCopyUtils.copyToByteArray(entity.getContent()); |
||||||
|
return new JSONObject(new String(content, charset)); |
||||||
|
} |
||||||
|
|
||||||
|
private String extractFileName(Header header) { |
||||||
|
if (header != null) { |
||||||
|
String value = header.getValue(); |
||||||
|
int start = value.indexOf(FILENAME_HEADER_PREFIX); |
||||||
|
if (start != -1) { |
||||||
|
value = value.substring(start + FILENAME_HEADER_PREFIX.length(), |
||||||
|
value.length()); |
||||||
|
int end = value.indexOf("\""); |
||||||
|
if (end != -1) { |
||||||
|
return value.substring(0, end); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -1,225 +0,0 @@ |
|||||||
/* |
|
||||||
* 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.IOException; |
|
||||||
import java.io.InputStream; |
|
||||||
import java.net.URI; |
|
||||||
import java.nio.charset.Charset; |
|
||||||
|
|
||||||
import org.apache.http.Header; |
|
||||||
import org.apache.http.HttpEntity; |
|
||||||
import org.apache.http.HttpHeaders; |
|
||||||
import org.apache.http.client.methods.CloseableHttpResponse; |
|
||||||
import org.apache.http.client.methods.HttpGet; |
|
||||||
import org.apache.http.entity.ContentType; |
|
||||||
import org.apache.http.impl.client.CloseableHttpClient; |
|
||||||
import org.apache.http.message.BasicHeader; |
|
||||||
import org.json.JSONException; |
|
||||||
import org.json.JSONObject; |
|
||||||
import org.springframework.boot.cli.util.Log; |
|
||||||
import org.springframework.util.StreamUtils; |
|
||||||
import org.springframework.util.StringUtils; |
|
||||||
|
|
||||||
/** |
|
||||||
* Invokes the initializr service over HTTP. |
|
||||||
* |
|
||||||
* @author Stephane Nicoll |
|
||||||
* @since 1.2.0 |
|
||||||
*/ |
|
||||||
class InitializrServiceHttpInvoker { |
|
||||||
|
|
||||||
private final CloseableHttpClient httpClient; |
|
||||||
|
|
||||||
/** |
|
||||||
* Create a new instance with the given {@link CloseableHttpClient http client}. |
|
||||||
*/ |
|
||||||
InitializrServiceHttpInvoker(CloseableHttpClient httpClient) { |
|
||||||
this.httpClient = httpClient; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Generate a project based on the specified {@link ProjectGenerationRequest} |
|
||||||
* @return an entity defining the project |
|
||||||
*/ |
|
||||||
ProjectGenerationResponse generate(ProjectGenerationRequest request) |
|
||||||
throws IOException { |
|
||||||
Log.info("Using service at " + request.getServiceUrl()); |
|
||||||
InitializrServiceMetadata metadata = loadMetadata(request.getServiceUrl()); |
|
||||||
URI url = request.generateUrl(metadata); |
|
||||||
CloseableHttpResponse httpResponse = executeProjectGenerationRequest(url); |
|
||||||
|
|
||||||
HttpEntity httpEntity = httpResponse.getEntity(); |
|
||||||
if (httpEntity == null) { |
|
||||||
throw new ProjectGenerationException( |
|
||||||
"No content received from server using '" + url + "'"); |
|
||||||
} |
|
||||||
if (httpResponse.getStatusLine().getStatusCode() != 200) { |
|
||||||
throw buildProjectGenerationException(request.getServiceUrl(), httpResponse); |
|
||||||
} |
|
||||||
return createResponse(httpResponse, httpEntity); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Load the {@link InitializrServiceMetadata} at the specified url. |
|
||||||
*/ |
|
||||||
InitializrServiceMetadata loadMetadata(String serviceUrl) throws IOException { |
|
||||||
CloseableHttpResponse httpResponse = executeInitializrMetadataRetrieval(serviceUrl); |
|
||||||
if (httpResponse.getEntity() == null) { |
|
||||||
throw new ProjectGenerationException( |
|
||||||
"No content received from server using '" + serviceUrl + "'"); |
|
||||||
} |
|
||||||
if (httpResponse.getStatusLine().getStatusCode() != 200) { |
|
||||||
throw buildProjectGenerationException(serviceUrl, httpResponse); |
|
||||||
} |
|
||||||
try { |
|
||||||
HttpEntity httpEntity = httpResponse.getEntity(); |
|
||||||
JSONObject root = getContentAsJson(getContent(httpEntity), |
|
||||||
getContentType(httpEntity)); |
|
||||||
return new InitializrServiceMetadata(root); |
|
||||||
} |
|
||||||
catch (JSONException e) { |
|
||||||
throw new ProjectGenerationException("Invalid content received from server (" |
|
||||||
+ e.getMessage() + ")"); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private ProjectGenerationResponse createResponse(CloseableHttpResponse httpResponse, |
|
||||||
HttpEntity httpEntity) throws IOException { |
|
||||||
ProjectGenerationResponse response = new ProjectGenerationResponse(); |
|
||||||
ContentType contentType = ContentType.getOrDefault(httpEntity); |
|
||||||
response.setContentType(contentType); |
|
||||||
|
|
||||||
InputStream in = httpEntity.getContent(); |
|
||||||
try { |
|
||||||
response.setContent(StreamUtils.copyToByteArray(in)); |
|
||||||
} |
|
||||||
finally { |
|
||||||
in.close(); |
|
||||||
} |
|
||||||
|
|
||||||
String detectedFileName = extractFileName(httpResponse |
|
||||||
.getFirstHeader("Content-Disposition")); |
|
||||||
if (detectedFileName != null) { |
|
||||||
response.setFileName(detectedFileName); |
|
||||||
} |
|
||||||
return response; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Request the creation of the project using the specified url |
|
||||||
*/ |
|
||||||
private CloseableHttpResponse executeProjectGenerationRequest(URI url) { |
|
||||||
try { |
|
||||||
HttpGet get = new HttpGet(url); |
|
||||||
return this.httpClient.execute(get); |
|
||||||
} |
|
||||||
catch (IOException e) { |
|
||||||
throw new ProjectGenerationException("Failed to invoke server at '" + url |
|
||||||
+ "' (" + e.getMessage() + ")"); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Retrieves the metadata of the service at the specified url |
|
||||||
*/ |
|
||||||
private CloseableHttpResponse executeInitializrMetadataRetrieval(String serviceUrl) { |
|
||||||
try { |
|
||||||
HttpGet get = new HttpGet(serviceUrl); |
|
||||||
get.setHeader(new BasicHeader(HttpHeaders.ACCEPT, "application/json")); |
|
||||||
return this.httpClient.execute(get); |
|
||||||
} |
|
||||||
catch (IOException e) { |
|
||||||
throw new ProjectGenerationException( |
|
||||||
"Failed to retrieve metadata from service at '" + serviceUrl + "' (" |
|
||||||
+ e.getMessage() + ")"); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private byte[] getContent(HttpEntity httpEntity) throws IOException { |
|
||||||
InputStream in = httpEntity.getContent(); |
|
||||||
try { |
|
||||||
return StreamUtils.copyToByteArray(in); |
|
||||||
} |
|
||||||
finally { |
|
||||||
in.close(); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private ContentType getContentType(HttpEntity httpEntity) { |
|
||||||
return ContentType.getOrDefault(httpEntity); |
|
||||||
} |
|
||||||
|
|
||||||
private JSONObject getContentAsJson(byte[] content, ContentType contentType) { |
|
||||||
Charset charset = contentType.getCharset() != null ? contentType.getCharset() |
|
||||||
: Charset.forName("UTF-8"); |
|
||||||
String data = new String(content, charset); |
|
||||||
return new JSONObject(data); |
|
||||||
} |
|
||||||
|
|
||||||
private ProjectGenerationException buildProjectGenerationException(String url, |
|
||||||
CloseableHttpResponse httpResponse) { |
|
||||||
StringBuilder sb = new StringBuilder("Project generation failed using '"); |
|
||||||
sb.append(url).append("' - service returned ") |
|
||||||
.append(httpResponse.getStatusLine().getReasonPhrase()); |
|
||||||
String error = extractMessage(httpResponse.getEntity()); |
|
||||||
if (StringUtils.hasText(error)) { |
|
||||||
sb.append(": '").append(error).append("'"); |
|
||||||
} |
|
||||||
else { |
|
||||||
sb.append(" (unexpected ") |
|
||||||
.append(httpResponse.getStatusLine().getStatusCode()) |
|
||||||
.append(" error)"); |
|
||||||
} |
|
||||||
throw new ProjectGenerationException(sb.toString()); |
|
||||||
} |
|
||||||
|
|
||||||
private String extractMessage(HttpEntity entity) { |
|
||||||
if (entity == null) { |
|
||||||
return null; |
|
||||||
} |
|
||||||
try { |
|
||||||
JSONObject error = getContentAsJson(getContent(entity), |
|
||||||
getContentType(entity)); |
|
||||||
if (error.has("message")) { |
|
||||||
return error.getString("message"); |
|
||||||
} |
|
||||||
return null; |
|
||||||
} |
|
||||||
catch (Exception e) { |
|
||||||
return null; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
private static String extractFileName(Header h) { |
|
||||||
if (h == null) { |
|
||||||
return null; |
|
||||||
} |
|
||||||
String value = h.getValue(); |
|
||||||
String prefix = "filename=\""; |
|
||||||
int start = value.indexOf(prefix); |
|
||||||
if (start != -1) { |
|
||||||
value = value.substring(start + prefix.length(), value.length()); |
|
||||||
int end = value.indexOf("\""); |
|
||||||
if (end != -1) { |
|
||||||
return value.substring(0, end); |
|
||||||
} |
|
||||||
} |
|
||||||
return null; |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
@ -1,119 +0,0 @@ |
|||||||
/* |
|
||||||
* 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.IOException; |
|
||||||
import java.util.ArrayList; |
|
||||||
import java.util.Collections; |
|
||||||
import java.util.Comparator; |
|
||||||
import java.util.Iterator; |
|
||||||
import java.util.List; |
|
||||||
import java.util.Map; |
|
||||||
|
|
||||||
import org.apache.http.impl.client.CloseableHttpClient; |
|
||||||
import org.codehaus.plexus.util.StringUtils; |
|
||||||
|
|
||||||
/** |
|
||||||
* A helper class generating a report from the metadata of a particular service. |
|
||||||
* |
|
||||||
* @author Stephane Nicoll |
|
||||||
* @since 1.2.0 |
|
||||||
*/ |
|
||||||
class ListMetadataCommand { |
|
||||||
|
|
||||||
private static final String NEW_LINE = System.getProperty("line.separator"); |
|
||||||
|
|
||||||
private final InitializrServiceHttpInvoker initializrServiceInvoker; |
|
||||||
|
|
||||||
/** |
|
||||||
* Creates an instance using the specified {@link CloseableHttpClient}. |
|
||||||
*/ |
|
||||||
ListMetadataCommand(CloseableHttpClient httpClient) { |
|
||||||
this.initializrServiceInvoker = new InitializrServiceHttpInvoker(httpClient); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Generate a report for the specified service. The report contains the available |
|
||||||
* capabilities as advertized by the root endpoint. |
|
||||||
*/ |
|
||||||
String generateReport(String serviceUrl) throws IOException { |
|
||||||
InitializrServiceMetadata metadata = this.initializrServiceInvoker |
|
||||||
.loadMetadata(serviceUrl); |
|
||||||
String header = "Capabilities of " + serviceUrl; |
|
||||||
int size = header.length(); |
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder(); |
|
||||||
sb.append(StringUtils.repeat("=", size)).append(NEW_LINE).append(header) |
|
||||||
.append(NEW_LINE).append(StringUtils.repeat("=", size)).append(NEW_LINE) |
|
||||||
.append(NEW_LINE).append("Available dependencies:").append(NEW_LINE) |
|
||||||
.append("-----------------------").append(NEW_LINE); |
|
||||||
|
|
||||||
List<Dependency> dependencies = new ArrayList<Dependency>( |
|
||||||
metadata.getDependencies()); |
|
||||||
Collections.sort(dependencies, new Comparator<Dependency>() { |
|
||||||
@Override |
|
||||||
public int compare(Dependency o1, Dependency o2) { |
|
||||||
return o1.getId().compareTo(o2.getId()); |
|
||||||
} |
|
||||||
}); |
|
||||||
for (Dependency dependency : 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) |
|
||||||
.append("------------------------").append(NEW_LINE); |
|
||||||
List<String> typeIds = new ArrayList<String>(metadata.getProjectTypes().keySet()); |
|
||||||
Collections.sort(typeIds); |
|
||||||
for (String typeId : typeIds) { |
|
||||||
ProjectType type = metadata.getProjectTypes().get(typeId); |
|
||||||
sb.append(typeId).append(" - ").append(type.getName()); |
|
||||||
if (!type.getTags().isEmpty()) { |
|
||||||
sb.append(" ["); |
|
||||||
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()) { |
|
||||||
sb.append(" (default)"); |
|
||||||
} |
|
||||||
sb.append(NEW_LINE); |
|
||||||
} |
|
||||||
|
|
||||||
sb.append(NEW_LINE).append("Defaults:").append(NEW_LINE).append("---------") |
|
||||||
.append(NEW_LINE); |
|
||||||
|
|
||||||
List<String> defaultsKeys = new ArrayList<String>(metadata.getDefaults().keySet()); |
|
||||||
Collections.sort(defaultsKeys); |
|
||||||
for (String defaultsKey : defaultsKeys) { |
|
||||||
sb.append(defaultsKey).append(": ") |
|
||||||
.append(metadata.getDefaults().get(defaultsKey)).append(NEW_LINE); |
|
||||||
} |
|
||||||
return sb.toString(); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
@ -0,0 +1,137 @@ |
|||||||
|
/* |
||||||
|
* 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 + "'"); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,139 @@ |
|||||||
|
/* |
||||||
|
* 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.IOException; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.Collections; |
||||||
|
import java.util.Comparator; |
||||||
|
import java.util.Iterator; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
import org.apache.http.impl.client.CloseableHttpClient; |
||||||
|
import org.codehaus.plexus.util.StringUtils; |
||||||
|
|
||||||
|
/** |
||||||
|
* A helper class generating a report from the meta-data of a particular service. |
||||||
|
* |
||||||
|
* @author Stephane Nicoll |
||||||
|
* @since 1.2.0 |
||||||
|
*/ |
||||||
|
class ServiceCapabilitiesReportGenerator { |
||||||
|
|
||||||
|
private static final String NEW_LINE = System.getProperty("line.separator"); |
||||||
|
|
||||||
|
private final InitializrService initializrService; |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates an instance using the specified {@link CloseableHttpClient}. |
||||||
|
*/ |
||||||
|
ServiceCapabilitiesReportGenerator(InitializrService initializrService) { |
||||||
|
this.initializrService = initializrService; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Generate a report for the specified service. The report contains the available |
||||||
|
* capabilities as advertized by the root endpoint. |
||||||
|
*/ |
||||||
|
public String generate(String url) throws IOException { |
||||||
|
InitializrServiceMetadata metadata = this.initializrService.loadMetadata(url); |
||||||
|
String header = "Capabilities of " + url; |
||||||
|
StringBuilder report = new StringBuilder(); |
||||||
|
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(); |
||||||
|
} |
||||||
|
|
||||||
|
private void reportAvailableDependencies(InitializrServiceMetadata metadata, |
||||||
|
StringBuilder report) { |
||||||
|
report.append("Available dependencies:" + NEW_LINE); |
||||||
|
report.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); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private List<Dependency> getSortedDependencies(InitializrServiceMetadata metadata) { |
||||||
|
ArrayList<Dependency> dependencies = new ArrayList<Dependency>( |
||||||
|
metadata.getDependencies()); |
||||||
|
Collections.sort(dependencies, new Comparator<Dependency>() { |
||||||
|
@Override |
||||||
|
public int compare(Dependency o1, Dependency o2) { |
||||||
|
return o1.getId().compareTo(o2.getId()); |
||||||
|
} |
||||||
|
}); |
||||||
|
return dependencies; |
||||||
|
} |
||||||
|
|
||||||
|
private void reportAvilableProjectTypes(InitializrServiceMetadata metadata, |
||||||
|
StringBuilder report) { |
||||||
|
report.append("Available project types:" + NEW_LINE); |
||||||
|
report.append("------------------------" + NEW_LINE); |
||||||
|
List<String> typeIds = new ArrayList<String>(metadata.getProjectTypes().keySet()); |
||||||
|
Collections.sort(typeIds); |
||||||
|
for (String typeId : typeIds) { |
||||||
|
ProjectType type = metadata.getProjectTypes().get(typeId); |
||||||
|
report.append(typeId + " - " + type.getName()); |
||||||
|
if (!type.getTags().isEmpty()) { |
||||||
|
reportTags(report, type); |
||||||
|
} |
||||||
|
if (type.isDefaultType()) { |
||||||
|
report.append(" (default)"); |
||||||
|
} |
||||||
|
report.append(NEW_LINE); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void reportTags(StringBuilder report, ProjectType type) { |
||||||
|
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()); |
||||||
|
Collections.sort(defaultsKeys); |
||||||
|
for (String defaultsKey : defaultsKeys) { |
||||||
|
String defaultsValue = metadata.getDefaults().get(defaultsKey); |
||||||
|
report.append(defaultsKey + ": " + defaultsValue + NEW_LINE); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
Loading…
Reference in new issue