blob: 19a811876aeeac70bfd2ffc9acb8f59b79eea18a [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.maven.plugins.help;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.apache.maven.RepositoryUtils;
import org.apache.maven.lifecycle.DefaultLifecycles;
import org.apache.maven.lifecycle.Lifecycle;
import org.apache.maven.lifecycle.internal.MojoDescriptorCreator;
import org.apache.maven.lifecycle.mapping.LifecycleMapping;
import org.apache.maven.model.Plugin;
import org.apache.maven.model.building.ModelBuildingRequest;
import org.apache.maven.plugin.MavenPluginManager;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.descriptor.MojoDescriptor;
import org.apache.maven.plugin.descriptor.Parameter;
import org.apache.maven.plugin.descriptor.PluginDescriptor;
import org.apache.maven.plugin.prefix.NoPluginFoundForPrefixException;
import org.apache.maven.plugin.version.DefaultPluginVersionRequest;
import org.apache.maven.plugin.version.PluginVersionResolutionException;
import org.apache.maven.plugin.version.PluginVersionResolver;
import org.apache.maven.plugin.version.PluginVersionResult;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.project.DefaultProjectBuildingRequest;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.ProjectBuildingRequest;
import org.apache.maven.reporting.MavenReport;
import org.apache.maven.shared.utils.logging.MessageUtils;
import org.apache.maven.tools.plugin.generator.HtmlToPlainTextConverter;
import org.codehaus.plexus.util.StringUtils;
import org.eclipse.aether.artifact.Artifact;
import org.eclipse.aether.artifact.DefaultArtifact;
/**
* Displays a list of the attributes for a Maven Plugin and/or goals (aka Mojo - Maven plain Old Java Object).
*
* @see <a href="http://maven.apache.org/general.html#What_is_a_Mojo">What is a Mojo?</a>
* @since 2.0
*/
@Mojo(name = "describe", requiresProject = false, aggregator = true)
public class DescribeMojo extends AbstractHelpMojo {
/**
* The default indent size when writing description's Mojo.
*/
private static final int INDENT_SIZE = 2;
/**
* For unknown values
*/
private static final String UNKNOWN = "Unknown";
/**
* For not defined values
*/
private static final String NOT_DEFINED = "Not defined";
/**
* For deprecated values
*/
private static final String NO_REASON = "No reason given";
private static final Pattern EXPRESSION = Pattern.compile("^\\$\\{([^}]+)\\}$");
// ----------------------------------------------------------------------
// Mojo components
// ----------------------------------------------------------------------
/**
* Component used to get a plugin descriptor from a given plugin.
*/
@Component
protected MavenPluginManager pluginManager;
/**
* Component used to get a plugin by its prefix and get mojo descriptors.
*/
@Component
private MojoDescriptorCreator mojoDescriptorCreator;
/**
* Component used to resolve the version for a plugin.
*/
@Component
private PluginVersionResolver pluginVersionResolver;
/**
* The Maven default built-in lifecycles.
*/
@Component
private DefaultLifecycles defaultLifecycles;
/**
* A map from each packaging to its lifecycle mapping.
*/
@Component
private Map<String, LifecycleMapping> lifecycleMappings;
// ----------------------------------------------------------------------
// Mojo parameters
// ----------------------------------------------------------------------
/**
* The Maven Plugin to describe. This must be specified in one of three ways:
* <br/>
* <ol>
* <li>plugin-prefix, i.e. 'help'</li>
* <li>groupId:artifactId, i.e. 'org.apache.maven.plugins:maven-help-plugin'</li>
* <li>groupId:artifactId:version, i.e. 'org.apache.maven.plugins:maven-help-plugin:2.0'</li>
* </ol>
*/
@org.apache.maven.plugins.annotations.Parameter(property = "plugin", alias = "prefix")
private String plugin;
/**
* The Maven Plugin <code>groupId</code> to describe.
* <br/>
* <b>Note</b>: Should be used with <code>artifactId</code> parameter.
*/
@org.apache.maven.plugins.annotations.Parameter(property = "groupId")
private String groupId;
/**
* The Maven Plugin <code>artifactId</code> to describe.
* <br/>
* <b>Note</b>: Should be used with <code>groupId</code> parameter.
*/
@org.apache.maven.plugins.annotations.Parameter(property = "artifactId")
private String artifactId;
/**
* The Maven Plugin <code>version</code> to describe.
* <br/>
* <b>Note</b>: Should be used with <code>groupId/artifactId</code> parameters.
*/
@org.apache.maven.plugins.annotations.Parameter(property = "version")
private String version;
/**
* The goal name of a Mojo to describe within the specified Maven Plugin.
* If this parameter is specified, only the corresponding goal (Mojo) will be described,
* rather than the whole Plugin.
*
* @since 2.1
*/
@org.apache.maven.plugins.annotations.Parameter(property = "goal")
private String goal;
/**
* This flag specifies that a detailed (verbose) list of goal (Mojo) information should be given.
*
* @since 2.1
*/
@org.apache.maven.plugins.annotations.Parameter(property = "detail", defaultValue = "false")
private boolean detail;
/**
* This flag specifies that a minimal list of goal (Mojo) information should be given.
*
* @since 2.1
*/
@org.apache.maven.plugins.annotations.Parameter(property = "minimal", defaultValue = "false")
private boolean minimal;
/**
* A Maven command like a single goal or a single phase following the Maven command line:
* <br/>
* <code>mvn [options] [&lt;goal(s)&gt;] [&lt;phase(s)&gt;]</code>
*
* @since 2.1
*/
@org.apache.maven.plugins.annotations.Parameter(property = "cmd")
private String cmd;
// ----------------------------------------------------------------------
// Public methods
// ----------------------------------------------------------------------
/**
* {@inheritDoc}
*/
public void execute() throws MojoExecutionException, MojoFailureException {
StringBuilder descriptionBuffer = new StringBuilder();
boolean describePlugin = true;
if (cmd != null && !cmd.isEmpty()) {
describePlugin = describeCommand(descriptionBuffer);
}
if (describePlugin) {
PluginInfo pi = parsePluginLookupInfo();
PluginDescriptor descriptor = lookupPluginDescriptor(pi);
if (goal != null && !goal.isEmpty()) {
MojoDescriptor mojo = descriptor.getMojo(goal);
if (mojo == null) {
throw new MojoFailureException(
"The goal '" + goal + "' does not exist in the plugin '" + pi.getPrefix() + "'");
}
describeMojo(mojo, descriptionBuffer);
} else {
describePlugin(descriptor, descriptionBuffer);
}
}
writeDescription(descriptionBuffer);
}
// ----------------------------------------------------------------------
// Private methods
// ----------------------------------------------------------------------
/**
* Method to write the Mojo description to the output file
*
* @param descriptionBuffer contains the description to be written to the file
* @throws MojoExecutionException if any
*/
private void writeDescription(StringBuilder descriptionBuffer) throws MojoExecutionException {
if (output != null) {
try {
writeFile(output, descriptionBuffer);
} catch (IOException e) {
throw new MojoExecutionException("Cannot write plugin/goal description to output: " + output, e);
}
getLog().info("Wrote descriptions to: " + output);
} else {
getLog().info(descriptionBuffer.toString());
}
}
/**
* Method for retrieving the description of the plugin
*
* @param pi holds information of the plugin whose description is to be retrieved
* @return a PluginDescriptor where the plugin description is to be retrieved
* @throws MojoExecutionException if the plugin could not be verify
* @throws MojoFailureException if groupId or artifactId is empty
*/
private PluginDescriptor lookupPluginDescriptor(PluginInfo pi) throws MojoExecutionException, MojoFailureException {
Plugin forLookup = null;
if (StringUtils.isNotEmpty(pi.getPrefix())) {
try {
forLookup = mojoDescriptorCreator.findPluginForPrefix(pi.getPrefix(), session);
} catch (NoPluginFoundForPrefixException e) {
throw new MojoExecutionException("Unable to find the plugin with prefix: " + pi.getPrefix(), e);
}
} else if (StringUtils.isNotEmpty(pi.getGroupId()) && StringUtils.isNotEmpty(pi.getArtifactId())) {
forLookup = new Plugin();
forLookup.setGroupId(pi.getGroupId());
forLookup.setArtifactId(pi.getArtifactId());
}
if (forLookup == null) {
String msg = "You must specify either: both 'groupId' and 'artifactId' parameters OR a 'plugin' parameter"
+ " OR a 'cmd' parameter. For instance:" + LS
+ " # mvn help:describe -Dcmd=install" + LS
+ "or" + LS
+ " # mvn help:describe -Dcmd=help:describe" + LS
+ "or" + LS
+ " # mvn help:describe -Dplugin=org.apache.maven.plugins:maven-help-plugin" + LS
+ "or" + LS
+ " # mvn help:describe -DgroupId=org.apache.maven.plugins -DartifactId=maven-help-plugin" + LS
+ LS
+ "Try 'mvn help:help -Ddetail=true' for more information.";
throw new MojoFailureException(msg);
}
if (StringUtils.isNotEmpty(pi.getVersion())) {
forLookup.setVersion(pi.getVersion());
} else {
try {
DefaultPluginVersionRequest versionRequest = new DefaultPluginVersionRequest(forLookup, session);
versionRequest.setPom(project.getModel());
PluginVersionResult versionResult = pluginVersionResolver.resolve(versionRequest);
forLookup.setVersion(versionResult.getVersion());
} catch (PluginVersionResolutionException e) {
throw new MojoExecutionException(
"Unable to resolve the version of the plugin with prefix: " + pi.getPrefix(), e);
}
}
try {
return pluginManager.getPluginDescriptor(
forLookup, project.getRemotePluginRepositories(), session.getRepositorySession());
} catch (Exception e) {
throw new MojoExecutionException(
"Error retrieving plugin descriptor for:" + LS + LS + "groupId: '"
+ groupId + "'" + LS + "artifactId: '" + artifactId + "'" + LS + "version: '" + version
+ "'" + LS
+ LS,
e);
}
}
/**
* Method for parsing the plugin parameter
*
* @return Plugin info containing information about the plugin whose description is to be retrieved
* @throws MojoFailureException if <code>plugin<*code> parameter is not conform to
* <code>groupId:artifactId[:version]</code>
*/
private PluginInfo parsePluginLookupInfo() throws MojoFailureException {
PluginInfo pi = new PluginInfo();
if (plugin != null && !plugin.isEmpty()) {
if (plugin.indexOf(':') > -1) {
String[] pluginParts = plugin.split(":");
switch (pluginParts.length) {
case 1:
pi.setPrefix(pluginParts[0]);
break;
case 2:
pi.setGroupId(pluginParts[0]);
pi.setArtifactId(pluginParts[1]);
break;
case 3:
pi.setGroupId(pluginParts[0]);
pi.setArtifactId(pluginParts[1]);
pi.setVersion(pluginParts[2]);
break;
default:
throw new MojoFailureException("plugin parameter must be a plugin prefix,"
+ " or conform to: 'groupId:artifactId[:version]'.");
}
} else {
pi.setPrefix(plugin);
}
} else {
pi.setGroupId(groupId);
pi.setArtifactId(artifactId);
pi.setVersion(version);
}
return pi;
}
/**
* Method for retrieving the plugin description
*
* @param pd contains the plugin description
* @param buffer contains the information to be displayed or printed
* @throws MojoFailureException if any reflection exceptions occur.
* @throws MojoExecutionException if any
*/
private void describePlugin(PluginDescriptor pd, StringBuilder buffer)
throws MojoFailureException, MojoExecutionException {
append(buffer, pd.getId(), 0);
buffer.append(LS);
String name = pd.getName();
if (name == null) {
// Can be null because of MPLUGIN-137 (and descriptors generated with maven-plugin-tools-api <= 2.4.3)
Artifact aetherArtifact = new DefaultArtifact(pd.getGroupId(), pd.getArtifactId(), "jar", pd.getVersion());
ProjectBuildingRequest pbr = new DefaultProjectBuildingRequest(session.getProjectBuildingRequest());
pbr.setRemoteRepositories(project.getRemoteArtifactRepositories());
pbr.setPluginArtifactRepositories(project.getPluginArtifactRepositories());
pbr.setProject(null);
pbr.setValidationLevel(ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL);
try {
Artifact artifactCopy = resolveArtifact(aetherArtifact).getArtifact();
name = projectBuilder
.build(RepositoryUtils.toArtifact(artifactCopy), pbr)
.getProject()
.getName();
} catch (Exception e) {
// oh well, we tried our best.
getLog().warn("Unable to get the name of the plugin " + pd.getId() + ": " + e.getMessage());
name = pd.getId();
}
}
append(buffer, "Name", MessageUtils.buffer().strong(name).toString(), 0);
appendAsParagraph(buffer, "Description", toDescription(pd.getDescription()), 0);
append(buffer, "Group Id", pd.getGroupId(), 0);
append(buffer, "Artifact Id", pd.getArtifactId(), 0);
append(buffer, "Version", pd.getVersion(), 0);
append(
buffer,
"Goal Prefix",
MessageUtils.buffer().strong(pd.getGoalPrefix()).toString(),
0);
buffer.append(LS);
List<MojoDescriptor> mojos = pd.getMojos();
if (mojos == null) {
append(buffer, "This plugin has no goals.", 0);
return;
}
if (!minimal) {
append(buffer, "This plugin has " + mojos.size() + " goal" + (mojos.size() > 1 ? "s" : "") + ":", 0);
buffer.append(LS);
mojos = mojos.stream()
.sorted((m1, m2) -> m1.getGoal().compareToIgnoreCase(m2.getGoal()))
.collect(Collectors.toList());
for (MojoDescriptor md : mojos) {
describeMojoGuts(md, buffer, detail);
buffer.append(LS);
}
}
if (!detail) {
buffer.append("For more information, run 'mvn help:describe [...] -Ddetail'");
buffer.append(LS);
}
}
/**
* Displays information about the Plugin Mojo
*
* @param md contains the description of the Plugin Mojo
* @param buffer the displayed output
* @throws MojoFailureException if any reflection exceptions occur.
* @throws MojoExecutionException if any
*/
private void describeMojo(MojoDescriptor md, StringBuilder buffer)
throws MojoFailureException, MojoExecutionException {
buffer.append("Mojo: '").append(md.getFullGoalName()).append("'");
buffer.append(LS);
describeMojoGuts(md, buffer, detail);
buffer.append(LS);
if (!detail) {
buffer.append("For more information, run 'mvn help:describe [...] -Ddetail'");
buffer.append(LS);
}
}
/**
* Displays detailed information about the Plugin Mojo
*
* @param md contains the description of the Plugin Mojo
* @param buffer contains information to be printed or displayed
* @param fullDescription specifies whether all the details about the Plugin Mojo is to be displayed
* @throws MojoFailureException if any reflection exceptions occur.
* @throws MojoExecutionException if any
*/
private void describeMojoGuts(MojoDescriptor md, StringBuilder buffer, boolean fullDescription)
throws MojoFailureException, MojoExecutionException {
append(buffer, MessageUtils.buffer().strong(md.getFullGoalName()).toString(), 0);
// indent 1
appendAsParagraph(buffer, "Description", toDescription(md.getDescription()), 1);
String deprecation = md.getDeprecated();
if (deprecation != null && deprecation.length() <= 0) {
deprecation = NO_REASON;
}
if (deprecation != null && !deprecation.isEmpty()) {
append(
buffer,
MessageUtils.buffer().warning("Deprecated. " + deprecation).toString(),
1);
}
if (isReportGoal(md)) {
append(buffer, "Note", "This goal should be used as a Maven report.", 1);
}
if (!fullDescription) {
return;
}
append(buffer, "Implementation", md.getImplementation(), 1);
append(buffer, "Language", md.getLanguage(), 1);
String phase = md.getPhase();
if (phase != null && !phase.isEmpty()) {
append(buffer, "Bound to phase", phase, 1);
}
String eGoal = md.getExecuteGoal();
String eLife = md.getExecuteLifecycle();
String ePhase = md.getExecutePhase();
if ((eGoal != null && !eGoal.isEmpty()) || (ePhase != null && !ePhase.isEmpty())) {
append(buffer, "Before this goal executes, it will call:", 1);
if (eGoal != null && !eGoal.isEmpty()) {
append(buffer, "Single goal", "'" + eGoal + "'", 2);
}
if (ePhase != null && !ePhase.isEmpty()) {
String s = "Phase: '" + ePhase + "'";
if (eLife != null && !eLife.isEmpty()) {
s += " in Lifecycle Overlay: '" + eLife + "'";
}
append(buffer, s, 2);
}
}
buffer.append(LS);
describeMojoParameters(md, buffer);
}
/**
* Displays parameter information of the Plugin Mojo
*
* @param md contains the description of the Plugin Mojo
* @param buffer contains information to be printed or displayed
* @throws MojoFailureException if any reflection exceptions occur.
* @throws MojoExecutionException if any
*/
private void describeMojoParameters(MojoDescriptor md, StringBuilder buffer)
throws MojoFailureException, MojoExecutionException {
List<Parameter> params = md.getParameters();
if (params == null || params.isEmpty()) {
append(buffer, "This mojo doesn't use any parameters.", 1);
return;
}
params = params.stream()
.sorted((p1, p2) -> p1.getName().compareToIgnoreCase(p2.getName()))
.collect(Collectors.toList());
append(buffer, "Available parameters:", 1);
// indent 2
for (Parameter parameter : params) {
if (!parameter.isEditable()) {
continue;
}
buffer.append(LS);
// DGF wouldn't it be nice if this worked?
String defaultVal = parameter.getDefaultValue();
if (defaultVal == null) {
// defaultVal is ALWAYS null, this is a bug in PluginDescriptorBuilder (cf. MNG-4941)
defaultVal =
md.getMojoConfiguration().getChild(parameter.getName()).getAttribute("default-value", null);
}
if (defaultVal != null && !defaultVal.isEmpty()) {
defaultVal = " (Default: " + MessageUtils.buffer().strong(defaultVal) + ")";
} else {
defaultVal = "";
}
append(buffer, MessageUtils.buffer().strong(parameter.getName()) + defaultVal, 2);
String alias = parameter.getAlias();
if (!(alias == null || alias.isEmpty())) {
append(buffer, "Alias", alias, 3);
}
if (parameter.isRequired()) {
append(buffer, "Required", "true", 3);
}
String expression = parameter.getExpression();
if (expression == null || expression.isEmpty()) {
// expression is ALWAYS null, this is a bug in PluginDescriptorBuilder (cf. MNG-4941).
// Fixed with Maven-3.0.1
expression =
md.getMojoConfiguration().getChild(parameter.getName()).getValue(null);
}
if (expression != null && !expression.isEmpty()) {
Matcher matcher = EXPRESSION.matcher(expression);
if (matcher.matches()) {
append(buffer, "User property", matcher.group(1), 3);
} else {
append(buffer, "Expression", expression, 3);
}
}
append(buffer, toDescription(parameter.getDescription()), 3);
String deprecation = parameter.getDeprecated();
if (deprecation != null && deprecation.length() <= 0) {
deprecation = NO_REASON;
}
if (deprecation != null && !deprecation.isEmpty()) {
append(
buffer,
MessageUtils.buffer()
.warning("Deprecated. " + deprecation)
.toString(),
3);
}
}
}
/**
* Describe the <code>cmd</code> parameter
*
* @param descriptionBuffer not null
* @return <code>true</code> if it implies to describe a plugin, <code>false</code> otherwise.
* @throws MojoExecutionException if any
*/
private boolean describeCommand(StringBuilder descriptionBuffer) throws MojoExecutionException {
if (cmd.indexOf(':') == -1) {
// phase
Lifecycle lifecycle = defaultLifecycles.getPhaseToLifecycleMap().get(cmd);
if (lifecycle == null) {
throw new MojoExecutionException("The given phase '" + cmd + "' is an unknown phase.");
}
Map<String, String> defaultLifecyclePhases = lifecycleMappings
.get(project.getPackaging())
.getLifecycles()
.get("default")
.getPhases();
List<String> phases = lifecycle.getPhases();
if (lifecycle.getDefaultPhases() == null) {
descriptionBuffer.append("'").append(cmd);
descriptionBuffer
.append("' is a phase corresponding to this plugin:")
.append(LS);
for (String key : phases) {
if (!key.equals(cmd)) {
continue;
}
if (defaultLifecyclePhases.get(key) != null) {
descriptionBuffer.append(defaultLifecyclePhases.get(key));
descriptionBuffer.append(LS);
}
}
descriptionBuffer.append(LS);
descriptionBuffer.append("It is a part of the lifecycle for the POM packaging '");
descriptionBuffer.append(project.getPackaging());
descriptionBuffer.append("'. This lifecycle includes the following phases:");
descriptionBuffer.append(LS);
for (String key : phases) {
descriptionBuffer.append("* ").append(key).append(": ");
String value = defaultLifecyclePhases.get(key);
if (value != null && !value.isEmpty()) {
for (StringTokenizer tok = new StringTokenizer(value, ","); tok.hasMoreTokens(); ) {
descriptionBuffer.append(tok.nextToken().trim());
if (!tok.hasMoreTokens()) {
descriptionBuffer.append(LS);
} else {
descriptionBuffer.append(", ");
}
}
} else {
descriptionBuffer.append(NOT_DEFINED).append(LS);
}
}
} else {
descriptionBuffer.append("'").append(cmd);
descriptionBuffer.append("' is a phase within the '").append(lifecycle.getId());
descriptionBuffer.append("' lifecycle, which has the following phases: ");
descriptionBuffer.append(LS);
for (String key : phases) {
descriptionBuffer.append("* ").append(key).append(": ");
if (lifecycle.getDefaultPhases().get(key) != null) {
descriptionBuffer
.append(lifecycle.getDefaultPhases().get(key))
.append(LS);
} else {
descriptionBuffer.append(NOT_DEFINED).append(LS);
}
}
}
return false;
}
// goals
MojoDescriptor mojoDescriptor;
try {
mojoDescriptor = mojoDescriptorCreator.getMojoDescriptor(cmd, session, project);
} catch (Exception e) {
throw new MojoExecutionException("Unable to get descriptor for " + cmd, e);
}
descriptionBuffer
.append("'")
.append(cmd)
.append("' is a plugin goal (aka mojo)")
.append(".");
descriptionBuffer.append(LS);
plugin = mojoDescriptor.getPluginDescriptor().getId();
goal = mojoDescriptor.getGoal();
return true;
}
/**
* Invoke the following private method
* <code>HelpMojo#toLines(String, int, int, int)</code>
*
* @param text The text to split into lines, must not be <code>null</code>.
* @param indent The base indentation level of each line, must not be negative.
* @param indentSize The size of each indentation, must not be negative.
* @param lineLength The length of the line, must not be negative.
* @return The sequence of display lines, never <code>null</code>.
* @throws MojoFailureException if any can not invoke the method
* @throws MojoExecutionException if no line was found for <code>text</code>
*/
private static List<String> toLines(String text, int indent, int indentSize, int lineLength)
throws MojoFailureException, MojoExecutionException {
try {
Method m =
HelpMojo.class.getDeclaredMethod("toLines", String.class, Integer.TYPE, Integer.TYPE, Integer.TYPE);
m.setAccessible(true);
@SuppressWarnings("unchecked")
List<String> output = (List<String>) m.invoke(HelpMojo.class, text, indent, indentSize, lineLength);
if (output == null) {
throw new MojoExecutionException("No output was specified.");
}
return output;
} catch (SecurityException e) {
throw new MojoFailureException("SecurityException: " + e.getMessage());
} catch (IllegalArgumentException e) {
throw new MojoFailureException("IllegalArgumentException: " + e.getMessage());
} catch (NoSuchMethodException e) {
throw new MojoFailureException("NoSuchMethodException: " + e.getMessage());
} catch (IllegalAccessException e) {
throw new MojoFailureException("IllegalAccessException: " + e.getMessage());
} catch (InvocationTargetException e) {
Throwable cause = e.getCause();
if (cause instanceof NegativeArraySizeException) {
throw new MojoFailureException("NegativeArraySizeException: " + cause.getMessage());
}
throw new MojoFailureException("InvocationTargetException: " + e.getMessage());
}
}
/**
* Append a description to the buffer by respecting the indentSize and lineLength parameters.
* <b>Note</b>: The last character is always a new line.
*
* @param sb The buffer to append the description, not <code>null</code>.
* @param description The description, not <code>null</code>.
* @param indent The base indentation level of each line, must not be negative.
* @throws MojoFailureException if any reflection exceptions occur.
* @throws MojoExecutionException if any
* @see #toLines(String, int, int, int)
*/
private static void append(StringBuilder sb, String description, int indent)
throws MojoFailureException, MojoExecutionException {
if (description == null || description.isEmpty()) {
sb.append(UNKNOWN).append(LS);
return;
}
for (String line : toLines(description, indent, INDENT_SIZE, LINE_LENGTH)) {
sb.append(line).append(LS);
}
}
/**
* Append a description to the buffer by respecting the indentSize and lineLength parameters.
* <b>Note</b>: The last character is always a new line.
*
* @param sb The buffer to append the description, not <code>null</code>.
* @param key The key, not <code>null</code>.
* @param value The value associated to the key, could be <code>null</code>.
* @param indent The base indentation level of each line, must not be negative.
* @throws MojoFailureException if any reflection exceptions occur.
* @throws MojoExecutionException if any
* @see #toLines(String, int, int, int)
*/
private static void append(StringBuilder sb, String key, String value, int indent)
throws MojoFailureException, MojoExecutionException {
if (key == null || key.isEmpty()) {
throw new IllegalArgumentException("Key is required!");
}
if (value == null || value.isEmpty()) {
value = UNKNOWN;
}
String description = key + ": " + value;
for (String line : toLines(description, indent, INDENT_SIZE, LINE_LENGTH)) {
sb.append(line).append(LS);
}
}
/**
* Append a description to the buffer by respecting the indentSize and lineLength parameters for the first line,
* and append the next lines with <code>indent + 1</code> like a paragraph.
* <b>Note</b>: The last character is always a new line.
*
* @param sb The buffer to append the description, not <code>null</code>.
* @param key The key, not <code>null</code>.
* @param value The value, could be <code>null</code>.
* @param indent The base indentation level of each line, must not be negative.
* @throws MojoFailureException if any reflection exceptions occur.
* @throws MojoExecutionException if any
* @see #toLines(String, int, int, int)
*/
private static void appendAsParagraph(StringBuilder sb, String key, String value, int indent)
throws MojoFailureException, MojoExecutionException {
if (value == null || value.isEmpty()) {
value = UNKNOWN;
}
String description;
if (key == null) {
description = value;
} else {
description = key + ": " + value;
}
List<String> l1 = toLines(description, indent, INDENT_SIZE, LINE_LENGTH - INDENT_SIZE);
List<String> l2 = toLines(description, indent + 1, INDENT_SIZE, LINE_LENGTH);
l2.set(0, l1.get(0));
for (String line : l2) {
sb.append(line).append(LS);
}
}
/**
* Determines if this Mojo should be used as a report or not. This resolves the plugin project along with all of its
* transitive dependencies to determine if the Java class of this goal implements <code>MavenReport</code>.
*
* @param md Mojo descriptor
* @return Whether or not this goal should be used as a report.
*/
private boolean isReportGoal(MojoDescriptor md) {
PluginDescriptor pd = md.getPluginDescriptor();
List<URL> urls = new ArrayList<>();
ProjectBuildingRequest pbr = new DefaultProjectBuildingRequest(session.getProjectBuildingRequest());
pbr.setRemoteRepositories(project.getRemoteArtifactRepositories());
pbr.setPluginArtifactRepositories(project.getPluginArtifactRepositories());
pbr.setResolveDependencies(true);
pbr.setProject(null);
pbr.setValidationLevel(ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL);
try {
Artifact jar = resolveArtifact(
new DefaultArtifact(pd.getGroupId(), pd.getArtifactId(), "jar", pd.getVersion()))
.getArtifact();
Artifact pom = resolveArtifact(
new DefaultArtifact(pd.getGroupId(), pd.getArtifactId(), "pom", pd.getVersion()))
.getArtifact();
MavenProject mavenProject = projectBuilder.build(pom.getFile(), pbr).getProject();
urls.add(jar.getFile().toURI().toURL());
for (String artifact : mavenProject.getCompileClasspathElements()) {
urls.add(new File(artifact).toURI().toURL());
}
try (URLClassLoader classLoader =
new URLClassLoader(urls.toArray(new URL[0]), getClass().getClassLoader())) {
return MavenReport.class.isAssignableFrom(Class.forName(md.getImplementation(), false, classLoader));
}
} catch (Exception e) {
getLog().warn("Couldn't identify if this goal is a report goal: " + e.getMessage());
return false;
}
}
/**
* Gets the effective string to use for the plugin/mojo/parameter description.
*
* @param description The description of the element, may be <code>null</code>.
* @return The effective description string, never <code>null</code>.
*/
private static String toDescription(String description) {
if (description != null && !description.isEmpty()) {
return new HtmlToPlainTextConverter().convert(description);
}
return "(no description available)";
}
/**
* Class to wrap Plugin information.
*/
static class PluginInfo {
private String prefix;
private String groupId;
private String artifactId;
private String version;
/**
* @return the prefix
*/
public String getPrefix() {
return prefix;
}
/**
* @param prefix the prefix to set
*/
public void setPrefix(String prefix) {
this.prefix = prefix;
}
/**
* @return the groupId
*/
public String getGroupId() {
return groupId;
}
/**
* @param groupId the groupId to set
*/
public void setGroupId(String groupId) {
this.groupId = groupId;
}
/**
* @return the artifactId
*/
public String getArtifactId() {
return artifactId;
}
/**
* @param artifactId the artifactId to set
*/
public void setArtifactId(String artifactId) {
this.artifactId = artifactId;
}
/**
* @return the version
*/
public String getVersion() {
return version;
}
/**
* @param version the version to set
*/
public void setVersion(String version) {
this.version = version;
}
}
}