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 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 }