View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.plugin.descriptor;
20  
21  import java.util.ArrayList;
22  import java.util.LinkedHashMap;
23  import java.util.List;
24  import java.util.Map;
25  import java.util.Objects;
26  
27  import org.apache.maven.plugin.Mojo;
28  import org.codehaus.plexus.component.repository.ComponentDescriptor;
29  import org.codehaus.plexus.configuration.PlexusConfiguration;
30  import org.codehaus.plexus.configuration.xml.XmlPlexusConfiguration;
31  
32  /**
33   * The bean containing the Mojo descriptor.<br>
34   * For more information about the usage tag, have a look to:
35   * <a href="https://maven.apache.org/developers/mojo-api-specification.html">
36   * https://maven.apache.org/developers/mojo-api-specification.html</a>
37   *
38   * TODO is there a need for the delegation of MavenMojoDescriptor to this?
39   * Why not just extend ComponentDescriptor here?
40   */
41  public class MojoDescriptor extends ComponentDescriptor<Mojo> implements Cloneable {
42      /** The Plexus component type */
43      public static final String MAVEN_PLUGIN = "maven-plugin";
44  
45      /** "once-per-session" execution strategy */
46      public static final String SINGLE_PASS_EXEC_STRATEGY = "once-per-session";
47  
48      /** "always" execution strategy */
49      public static final String MULTI_PASS_EXEC_STRATEGY = "always";
50  
51      private static final String DEFAULT_INSTANTIATION_STRATEGY = "per-lookup";
52  
53      private static final String DEFAULT_LANGUAGE = "java";
54  
55      private final ArrayList<Parameter> parameters;
56  
57      /** By default, the execution strategy is "once-per-session" */
58      private String executionStrategy = SINGLE_PASS_EXEC_STRATEGY;
59  
60      /**
61       * The goal name for the Mojo, that users will reference from the command line to execute the Mojo directly, or
62       * inside a POM in order to provide Mojo-specific configuration.
63       */
64      private String goal;
65  
66      /**
67       * Defines a default phase to bind a mojo execution to if the user does not explicitly set a phase in the POM.
68       * <i>Note:</i> This will not automagically make a mojo run when the plugin declaration is added to the POM. It
69       * merely enables the user to omit the <code>&lt;phase&gt;</code> element from the surrounding
70       * <code>&lt;execution&gt;</code> element.
71       */
72      private String phase;
73  
74      /** Specify the version when the Mojo was added to the API. Similar to Javadoc since. */
75      private String since;
76  
77      /** Reference the invocation phase of the Mojo. */
78      private String executePhase;
79  
80      /** Reference the invocation goal of the Mojo. */
81      private String executeGoal;
82  
83      /** Reference the invocation lifecycle of the Mojo. */
84      private String executeLifecycle;
85  
86      /**
87       * Description with reason of Mojo deprecation. Similar to Javadoc {@code @deprecated}.
88       * This will trigger a warning when a user tries to use a Mojo marked as deprecated.
89       */
90      private String deprecated;
91  
92      /**
93       * Flags this Mojo to run it in a multi-module way, i.e. aggregate the build with the set of projects listed as
94       * modules. By default, no need to aggregate the Maven project and its child modules
95       */
96      private boolean aggregator = false;
97  
98      // ----------------------------------------------------------------------
99      //
100     // ----------------------------------------------------------------------
101 
102     /** Specify the required dependencies in a specified scope */
103     private String dependencyResolutionRequired = null;
104 
105     /**
106      * The scope of (transitive) dependencies that should be collected but not resolved.
107      * @since 3.0-alpha-3
108      */
109     private String dependencyCollectionRequired;
110 
111     /**  By default, the Mojo needs a Maven project to be executed */
112     private boolean projectRequired = true;
113 
114     /**  By default, the Mojo is assumed to work offline as well */
115     private boolean onlineRequired = false;
116 
117     /**  Plugin configuration */
118     private PlexusConfiguration mojoConfiguration;
119 
120     /**  Plugin descriptor */
121     private PluginDescriptor pluginDescriptor;
122 
123     /**  By default, the Mojo is inherited */
124     private boolean inheritedByDefault = true;
125 
126     /**  By default, the Mojo cannot be invoked directly */
127     private boolean directInvocationOnly = false;
128 
129     /**  By default, the Mojo don't need reports to run */
130     private boolean requiresReports = false;
131 
132     /**
133      * By default, mojos are not threadsafe
134      * @since 3.0-beta-2
135      */
136     private boolean threadSafe = false;
137 
138     private boolean v4Api = false;
139 
140     /**
141      * Default constructor.
142      */
143     public MojoDescriptor() {
144         this.parameters = new ArrayList<>();
145         setInstantiationStrategy(DEFAULT_INSTANTIATION_STRATEGY);
146         setComponentFactory(DEFAULT_LANGUAGE);
147     }
148 
149     // ----------------------------------------------------------------------
150     //
151     // ----------------------------------------------------------------------
152 
153     /**
154      * @return the language of this Mojo, i.e. <code>java</code>
155      */
156     public String getLanguage() {
157         return getComponentFactory();
158     }
159 
160     /**
161      * @param language the new language
162      */
163     public void setLanguage(String language) {
164         setComponentFactory(language);
165     }
166 
167     /**
168      * @return Description with reason of a Mojo deprecation.
169      */
170     public String getDeprecated() {
171         return deprecated;
172     }
173 
174     /**
175      * @param deprecated Description with reason of a Mojo deprecation.
176      */
177     public void setDeprecated(String deprecated) {
178         this.deprecated = deprecated;
179     }
180 
181     /**
182      * @return the list of parameters copy. Any change to returned list is NOT reflected on this instance. To add
183      * parameters, use {@link #addParameter(Parameter)} method.
184      */
185     public List<Parameter> getParameters() {
186         return new ArrayList<>(parameters);
187     }
188 
189     /**
190      * @param parameters the new list of parameters
191      * @throws DuplicateParameterException if any
192      */
193     public void setParameters(List<Parameter> parameters) throws DuplicateParameterException {
194         this.parameters.clear();
195         for (Parameter parameter : parameters) {
196             addParameter(parameter);
197         }
198     }
199 
200     /**
201      * @param parameter add a new parameter
202      * @throws DuplicateParameterException if any
203      */
204     public void addParameter(Parameter parameter) throws DuplicateParameterException {
205         if (parameters.contains(parameter)) {
206             throw new DuplicateParameterException(parameter.getName()
207                     + " has been declared multiple times in mojo with goal: " + getGoal() + " (implementation: "
208                     + getImplementation() + ")");
209         }
210 
211         parameters.add(parameter);
212     }
213 
214     /**
215      * @return the list parameters as a Map (keyed by {@link Parameter#getName()}) that is built from
216      * {@link #parameters} list on each call. In other words, the map returned is built on fly and is a copy.
217      * Any change to this map is NOT reflected on list and other way around!
218      */
219     public Map<String, Parameter> getParameterMap() {
220         LinkedHashMap<String, Parameter> parameterMap = new LinkedHashMap<>();
221 
222         for (Parameter pd : parameters) {
223             parameterMap.put(pd.getName(), pd);
224         }
225 
226         return parameterMap;
227     }
228 
229     // ----------------------------------------------------------------------
230     // Dependency requirement
231     // ----------------------------------------------------------------------
232 
233     /**
234      * @param requiresDependencyResolution the new required dependencies in a specified scope
235      */
236     public void setDependencyResolutionRequired(String requiresDependencyResolution) {
237         this.dependencyResolutionRequired = requiresDependencyResolution;
238     }
239 
240     public String getDependencyResolutionRequired() {
241         return dependencyResolutionRequired;
242     }
243 
244     /**
245      * @return the required dependencies in a specified scope
246      * TODO the name is not intelligible
247      */
248     @Deprecated
249     public String isDependencyResolutionRequired() {
250         return dependencyResolutionRequired;
251     }
252 
253     /**
254      * @since 3.0-alpha-3
255      */
256     public void setDependencyCollectionRequired(String requiresDependencyCollection) {
257         this.dependencyCollectionRequired = requiresDependencyCollection;
258     }
259 
260     /**
261      * Gets the scope of (transitive) dependencies that should be collected. Dependency collection refers to the process
262      * of calculating the complete dependency tree in terms of artifact coordinates. In contrast to dependency
263      * resolution, this does not include the download of the files for the dependency artifacts. It is meant for mojos
264      * that only want to analyze the set of transitive dependencies, in particular during early lifecycle phases where
265      * full dependency resolution might fail due to projects which haven't been built yet.
266      *
267      * @return The scope of (transitive) dependencies that should be collected or {@code null} if none.
268      * @since 3.0-alpha-3
269      */
270     public String getDependencyCollectionRequired() {
271         return dependencyCollectionRequired;
272     }
273 
274     // ----------------------------------------------------------------------
275     // Project requirement
276     // ----------------------------------------------------------------------
277 
278     /**
279      * @param requiresProject <code>true</code> if the Mojo needs a Maven project to be executed, <code>false</code>
280      * otherwise.
281      */
282     public void setProjectRequired(boolean requiresProject) {
283         this.projectRequired = requiresProject;
284     }
285 
286     /**
287      * @return <code>true</code> if the Mojo needs a Maven project to be executed, <code>false</code> otherwise.
288      */
289     public boolean isProjectRequired() {
290         return projectRequired;
291     }
292 
293     // ----------------------------------------------------------------------
294     // Online vs. Offline requirement
295     // ----------------------------------------------------------------------
296 
297     /**
298      * @param requiresOnline <code>true</code> if the Mojo is online, <code>false</code> otherwise.
299      */
300     public void setOnlineRequired(boolean requiresOnline) {
301         this.onlineRequired = requiresOnline;
302     }
303 
304     /**
305      * @return <code>true</code> if the Mojo is online, <code>false</code> otherwise.
306      */
307     // blech! this isn't even intelligible as a method name. provided for
308     // consistency...
309     public boolean isOnlineRequired() {
310         return onlineRequired;
311     }
312 
313     /**
314      * @return <code>true</code> if the Mojo is online, <code>false</code> otherwise.
315      */
316     // more english-friendly method...keep the code clean! :)
317     public boolean requiresOnline() {
318         return onlineRequired;
319     }
320 
321     /**
322      * @return the bound phase name of the Mojo
323      */
324     public String getPhase() {
325         return phase;
326     }
327 
328     /**
329      * @param phase the new bound phase name of the Mojo
330      */
331     public void setPhase(String phase) {
332         this.phase = phase;
333     }
334 
335     /**
336      * @return the version when the Mojo was added to the API
337      */
338     public String getSince() {
339         return since;
340     }
341 
342     /**
343      * @param since the new version when the Mojo was added to the API
344      */
345     public void setSince(String since) {
346         this.since = since;
347     }
348 
349     /**
350      * @return The goal name of the Mojo
351      */
352     public String getGoal() {
353         return goal;
354     }
355 
356     /**
357      * @param goal The new goal name of the Mojo
358      */
359     public void setGoal(String goal) {
360         this.goal = goal;
361     }
362 
363     /**
364      * @return the invocation phase of the Mojo
365      */
366     public String getExecutePhase() {
367         return executePhase;
368     }
369 
370     /**
371      * @param executePhase the new invocation phase of the Mojo
372      */
373     public void setExecutePhase(String executePhase) {
374         this.executePhase = executePhase;
375     }
376 
377     /**
378      * @return <code>true</code> if the Mojo uses <code>always</code> for the <code>executionStrategy</code>
379      */
380     public boolean alwaysExecute() {
381         return MULTI_PASS_EXEC_STRATEGY.equals(executionStrategy);
382     }
383 
384     /**
385      * @return the execution strategy
386      */
387     public String getExecutionStrategy() {
388         return executionStrategy;
389     }
390 
391     /**
392      * @param executionStrategy the new execution strategy
393      */
394     public void setExecutionStrategy(String executionStrategy) {
395         this.executionStrategy = executionStrategy;
396     }
397 
398     /**
399      * @return the mojo configuration
400      */
401     public PlexusConfiguration getMojoConfiguration() {
402         if (mojoConfiguration == null) {
403             mojoConfiguration = new XmlPlexusConfiguration("configuration");
404         }
405         return mojoConfiguration;
406     }
407 
408     /**
409      * @param mojoConfiguration a new mojo configuration
410      */
411     public void setMojoConfiguration(PlexusConfiguration mojoConfiguration) {
412         this.mojoConfiguration = mojoConfiguration;
413     }
414 
415     /** {@inheritDoc} */
416     public String getRole() {
417         return isV4Api() ? "org.apache.maven.api.plugin.Mojo" : Mojo.ROLE;
418     }
419 
420     /** {@inheritDoc} */
421     public String getRoleHint() {
422         return getId();
423     }
424 
425     /**
426      * @return the id of the mojo, based on the goal name
427      */
428     public String getId() {
429         return getPluginDescriptor().getId() + ":" + getGoal();
430     }
431 
432     /**
433      * @return the full goal name
434      * @see PluginDescriptor#getGoalPrefix()
435      * @see #getGoal()
436      */
437     public String getFullGoalName() {
438         return getPluginDescriptor().getGoalPrefix() + ":" + getGoal();
439     }
440 
441     /** {@inheritDoc} */
442     public String getComponentType() {
443         return MAVEN_PLUGIN;
444     }
445 
446     /**
447      * @return the plugin descriptor
448      */
449     public PluginDescriptor getPluginDescriptor() {
450         return pluginDescriptor;
451     }
452 
453     /**
454      * @param pluginDescriptor the new plugin descriptor
455      */
456     public void setPluginDescriptor(PluginDescriptor pluginDescriptor) {
457         this.pluginDescriptor = pluginDescriptor;
458     }
459 
460     /**
461      * @return <code>true</code> if the Mojo is inherited, <code>false</code> otherwise.
462      */
463     public boolean isInheritedByDefault() {
464         return inheritedByDefault;
465     }
466 
467     /**
468      * @param inheritedByDefault <code>true</code> if the Mojo is inherited, <code>false</code> otherwise.
469      */
470     public void setInheritedByDefault(boolean inheritedByDefault) {
471         this.inheritedByDefault = inheritedByDefault;
472     }
473 
474     /** {@inheritDoc} */
475     public boolean equals(Object object) {
476         if (this == object) {
477             return true;
478         }
479 
480         if (object instanceof MojoDescriptor) {
481             MojoDescriptor other = (MojoDescriptor) object;
482 
483             return Objects.equals(getPluginDescriptor(), other.getPluginDescriptor())
484                     && Objects.equals(getGoal(), other.getGoal());
485         }
486 
487         return false;
488     }
489 
490     /** {@inheritDoc} */
491     public int hashCode() {
492         return Objects.hash(getGoal(), getPluginDescriptor());
493     }
494 
495     /**
496      * @return the invocation lifecycle of the Mojo
497      */
498     public String getExecuteLifecycle() {
499         return executeLifecycle;
500     }
501 
502     /**
503      * @param executeLifecycle the new invocation lifecycle of the Mojo
504      */
505     public void setExecuteLifecycle(String executeLifecycle) {
506         this.executeLifecycle = executeLifecycle;
507     }
508 
509     /**
510      * @param aggregator <code>true</code> if the Mojo uses the Maven project and its child modules,
511      * <code>false</code> otherwise.
512      */
513     public void setAggregator(boolean aggregator) {
514         this.aggregator = aggregator;
515     }
516 
517     /**
518      * @return <code>true</code> if the Mojo uses the Maven project and its child modules,
519      * <code>false</code> otherwise.
520      */
521     public boolean isAggregator() {
522         return aggregator;
523     }
524 
525     /**
526      * @return <code>true</code> if the Mojo cannot be invoked directly, <code>false</code> otherwise.
527      */
528     public boolean isDirectInvocationOnly() {
529         return directInvocationOnly;
530     }
531 
532     /**
533      * @param directInvocationOnly <code>true</code> if the Mojo cannot be invoked directly,
534      * <code>false</code> otherwise.
535      */
536     public void setDirectInvocationOnly(boolean directInvocationOnly) {
537         this.directInvocationOnly = directInvocationOnly;
538     }
539 
540     /**
541      * @return <code>true</code> if the Mojo needs reports to run, <code>false</code> otherwise.
542      */
543     public boolean isRequiresReports() {
544         return requiresReports;
545     }
546 
547     /**
548      * @param requiresReports <code>true</code> if the Mojo needs reports to run, <code>false</code> otherwise.
549      */
550     public void setRequiresReports(boolean requiresReports) {
551         this.requiresReports = requiresReports;
552     }
553 
554     /**
555      * @param executeGoal the new invocation goal of the Mojo
556      */
557     public void setExecuteGoal(String executeGoal) {
558         this.executeGoal = executeGoal;
559     }
560 
561     /**
562      * @return the invocation goal of the Mojo
563      */
564     public String getExecuteGoal() {
565         return executeGoal;
566     }
567 
568     /**
569      * @return True if the <code>Mojo</code> is thread-safe and can be run safely in parallel
570      * @since 3.0-beta-2
571      */
572     public boolean isThreadSafe() {
573         return threadSafe;
574     }
575 
576     /**
577      * @param threadSafe indicates that the mojo is thread-safe and can be run safely in parallel
578      * @since 3.0-beta-2
579      */
580     public void setThreadSafe(boolean threadSafe) {
581         this.threadSafe = threadSafe;
582     }
583 
584     /**
585      * @return {@code true} if this mojo forks either a goal or the lifecycle, {@code false} otherwise.
586      */
587     public boolean isForking() {
588         return (getExecuteGoal() != null && getExecuteGoal().length() > 0)
589                 || (getExecutePhase() != null && getExecutePhase().length() > 0);
590     }
591 
592     public boolean isV4Api() {
593         return v4Api;
594     }
595 
596     public void setV4Api(boolean v4Api) {
597         this.v4Api = v4Api;
598     }
599 
600     /**
601      * Creates a shallow copy of this mojo descriptor.
602      */
603     @Override
604     public MojoDescriptor clone() {
605         try {
606             return (MojoDescriptor) super.clone();
607         } catch (CloneNotSupportedException e) {
608             throw new UnsupportedOperationException(e);
609         }
610     }
611 }