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><phase></code> element from the surrounding
69 * <code><execution></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 }