blob: 4c067effac3425f8fbfd668855c8c24c20de61b9 [file] [log] [blame]
/*
* 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.plugin.descriptor;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import org.apache.maven.plugin.Mojo;
import org.codehaus.plexus.component.repository.ComponentDescriptor;
import org.codehaus.plexus.configuration.PlexusConfiguration;
import org.codehaus.plexus.configuration.xml.XmlPlexusConfiguration;
/**
* The bean containing the Mojo descriptor.<br>
* For more information about the usage tag, have a look to:
* <a href="https://maven.apache.org/developers/mojo-api-specification.html">
* https://maven.apache.org/developers/mojo-api-specification.html</a>
*
* TODO is there a need for the delegation of MavenMojoDescriptor to this?
* Why not just extend ComponentDescriptor here?
*/
public class MojoDescriptor extends ComponentDescriptor<Mojo> implements Cloneable {
/** The Plexus component type */
public static final String MAVEN_PLUGIN = "maven-plugin";
/** "once-per-session" execution strategy */
public static final String SINGLE_PASS_EXEC_STRATEGY = "once-per-session";
/** "always" execution strategy */
public static final String MULTI_PASS_EXEC_STRATEGY = "always";
private static final String DEFAULT_INSTANTIATION_STRATEGY = "per-lookup";
private static final String DEFAULT_LANGUAGE = "java";
private final ArrayList<Parameter> parameters;
/** By default, the execution strategy is "once-per-session" */
private String executionStrategy = SINGLE_PASS_EXEC_STRATEGY;
/**
* The goal name for the Mojo, that users will reference from the command line to execute the Mojo directly, or
* inside a POM in order to provide Mojo-specific configuration.
*/
private String goal;
/**
* Defines a default phase to bind a mojo execution to if the user does not explicitly set a phase in the POM.
* <i>Note:</i> This will not automagically make a mojo run when the plugin declaration is added to the POM. It
* merely enables the user to omit the <code>&lt;phase&gt;</code> element from the surrounding
* <code>&lt;execution&gt;</code> element.
*/
private String phase;
/** Specify the version when the Mojo was added to the API. Similar to Javadoc since. */
private String since;
/** Reference the invocation phase of the Mojo. */
private String executePhase;
/** Reference the invocation goal of the Mojo. */
private String executeGoal;
/** Reference the invocation lifecycle of the Mojo. */
private String executeLifecycle;
/**
* Description with reason of Mojo deprecation. Similar to Javadoc {@code @deprecated}.
* This will trigger a warning when a user tries to use a Mojo marked as deprecated.
*/
private String deprecated;
/**
* Flags this Mojo to run it in a multi-module way, i.e. aggregate the build with the set of projects listed as
* modules. By default, no need to aggregate the Maven project and its child modules
*/
private boolean aggregator = false;
// ----------------------------------------------------------------------
//
// ----------------------------------------------------------------------
/** Specify the required dependencies in a specified scope */
private String dependencyResolutionRequired = null;
/**
* The scope of (transitive) dependencies that should be collected but not resolved.
* @since 3.0-alpha-3
*/
private String dependencyCollectionRequired;
/** By default, the Mojo needs a Maven project to be executed */
private boolean projectRequired = true;
/** By default, the Mojo is assumed to work offline as well */
private boolean onlineRequired = false;
/** Plugin configuration */
private PlexusConfiguration mojoConfiguration;
/** Plugin descriptor */
private PluginDescriptor pluginDescriptor;
/** By default, the Mojo is inherited */
private boolean inheritedByDefault = true;
/** By default, the Mojo cannot be invoked directly */
private boolean directInvocationOnly = false;
/** By default, the Mojo don't need reports to run */
private boolean requiresReports = false;
/**
* By default, mojos are not threadsafe
* @since 3.0-beta-2
*/
private boolean threadSafe = false;
private boolean v4Api = false;
/**
* Default constructor.
*/
public MojoDescriptor() {
this.parameters = new ArrayList<>();
setInstantiationStrategy(DEFAULT_INSTANTIATION_STRATEGY);
setComponentFactory(DEFAULT_LANGUAGE);
}
public MojoDescriptor(PluginDescriptor pd, org.apache.maven.api.plugin.descriptor.MojoDescriptor md) {
this();
this.setPluginDescriptor(pd);
this.setGoal(md.getGoal());
this.setExecuteGoal(md.getExecuteGoal());
this.setExecuteLifecycle(md.getExecuteLifecycle());
this.setExecutePhase(md.getExecutePhase());
this.setDeprecated(md.getDeprecated());
this.setLanguage(md.getLanguage());
this.setAggregator(md.isAggregator());
this.setDependencyCollectionRequired(md.getDependencyCollection());
this.setDependencyResolutionRequired(md.getDependencyResolution());
this.setComponentConfigurator(md.getConfigurator());
this.setInheritedByDefault(md.isInheritedByDefault());
this.setPhase(md.getPhase());
this.setOnlineRequired(md.isOnlineRequired());
this.setProjectRequired(md.isProjectRequired());
this.setSince(md.getSince());
this.setThreadSafe(true);
this.setV4Api(true);
this.setImplementation(md.getImplementation());
try {
this.setParameters(md.getParameters().stream().map(Parameter::new).collect(Collectors.toList()));
} catch (DuplicateParameterException e) {
throw new IllegalArgumentException(e);
}
this.mojoDescriptorV4 = md;
}
// ----------------------------------------------------------------------
//
// ----------------------------------------------------------------------
/**
* @return the language of this Mojo, i.e. <code>java</code>
*/
public String getLanguage() {
return getComponentFactory();
}
/**
* @param language the new language
*/
public void setLanguage(String language) {
setComponentFactory(language);
}
/**
* @return Description with reason of a Mojo deprecation.
*/
public String getDeprecated() {
return deprecated;
}
/**
* @param deprecated Description with reason of a Mojo deprecation.
*/
public void setDeprecated(String deprecated) {
this.deprecated = deprecated;
}
/**
* @return the list of parameters copy. Any change to returned list is NOT reflected on this instance. To add
* parameters, use {@link #addParameter(Parameter)} method.
*/
public List<Parameter> getParameters() {
return new ArrayList<>(parameters);
}
/**
* @param parameters the new list of parameters
* @throws DuplicateParameterException if any
*/
public void setParameters(List<Parameter> parameters) throws DuplicateParameterException {
this.parameters.clear();
for (Parameter parameter : parameters) {
addParameter(parameter);
}
}
/**
* @param parameter add a new parameter
* @throws DuplicateParameterException if any
*/
public void addParameter(Parameter parameter) throws DuplicateParameterException {
if (parameters.contains(parameter)) {
throw new DuplicateParameterException(parameter.getName()
+ " has been declared multiple times in mojo with goal: " + getGoal() + " (implementation: "
+ getImplementation() + ")");
}
parameters.add(parameter);
}
/**
* @return the list parameters as a Map (keyed by {@link Parameter#getName()}) that is built from
* {@link #parameters} list on each call. In other words, the map returned is built on fly and is a copy.
* Any change to this map is NOT reflected on list and other way around!
*/
public Map<String, Parameter> getParameterMap() {
LinkedHashMap<String, Parameter> parameterMap = new LinkedHashMap<>();
for (Parameter pd : parameters) {
parameterMap.put(pd.getName(), pd);
}
return parameterMap;
}
// ----------------------------------------------------------------------
// Dependency requirement
// ----------------------------------------------------------------------
/**
* @param requiresDependencyResolution the new required dependencies in a specified scope
*/
public void setDependencyResolutionRequired(String requiresDependencyResolution) {
this.dependencyResolutionRequired = requiresDependencyResolution;
}
public String getDependencyResolutionRequired() {
return dependencyResolutionRequired;
}
/**
* @return the required dependencies in a specified scope
* TODO the name is not intelligible
*/
@Deprecated
public String isDependencyResolutionRequired() {
return dependencyResolutionRequired;
}
/**
* @since 3.0-alpha-3
*/
public void setDependencyCollectionRequired(String requiresDependencyCollection) {
this.dependencyCollectionRequired = requiresDependencyCollection;
}
/**
* Gets the scope of (transitive) dependencies that should be collected. Dependency collection refers to the process
* of calculating the complete dependency tree in terms of artifact coordinates. In contrast to dependency
* resolution, this does not include the download of the files for the dependency artifacts. It is meant for mojos
* that only want to analyze the set of transitive dependencies, in particular during early lifecycle phases where
* full dependency resolution might fail due to projects which haven't been built yet.
*
* @return The scope of (transitive) dependencies that should be collected or {@code null} if none.
* @since 3.0-alpha-3
*/
public String getDependencyCollectionRequired() {
return dependencyCollectionRequired;
}
// ----------------------------------------------------------------------
// Project requirement
// ----------------------------------------------------------------------
/**
* @param requiresProject <code>true</code> if the Mojo needs a Maven project to be executed, <code>false</code>
* otherwise.
*/
public void setProjectRequired(boolean requiresProject) {
this.projectRequired = requiresProject;
}
/**
* @return <code>true</code> if the Mojo needs a Maven project to be executed, <code>false</code> otherwise.
*/
public boolean isProjectRequired() {
return projectRequired;
}
// ----------------------------------------------------------------------
// Online vs. Offline requirement
// ----------------------------------------------------------------------
/**
* @param requiresOnline <code>true</code> if the Mojo is online, <code>false</code> otherwise.
*/
public void setOnlineRequired(boolean requiresOnline) {
this.onlineRequired = requiresOnline;
}
/**
* @return <code>true</code> if the Mojo is online, <code>false</code> otherwise.
*/
// blech! this isn't even intelligible as a method name. provided for
// consistency...
public boolean isOnlineRequired() {
return onlineRequired;
}
/**
* @return <code>true</code> if the Mojo is online, <code>false</code> otherwise.
*/
// more english-friendly method...keep the code clean! :)
public boolean requiresOnline() {
return onlineRequired;
}
/**
* @return the bound phase name of the Mojo
*/
public String getPhase() {
return phase;
}
/**
* @param phase the new bound phase name of the Mojo
*/
public void setPhase(String phase) {
this.phase = phase;
}
/**
* @return the version when the Mojo was added to the API
*/
public String getSince() {
return since;
}
/**
* @param since the new version when the Mojo was added to the API
*/
public void setSince(String since) {
this.since = since;
}
/**
* @return The goal name of the Mojo
*/
public String getGoal() {
return goal;
}
/**
* @param goal The new goal name of the Mojo
*/
public void setGoal(String goal) {
this.goal = goal;
}
/**
* @return the invocation phase of the Mojo
*/
public String getExecutePhase() {
return executePhase;
}
/**
* @param executePhase the new invocation phase of the Mojo
*/
public void setExecutePhase(String executePhase) {
this.executePhase = executePhase;
}
/**
* @return <code>true</code> if the Mojo uses <code>always</code> for the <code>executionStrategy</code>
*/
public boolean alwaysExecute() {
return MULTI_PASS_EXEC_STRATEGY.equals(executionStrategy);
}
/**
* @return the execution strategy
*/
public String getExecutionStrategy() {
return executionStrategy;
}
/**
* @param executionStrategy the new execution strategy
*/
public void setExecutionStrategy(String executionStrategy) {
this.executionStrategy = executionStrategy;
}
/**
* @return the mojo configuration
*/
public PlexusConfiguration getMojoConfiguration() {
if (mojoConfiguration == null) {
mojoConfiguration = new XmlPlexusConfiguration("configuration");
}
return mojoConfiguration;
}
/**
* @param mojoConfiguration a new mojo configuration
*/
public void setMojoConfiguration(PlexusConfiguration mojoConfiguration) {
this.mojoConfiguration = mojoConfiguration;
}
/** {@inheritDoc} */
public String getRole() {
return isV4Api() ? "org.apache.maven.api.plugin.Mojo" : Mojo.ROLE;
}
/** {@inheritDoc} */
public String getRoleHint() {
return getId();
}
/**
* @return the id of the mojo, based on the goal name
*/
public String getId() {
return getPluginDescriptor().getId() + ":" + getGoal();
}
/**
* @return the full goal name
* @see PluginDescriptor#getGoalPrefix()
* @see #getGoal()
*/
public String getFullGoalName() {
return getPluginDescriptor().getGoalPrefix() + ":" + getGoal();
}
/** {@inheritDoc} */
public String getComponentType() {
return MAVEN_PLUGIN;
}
/**
* @return the plugin descriptor
*/
public PluginDescriptor getPluginDescriptor() {
return pluginDescriptor;
}
/**
* @param pluginDescriptor the new plugin descriptor
*/
public void setPluginDescriptor(PluginDescriptor pluginDescriptor) {
this.pluginDescriptor = pluginDescriptor;
}
/**
* @return <code>true</code> if the Mojo is inherited, <code>false</code> otherwise.
*/
public boolean isInheritedByDefault() {
return inheritedByDefault;
}
/**
* @param inheritedByDefault <code>true</code> if the Mojo is inherited, <code>false</code> otherwise.
*/
public void setInheritedByDefault(boolean inheritedByDefault) {
this.inheritedByDefault = inheritedByDefault;
}
/** {@inheritDoc} */
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (object instanceof MojoDescriptor) {
MojoDescriptor other = (MojoDescriptor) object;
return Objects.equals(getPluginDescriptor(), other.getPluginDescriptor())
&& Objects.equals(getGoal(), other.getGoal());
}
return false;
}
/** {@inheritDoc} */
public int hashCode() {
return Objects.hash(getGoal(), getPluginDescriptor());
}
/**
* @return the invocation lifecycle of the Mojo
*/
public String getExecuteLifecycle() {
return executeLifecycle;
}
/**
* @param executeLifecycle the new invocation lifecycle of the Mojo
*/
public void setExecuteLifecycle(String executeLifecycle) {
this.executeLifecycle = executeLifecycle;
}
/**
* @param aggregator <code>true</code> if the Mojo uses the Maven project and its child modules,
* <code>false</code> otherwise.
*/
public void setAggregator(boolean aggregator) {
this.aggregator = aggregator;
}
/**
* @return <code>true</code> if the Mojo uses the Maven project and its child modules,
* <code>false</code> otherwise.
*/
public boolean isAggregator() {
return aggregator;
}
/**
* @return <code>true</code> if the Mojo cannot be invoked directly, <code>false</code> otherwise.
*/
public boolean isDirectInvocationOnly() {
return directInvocationOnly;
}
/**
* @param directInvocationOnly <code>true</code> if the Mojo cannot be invoked directly,
* <code>false</code> otherwise.
*/
public void setDirectInvocationOnly(boolean directInvocationOnly) {
this.directInvocationOnly = directInvocationOnly;
}
/**
* @return <code>true</code> if the Mojo needs reports to run, <code>false</code> otherwise.
*/
public boolean isRequiresReports() {
return requiresReports;
}
/**
* @param requiresReports <code>true</code> if the Mojo needs reports to run, <code>false</code> otherwise.
*/
public void setRequiresReports(boolean requiresReports) {
this.requiresReports = requiresReports;
}
/**
* @param executeGoal the new invocation goal of the Mojo
*/
public void setExecuteGoal(String executeGoal) {
this.executeGoal = executeGoal;
}
/**
* @return the invocation goal of the Mojo
*/
public String getExecuteGoal() {
return executeGoal;
}
/**
* @return True if the <code>Mojo</code> is thread-safe and can be run safely in parallel
* @since 3.0-beta-2
*/
public boolean isThreadSafe() {
return threadSafe;
}
/**
* @param threadSafe indicates that the mojo is thread-safe and can be run safely in parallel
* @since 3.0-beta-2
*/
public void setThreadSafe(boolean threadSafe) {
this.threadSafe = threadSafe;
}
/**
* @return {@code true} if this mojo forks either a goal or the lifecycle, {@code false} otherwise.
*/
public boolean isForking() {
return (getExecuteGoal() != null && !getExecuteGoal().isEmpty())
|| (getExecutePhase() != null && !getExecutePhase().isEmpty());
}
public boolean isV4Api() {
return v4Api;
}
public void setV4Api(boolean v4Api) {
this.v4Api = v4Api;
}
/**
* Creates a shallow copy of this mojo descriptor.
*/
@Override
public MojoDescriptor clone() {
try {
return (MojoDescriptor) super.clone();
} catch (CloneNotSupportedException e) {
throw new UnsupportedOperationException(e);
}
}
private volatile org.apache.maven.api.plugin.descriptor.MojoDescriptor mojoDescriptorV4;
public org.apache.maven.api.plugin.descriptor.MojoDescriptor getMojoDescriptorV4() {
if (mojoDescriptorV4 == null) {
synchronized (this) {
if (mojoDescriptorV4 == null) {
mojoDescriptorV4 = org.apache.maven.api.plugin.descriptor.MojoDescriptor.newBuilder()
.goal(goal)
.description(getDescription())
.implementation(getImplementation())
.language(getLanguage())
.phase(phase)
.executeGoal(executeGoal)
.executeLifecycle(executeLifecycle)
.executePhase(executePhase)
.aggregator(aggregator)
.dependencyResolution(dependencyResolutionRequired)
.dependencyCollection(dependencyCollectionRequired)
.projectRequired(projectRequired)
.onlineRequired(onlineRequired)
.inheritedByDefault(inheritedByDefault)
.since(since)
.deprecated(deprecated)
.configurator(getComponentConfigurator())
.parameters(getParameters().stream()
.filter(p -> p.getRequirement() == null)
.map(Parameter::getParameterV4)
.collect(Collectors.toList()))
.id(getId())
.fullGoalName(getFullGoalName())
.build();
}
}
}
return mojoDescriptorV4;
}
}