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