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