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><phase></code> element from the surrounding
70 * <code><execution></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 /**
139 * Default constructor.
140 */
141 public MojoDescriptor() {
142 this.parameters = new ArrayList<>();
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 copy. Any change to returned list is NOT reflected on this instance. To add
181 * parameters, use {@link #addParameter(Parameter)} method.
182 */
183 public List<Parameter> getParameters() {
184 return new ArrayList<>(parameters);
185 }
186
187 /**
188 * @param parameters the new list of parameters
189 * @throws DuplicateParameterException if any
190 */
191 public void setParameters(List<Parameter> parameters) throws DuplicateParameterException {
192 this.parameters.clear();
193 for (Parameter parameter : parameters) {
194 addParameter(parameter);
195 }
196 }
197
198 /**
199 * @param parameter add a new parameter
200 * @throws DuplicateParameterException if any
201 */
202 public void addParameter(Parameter parameter) throws DuplicateParameterException {
203 if (parameters.contains(parameter)) {
204 throw new DuplicateParameterException(parameter.getName()
205 + " has been declared multiple times in mojo with goal: " + getGoal() + " (implementation: "
206 + getImplementation() + ")");
207 }
208
209 parameters.add(parameter);
210 }
211
212 /**
213 * @return the list parameters as a Map (keyed by {@link Parameter#getName()}) that is built from
214 * {@link #parameters} list on each call. In other words, the map returned is built on fly and is a copy.
215 * Any change to this map is NOT reflected on list and other way around!
216 */
217 public Map<String, Parameter> getParameterMap() {
218 LinkedHashMap<String, Parameter> parameterMap = new LinkedHashMap<>();
219
220 for (Parameter pd : parameters) {
221 parameterMap.put(pd.getName(), pd);
222 }
223
224 return parameterMap;
225 }
226
227 // ----------------------------------------------------------------------
228 // Dependency requirement
229 // ----------------------------------------------------------------------
230
231 /**
232 * @param requiresDependencyResolution the new required dependencies in a specified scope
233 */
234 public void setDependencyResolutionRequired(String requiresDependencyResolution) {
235 this.dependencyResolutionRequired = requiresDependencyResolution;
236 }
237
238 public String getDependencyResolutionRequired() {
239 return dependencyResolutionRequired;
240 }
241
242 /**
243 * @return the required dependencies in a specified scope
244 * TODO the name is not intelligible
245 */
246 @Deprecated
247 public String isDependencyResolutionRequired() {
248 return dependencyResolutionRequired;
249 }
250
251 /**
252 * @since 3.0-alpha-3
253 */
254 public void setDependencyCollectionRequired(String requiresDependencyCollection) {
255 this.dependencyCollectionRequired = requiresDependencyCollection;
256 }
257
258 /**
259 * Gets the scope of (transitive) dependencies that should be collected. Dependency collection refers to the process
260 * of calculating the complete dependency tree in terms of artifact coordinates. In contrast to dependency
261 * resolution, this does not include the download of the files for the dependency artifacts. It is meant for mojos
262 * that only want to analyze the set of transitive dependencies, in particular during early lifecycle phases where
263 * full dependency resolution might fail due to projects which haven't been built yet.
264 *
265 * @return The scope of (transitive) dependencies that should be collected or {@code null} if none.
266 * @since 3.0-alpha-3
267 */
268 public String getDependencyCollectionRequired() {
269 return dependencyCollectionRequired;
270 }
271
272 // ----------------------------------------------------------------------
273 // Project requirement
274 // ----------------------------------------------------------------------
275
276 /**
277 * @param requiresProject <code>true</code> if the Mojo needs a Maven project to be executed, <code>false</code>
278 * otherwise.
279 */
280 public void setProjectRequired(boolean requiresProject) {
281 this.projectRequired = requiresProject;
282 }
283
284 /**
285 * @return <code>true</code> if the Mojo needs a Maven project to be executed, <code>false</code> otherwise.
286 */
287 public boolean isProjectRequired() {
288 return projectRequired;
289 }
290
291 // ----------------------------------------------------------------------
292 // Online vs. Offline requirement
293 // ----------------------------------------------------------------------
294
295 /**
296 * @param requiresOnline <code>true</code> if the Mojo is online, <code>false</code> otherwise.
297 */
298 public void setOnlineRequired(boolean requiresOnline) {
299 this.onlineRequired = requiresOnline;
300 }
301
302 /**
303 * @return <code>true</code> if the Mojo is online, <code>false</code> otherwise.
304 */
305 // blech! this isn't even intelligible as a method name. provided for
306 // consistency...
307 public boolean isOnlineRequired() {
308 return onlineRequired;
309 }
310
311 /**
312 * @return <code>true</code> if the Mojo is online, <code>false</code> otherwise.
313 */
314 // more english-friendly method...keep the code clean! :)
315 public boolean requiresOnline() {
316 return onlineRequired;
317 }
318
319 /**
320 * @return the bound phase name of the Mojo
321 */
322 public String getPhase() {
323 return phase;
324 }
325
326 /**
327 * @param phase the new bound phase name of the Mojo
328 */
329 public void setPhase(String phase) {
330 this.phase = phase;
331 }
332
333 /**
334 * @return the version when the Mojo was added to the API
335 */
336 public String getSince() {
337 return since;
338 }
339
340 /**
341 * @param since the new version when the Mojo was added to the API
342 */
343 public void setSince(String since) {
344 this.since = since;
345 }
346
347 /**
348 * @return The goal name of the Mojo
349 */
350 public String getGoal() {
351 return goal;
352 }
353
354 /**
355 * @param goal The new goal name of the Mojo
356 */
357 public void setGoal(String goal) {
358 this.goal = goal;
359 }
360
361 /**
362 * @return the invocation phase of the Mojo
363 */
364 public String getExecutePhase() {
365 return executePhase;
366 }
367
368 /**
369 * @param executePhase the new invocation phase of the Mojo
370 */
371 public void setExecutePhase(String executePhase) {
372 this.executePhase = executePhase;
373 }
374
375 /**
376 * @return <code>true</code> if the Mojo uses <code>always</code> for the <code>executionStrategy</code>
377 */
378 public boolean alwaysExecute() {
379 return MULTI_PASS_EXEC_STRATEGY.equals(executionStrategy);
380 }
381
382 /**
383 * @return the execution strategy
384 */
385 public String getExecutionStrategy() {
386 return executionStrategy;
387 }
388
389 /**
390 * @param executionStrategy the new execution strategy
391 */
392 public void setExecutionStrategy(String executionStrategy) {
393 this.executionStrategy = executionStrategy;
394 }
395
396 /**
397 * @return the mojo configuration
398 */
399 public PlexusConfiguration getMojoConfiguration() {
400 if (mojoConfiguration == null) {
401 mojoConfiguration = new XmlPlexusConfiguration("configuration");
402 }
403 return mojoConfiguration;
404 }
405
406 /**
407 * @param mojoConfiguration a new mojo configuration
408 */
409 public void setMojoConfiguration(PlexusConfiguration mojoConfiguration) {
410 this.mojoConfiguration = mojoConfiguration;
411 }
412
413 /** {@inheritDoc} */
414 public String getRole() {
415 return Mojo.ROLE;
416 }
417
418 /** {@inheritDoc} */
419 public String getRoleHint() {
420 return getId();
421 }
422
423 /**
424 * @return the id of the mojo, based on the goal name
425 */
426 public String getId() {
427 return getPluginDescriptor().getId() + ":" + getGoal();
428 }
429
430 /**
431 * @return the full goal name
432 * @see PluginDescriptor#getGoalPrefix()
433 * @see #getGoal()
434 */
435 public String getFullGoalName() {
436 return getPluginDescriptor().getGoalPrefix() + ":" + getGoal();
437 }
438
439 /** {@inheritDoc} */
440 public String getComponentType() {
441 return MAVEN_PLUGIN;
442 }
443
444 /**
445 * @return the plugin descriptor
446 */
447 public PluginDescriptor getPluginDescriptor() {
448 return pluginDescriptor;
449 }
450
451 /**
452 * @param pluginDescriptor the new plugin descriptor
453 */
454 public void setPluginDescriptor(PluginDescriptor pluginDescriptor) {
455 this.pluginDescriptor = pluginDescriptor;
456 }
457
458 /**
459 * @return <code>true</code> if the Mojo is inherited, <code>false</code> otherwise.
460 */
461 public boolean isInheritedByDefault() {
462 return inheritedByDefault;
463 }
464
465 /**
466 * @param inheritedByDefault <code>true</code> if the Mojo is inherited, <code>false</code> otherwise.
467 */
468 public void setInheritedByDefault(boolean inheritedByDefault) {
469 this.inheritedByDefault = inheritedByDefault;
470 }
471
472 /** {@inheritDoc} */
473 public boolean equals(Object object) {
474 if (this == object) {
475 return true;
476 }
477
478 if (object instanceof MojoDescriptor) {
479 MojoDescriptor other = (MojoDescriptor) object;
480
481 return Objects.equals(getPluginDescriptor(), other.getPluginDescriptor())
482 && Objects.equals(getGoal(), other.getGoal());
483 }
484
485 return false;
486 }
487
488 /** {@inheritDoc} */
489 public int hashCode() {
490 return Objects.hash(getGoal(), getPluginDescriptor());
491 }
492
493 /**
494 * @return the invocation lifecycle of the Mojo
495 */
496 public String getExecuteLifecycle() {
497 return executeLifecycle;
498 }
499
500 /**
501 * @param executeLifecycle the new invocation lifecycle of the Mojo
502 */
503 public void setExecuteLifecycle(String executeLifecycle) {
504 this.executeLifecycle = executeLifecycle;
505 }
506
507 /**
508 * @param aggregator <code>true</code> if the Mojo uses the Maven project and its child modules,
509 * <code>false</code> otherwise.
510 */
511 public void setAggregator(boolean aggregator) {
512 this.aggregator = aggregator;
513 }
514
515 /**
516 * @return <code>true</code> if the Mojo uses the Maven project and its child modules,
517 * <code>false</code> otherwise.
518 */
519 public boolean isAggregator() {
520 return aggregator;
521 }
522
523 /**
524 * @return <code>true</code> if the Mojo cannot be invoked directly, <code>false</code> otherwise.
525 */
526 public boolean isDirectInvocationOnly() {
527 return directInvocationOnly;
528 }
529
530 /**
531 * @param directInvocationOnly <code>true</code> if the Mojo cannot be invoked directly,
532 * <code>false</code> otherwise.
533 */
534 public void setDirectInvocationOnly(boolean directInvocationOnly) {
535 this.directInvocationOnly = directInvocationOnly;
536 }
537
538 /**
539 * @return <code>true</code> if the Mojo needs reports to run, <code>false</code> otherwise.
540 */
541 public boolean isRequiresReports() {
542 return requiresReports;
543 }
544
545 /**
546 * @param requiresReports <code>true</code> if the Mojo needs reports to run, <code>false</code> otherwise.
547 */
548 public void setRequiresReports(boolean requiresReports) {
549 this.requiresReports = requiresReports;
550 }
551
552 /**
553 * @param executeGoal the new invocation goal of the Mojo
554 */
555 public void setExecuteGoal(String executeGoal) {
556 this.executeGoal = executeGoal;
557 }
558
559 /**
560 * @return the invocation goal of the Mojo
561 */
562 public String getExecuteGoal() {
563 return executeGoal;
564 }
565
566 /**
567 * @return True if the <code>Mojo</code> is thread-safe and can be run safely in parallel
568 * @since 3.0-beta-2
569 */
570 public boolean isThreadSafe() {
571 return threadSafe;
572 }
573
574 /**
575 * @param threadSafe indicates that the mojo is thread-safe and can be run safely in parallel
576 * @since 3.0-beta-2
577 */
578 public void setThreadSafe(boolean threadSafe) {
579 this.threadSafe = threadSafe;
580 }
581
582 /**
583 * @return {@code true} if this mojo forks either a goal or the lifecycle, {@code false} otherwise.
584 */
585 public boolean isForking() {
586 return (getExecuteGoal() != null && getExecuteGoal().length() > 0)
587 || (getExecutePhase() != null && getExecutePhase().length() > 0);
588 }
589
590 /**
591 * Creates a shallow copy of this mojo descriptor.
592 */
593 @Override
594 public MojoDescriptor clone() {
595 try {
596 return (MojoDescriptor) super.clone();
597 } catch (CloneNotSupportedException e) {
598 throw new UnsupportedOperationException(e);
599 }
600 }
601 }