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.enforcer.rules;
20  
21  import javax.inject.Inject;
22  import javax.inject.Named;
23  
24  import java.util.ArrayList;
25  import java.util.Arrays;
26  import java.util.Collection;
27  import java.util.Collections;
28  import java.util.HashMap;
29  import java.util.HashSet;
30  import java.util.List;
31  import java.util.Map;
32  import java.util.Objects;
33  import java.util.Set;
34  
35  import org.apache.maven.BuildFailureException;
36  import org.apache.maven.RepositoryUtils;
37  import org.apache.maven.artifact.Artifact;
38  import org.apache.maven.artifact.factory.ArtifactFactory;
39  import org.apache.maven.artifact.repository.ArtifactRepository;
40  import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
41  import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
42  import org.apache.maven.artifact.versioning.VersionRange;
43  import org.apache.maven.enforcer.rule.api.EnforcerRuleError;
44  import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
45  import org.apache.maven.enforcer.rules.utils.EnforcerRuleUtils;
46  import org.apache.maven.enforcer.rules.utils.ExpressionEvaluator;
47  import org.apache.maven.enforcer.rules.utils.PluginWrapper;
48  import org.apache.maven.execution.MavenSession;
49  import org.apache.maven.lifecycle.DefaultLifecycles;
50  import org.apache.maven.lifecycle.Lifecycle;
51  import org.apache.maven.lifecycle.LifecycleExecutionException;
52  import org.apache.maven.lifecycle.mapping.LifecycleMapping;
53  import org.apache.maven.model.BuildBase;
54  import org.apache.maven.model.Model;
55  import org.apache.maven.model.ModelBase;
56  import org.apache.maven.model.Plugin;
57  import org.apache.maven.model.PluginConfiguration;
58  import org.apache.maven.model.PluginContainer;
59  import org.apache.maven.model.Profile;
60  import org.apache.maven.model.ReportPlugin;
61  import org.apache.maven.model.Reporting;
62  import org.apache.maven.plugin.InvalidPluginException;
63  import org.apache.maven.plugin.PluginManager;
64  import org.apache.maven.plugin.PluginManagerException;
65  import org.apache.maven.plugin.PluginNotFoundException;
66  import org.apache.maven.plugin.version.PluginVersionNotFoundException;
67  import org.apache.maven.plugin.version.PluginVersionResolutionException;
68  import org.apache.maven.project.MavenProject;
69  import org.apache.maven.rtinfo.RuntimeInformation;
70  import org.apache.maven.settings.Settings;
71  import org.codehaus.plexus.PlexusContainer;
72  import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
73  import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
74  import org.codehaus.plexus.util.StringUtils;
75  import org.eclipse.aether.RepositorySystem;
76  import org.eclipse.aether.resolution.ArtifactRequest;
77  import org.eclipse.aether.resolution.ArtifactResolutionException;
78  
79  import static java.util.Optional.ofNullable;
80  
81  /**
82   * This rule will enforce that all plugins specified in the poms have a version declared.
83   *
84   * @author <a href="mailto:brianf@apache.org">Brian Fox</a>
85   */
86  @Named("requirePluginVersions")
87  public final class RequirePluginVersions extends AbstractStandardEnforcerRule {
88  
89      /**
90       * Don't allow the LATEST identifier.
91       */
92      private boolean banLatest = true;
93  
94      /**
95       * Don't allow the RELEASE identifier.
96       */
97      private boolean banRelease = true;
98  
99      /**
100      * Don't allow snapshot plugins.
101      */
102     private boolean banSnapshots = true;
103 
104     /**
105      * Don't allow timestamp snapshot plugins.
106      */
107     private boolean banTimestamps = true;
108 
109     /**
110      * @since 3.0.0
111      */
112     private boolean banMavenDefaults = true;
113 
114     /**
115      * The comma separated list of phases that should be used to find lifecycle plugin bindings. The default value is
116      * "clean,deploy,site".
117      */
118     private String phases = "clean,deploy,site";
119 
120     /**
121      * Additional plugins to enforce have versions. These are plugins that may not be in the poms but are used anyway,
122      * like help, eclipse etc. <br>
123      * The plugins should be specified in the form: <code>group:artifactId</code>.
124      */
125     private List<String> additionalPlugins;
126 
127     /**
128      * Plugins to skip for version enforcement. The plugins should be specified in the form:
129      * <code>group:artifactId</code>. NOTE: This is deprecated, use unCheckedPluginList instead.
130      */
131     private List<String> unCheckedPlugins;
132 
133     /**
134      * Same as unCheckedPlugins but as a comma list to better support properties. Sample form:
135      * <code>group:artifactId,group2:artifactId2</code>
136      *
137      * @since 1.0-beta-1
138      */
139     private String unCheckedPluginList;
140 
141     /** The phase to lifecycle map. */
142     private Map<String, Lifecycle> phaseToLifecycleMap;
143 
144     /** The lifecycles. */
145     private Collection<Lifecycle> lifecycles;
146 
147     /** The plugin manager. */
148     private final PluginManager pluginManager;
149 
150     /** The factory. */
151     private final ArtifactFactory factory;
152 
153     private final RepositorySystem repositorySystem;
154 
155     /** The session. */
156     private final MavenSession session;
157 
158     /** The utils. */
159     private final EnforcerRuleUtils utils;
160 
161     private final RuntimeInformation runtimeInformation;
162 
163     private final DefaultLifecycles defaultLifeCycles;
164 
165     private final MavenProject project;
166 
167     private final ExpressionEvaluator evaluator;
168 
169     private final PlexusContainer container;
170 
171     @SuppressWarnings("checkstyle:ParameterNumber")
172     @Inject
173     public RequirePluginVersions(
174             PluginManager pluginManager,
175             ArtifactFactory factory,
176             RepositorySystem repositorySystem,
177             MavenSession session,
178             EnforcerRuleUtils utils,
179             RuntimeInformation runtimeInformation,
180             DefaultLifecycles defaultLifeCycles,
181             MavenProject project,
182             ExpressionEvaluator evaluator,
183             PlexusContainer container) {
184         this.pluginManager = Objects.requireNonNull(pluginManager);
185         this.factory = Objects.requireNonNull(factory);
186         this.repositorySystem = Objects.requireNonNull(repositorySystem);
187         this.session = Objects.requireNonNull(session);
188         this.utils = Objects.requireNonNull(utils);
189         this.runtimeInformation = Objects.requireNonNull(runtimeInformation);
190         this.defaultLifeCycles = Objects.requireNonNull(defaultLifeCycles);
191         this.project = Objects.requireNonNull(project);
192         this.evaluator = Objects.requireNonNull(evaluator);
193         this.container = Objects.requireNonNull(container);
194     }
195 
196     @Override
197     public void execute() throws EnforcerRuleException {
198 
199         try {
200             // get the various expressions out of the helper.
201 
202             lifecycles = defaultLifeCycles.getLifeCycles();
203 
204             // get all the plugins that are bound to the specified lifecycles
205             Set<Plugin> allPlugins = getBoundPlugins(project, phases);
206 
207             // insert any additional plugins specified by the user.
208             allPlugins = addAdditionalPlugins(allPlugins, additionalPlugins);
209             allPlugins.addAll(getProfilePlugins(project));
210 
211             // pull out any we should skip
212             allPlugins =
213                     removeUncheckedPlugins(combineUncheckedPlugins(unCheckedPlugins, unCheckedPluginList), allPlugins);
214 
215             // there's nothing to do here
216             if (allPlugins.isEmpty()) {
217                 getLog().info("No plugin bindings found.");
218                 return;
219             } else {
220                 getLog().debug("All Plugins in use: " + allPlugins);
221             }
222 
223             // get all the plugins that are mentioned in the pom (and parents)
224             List<PluginWrapper> pluginWrappers = getAllPluginEntries(project);
225 
226             for (PluginWrapper pluginWrapper : pluginWrappers) {
227                 getLog().debug("pluginWrappers: " + pluginWrapper.getGroupId() + ":" + pluginWrapper.getArtifactId()
228                         + ":" + pluginWrapper.getVersion() + " source: " + pluginWrapper.getSource());
229             }
230             // now look for the versions that aren't valid and add to a list.
231             List<Plugin> failures = new ArrayList<>();
232 
233             for (Plugin plugin : allPlugins) {
234                 if (!hasValidVersionSpecified(plugin, pluginWrappers)) {
235                     failures.add(plugin);
236                 }
237             }
238 
239             // if anything was found, log it then append the optional message.
240             if (!failures.isEmpty()) {
241                 handleMessagesToTheUser(project, failures);
242             }
243         } catch (PluginNotFoundException | LifecycleExecutionException e) {
244             throw new EnforcerRuleException(e.getLocalizedMessage(), e);
245         }
246     }
247 
248     private void handleMessagesToTheUser(MavenProject project, List<Plugin> failures) throws EnforcerRuleException {
249         StringBuilder newMsg = new StringBuilder();
250         newMsg.append("Some plugins are missing valid versions or depend on Maven ");
251         newMsg.append(runtimeInformation.getMavenVersion());
252         newMsg.append(" defaults");
253         handleBanMessages(newMsg);
254         newMsg.append(System.lineSeparator());
255         for (Plugin plugin : failures) {
256             newMsg.append("   ");
257             newMsg.append(plugin.getGroupId());
258             newMsg.append(":");
259             newMsg.append(plugin.getArtifactId());
260 
261             try {
262                 newMsg.append(". \tThe version currently in use is ");
263 
264                 Plugin currentPlugin = findCurrentPlugin(plugin, project);
265 
266                 if (currentPlugin == null) {
267                     newMsg.append("unknown");
268                 } else {
269                     newMsg.append(currentPlugin.getVersion());
270 
271                     if (PluginWrapper.isVersionFromDefaultLifecycleBindings(currentPlugin)
272                             .orElse(false)) {
273                         newMsg.append(" via default lifecycle bindings");
274                     } else {
275                         String msg = PluginWrapper.isVersionFromSuperpom(currentPlugin)
276                                 .filter(b -> b)
277                                 .map(t -> " via super POM")
278                                 // for Maven 3.6.0 or before (MNG-6593 / MNG-6600)
279                                 .orElse(" via super POM or default lifecycle bindings");
280                         newMsg.append(msg);
281                     }
282                 }
283             } catch (Exception e) {
284                 // lots can go wrong here. Don't allow any issues trying to
285                 // determine the issue stop me
286                 getLog().debug("Exception while determining plugin Version " + e.getMessage());
287                 newMsg.append(". Unable to determine the plugin version.");
288             }
289             newMsg.append(System.lineSeparator());
290         }
291         String message = getMessage();
292         if (message != null && !message.isEmpty()) {
293             newMsg.append(message);
294         }
295 
296         throw new EnforcerRuleException(newMsg.toString());
297     }
298 
299     private void handleBanMessages(StringBuilder newMsg) {
300         if (banLatest || banRelease || banSnapshots || banTimestamps) {
301             List<String> banList = new ArrayList<>();
302             if (banLatest) {
303                 banList.add("LATEST");
304             }
305             if (banRelease) {
306                 banList.add("RELEASE");
307             }
308             if (banSnapshots) {
309                 banList.add("SNAPSHOT");
310                 if (banTimestamps) {
311                     banList.add("TIMESTAMP SNAPSHOT");
312                 }
313             }
314             if (!banList.isEmpty()) {
315                 newMsg.append(" (");
316                 newMsg.append(String.join(", ", banList));
317                 newMsg.append(" as plugin version are not allowed)");
318             }
319         }
320     }
321 
322     /**
323      * Remove the plugins that the user doesn't want to check.
324      *
325      * @param uncheckedPlugins
326      * @param plugins
327      * @return The plugins which have been removed.
328      */
329     Set<Plugin> removeUncheckedPlugins(Collection<String> uncheckedPlugins, Set<Plugin> plugins)
330             throws EnforcerRuleError {
331         if (uncheckedPlugins != null && !uncheckedPlugins.isEmpty()) {
332             for (String pluginKey : uncheckedPlugins) {
333                 Plugin plugin = parsePluginString(pluginKey, "UncheckedPlugins");
334                 plugins.remove(plugin);
335             }
336         }
337         return plugins;
338     }
339 
340     /**
341      * Combines the old Collection with the new comma separated list.
342      *
343      * @param uncheckedPlugins     a new collections
344      * @param uncheckedPluginsList a list to merge
345      * @return List of unchecked plugins.
346      */
347     public Collection<String> combineUncheckedPlugins(
348             Collection<String> uncheckedPlugins, String uncheckedPluginsList) {
349         // if the comma list is empty, then there's nothing to do here.
350         if (uncheckedPluginsList != null && !uncheckedPluginsList.isEmpty()) {
351             // make sure there is a collection to add to.
352             if (uncheckedPlugins == null) {
353                 uncheckedPlugins = new HashSet<>();
354             } else if (!uncheckedPlugins.isEmpty()) {
355                 getLog().warn("The parameter 'unCheckedPlugins' is deprecated. Use 'unCheckedPluginList' instead");
356             }
357 
358             uncheckedPlugins.addAll(Arrays.asList(uncheckedPluginsList.split(",")));
359         }
360         return uncheckedPlugins;
361     }
362 
363     /**
364      * Add the additional plugins if they don't exist yet.
365      *
366      * @param existing   the existing plugins
367      * @param additional the additional plugins
368      * @return the additional and existing plugins
369      * @throws EnforcerRuleError the enforcer error
370      */
371     public Set<Plugin> addAdditionalPlugins(Set<Plugin> existing, List<String> additional) throws EnforcerRuleError {
372         if (additional != null) {
373             if (existing == null) {
374                 existing = new HashSet<>();
375             }
376             for (String pluginString : additional) {
377                 Plugin plugin = parsePluginString(pluginString, "AdditionalPlugins");
378                 existing.add(plugin);
379             }
380         }
381         return existing;
382     }
383 
384     /**
385      * Helper method to parse and inject a Plugin.
386      *
387      * @param pluginString a plugin description to parse
388      * @param field        a source of pluginString
389      * @return the prepared plugin
390      */
391     private Plugin parsePluginString(String pluginString, String field) throws EnforcerRuleError {
392         if (pluginString != null) {
393             String[] pluginStrings = pluginString.split(":");
394             if (pluginStrings.length == 2) {
395                 Plugin plugin = new Plugin();
396                 plugin.setGroupId(StringUtils.strip(pluginStrings[0]));
397                 plugin.setArtifactId(StringUtils.strip(pluginStrings[1]));
398 
399                 return plugin;
400             } else {
401                 throw new EnforcerRuleError("Invalid " + field + " string: " + pluginString);
402             }
403         } else {
404             throw new EnforcerRuleError("Invalid " + field + " null plugin string.");
405         }
406     }
407 
408     /**
409      * Finds the plugins that are listed in active profiles.
410      *
411      * @param project the project
412      * @return the profile plugins
413      */
414     public Set<Plugin> getProfilePlugins(MavenProject project) {
415         Set<Plugin> result = new HashSet<>();
416         List<Profile> profiles = project.getActiveProfiles();
417         if (profiles != null && !profiles.isEmpty()) {
418             for (Profile p : profiles) {
419                 BuildBase b = p.getBuild();
420                 if (b != null) {
421                     List<Plugin> plugins = b.getPlugins();
422                     if (plugins != null) {
423                         result.addAll(plugins);
424                     }
425                 }
426             }
427         }
428         return result;
429     }
430 
431     /**
432      * Given a plugin, this will retrieve the matching plugin artifact from the model.
433      *
434      * @param plugin  plugin to lookup
435      * @param project project to search
436      * @return matching plugin, <code>null</code> if not found.
437      */
438     private Plugin findCurrentPlugin(Plugin plugin, MavenProject project) throws EnforcerRuleException {
439         Plugin found = null;
440         try {
441             Model model = project.getModel();
442             Map<String, Plugin> plugins = model.getBuild().getPluginsAsMap();
443             found = plugins.get(plugin.getKey());
444         } catch (NullPointerException e) {
445             // nothing to do here
446         }
447 
448         if (found == null) {
449             Artifact artifact = factory.createPluginArtifact(
450                     plugin.getGroupId(), plugin.getArtifactId(), VersionRange.createFromVersion("LATEST"));
451 
452             try {
453                 repositorySystem.resolveArtifact(
454                         session.getRepositorySession(),
455                         new ArtifactRequest(
456                                 RepositoryUtils.toArtifact(artifact),
457                                 session.getCurrentProject().getRemotePluginRepositories(),
458                                 "resolvePlugin"));
459             } catch (ArtifactResolutionException e) {
460                 throw new EnforcerRuleException("Unable to resolve the plugin " + artifact.getArtifactId(), e);
461             }
462             plugin.setVersion(artifact.getVersion());
463 
464             found = plugin;
465         }
466 
467         return found;
468     }
469 
470     /**
471      * Gets the plugins that are bound to the defined phases. This does not find plugins bound in the pom to a phase
472      * later than the plugin is executing.
473      *
474      * @param project   the project
475      * @param phases the phases
476      * @return the bound plugins
477      * @throws PluginNotFoundException     the plugin not found exception
478      * @throws LifecycleExecutionException the lifecycle execution exception
479      */
480     private Set<Plugin> getBoundPlugins(MavenProject project, String phases)
481             throws PluginNotFoundException, LifecycleExecutionException {
482 
483         Set<Plugin> allPlugins = new HashSet<>();
484 
485         // lookup the bindings for all the passed in phases
486         String[] lifecyclePhases = phases.split(",");
487         for (int i = 0; i < lifecyclePhases.length; i++) {
488             String lifecyclePhase = lifecyclePhases[i];
489             if (lifecyclePhase != null && !lifecyclePhase.isEmpty()) {
490                 try {
491                     Lifecycle lifecycle = getLifecycleForPhase(lifecyclePhase);
492                     getLog().debug("getBoundPlugins(): " + project.getId() + " " + lifecyclePhase + " "
493                             + lifecycle.getId());
494                     allPlugins.addAll(getAllPlugins(project, lifecycle));
495                 } catch (BuildFailureException e) {
496                     // swallow this because the
497                     // user may have declared a phase that
498                     // doesn't exist for every module.
499                 }
500             }
501         }
502         return allPlugins;
503     }
504 
505     /**
506      * Checks for valid version specified. Checks to see if the version is specified for the plugin. Can optionally ban
507      * "RELEASE" or "LATEST" even if specified.
508      *
509      * @param source         the source
510      * @param pluginWrappers the plugins
511      * @return true, if successful
512      */
513     public boolean hasValidVersionSpecified(Plugin source, List<PluginWrapper> pluginWrappers) {
514         boolean found = false;
515         boolean status = false;
516         for (PluginWrapper plugin : pluginWrappers) {
517             // find the matching plugin entry
518             if (isMatchingPlugin(source, plugin)) {
519                 found = true;
520                 // found the entry. now see if the version is specified
521                 String version = plugin.getVersion();
522                 try {
523                     version = (String) evaluator.evaluate(version);
524                 } catch (ExpressionEvaluationException e) {
525                     return false;
526                 }
527 
528                 if (isValidVersion(version)) {
529                     getLog().debug("checking for notEmpty and notIsWhitespace(): " + version);
530                     if (banRelease && version.equals("RELEASE")) {
531                         return false;
532                     }
533 
534                     if (banLatest && version.equals("LATEST")) {
535                         return false;
536                     }
537 
538                     if (banSnapshots && isSnapshot(version)) {
539                         return false;
540                     }
541                     // the version was specified and not
542                     // banned. It's ok. Keep looking through the list to make
543                     // sure it's not using a banned version somewhere else.
544 
545                     status = true;
546 
547                     if (!banRelease && !banLatest && !banSnapshots) {
548                         // no need to keep looking
549                         break;
550                     }
551                 }
552             }
553         }
554         if (!found) {
555             getLog().debug("plugin " + source.getGroupId() + ":" + source.getArtifactId() + " not found");
556         }
557         return status;
558     }
559 
560     private boolean isValidVersion(String version) {
561         return (version != null && !version.isEmpty()) && !StringUtils.isWhitespace(version);
562     }
563 
564     private boolean isMatchingPlugin(Plugin source, PluginWrapper plugin) {
565         return source.getArtifactId().equals(plugin.getArtifactId())
566                 && source.getGroupId().equals(plugin.getGroupId());
567     }
568 
569     /**
570      * Checks if is snapshot.
571      *
572      * @param baseVersion the base version
573      * @return true, if is snapshot
574      */
575     private boolean isSnapshot(String baseVersion) {
576         if (banTimestamps) {
577             return Artifact.VERSION_FILE_PATTERN.matcher(baseVersion).matches()
578                     || baseVersion.endsWith(Artifact.SNAPSHOT_VERSION);
579         } else {
580             return baseVersion.endsWith(Artifact.SNAPSHOT_VERSION);
581         }
582     }
583 
584     /*
585      * Uses borrowed lifecycle code to get a list of all plugins bound to the lifecycle.
586      */
587 
588     /**
589      * Gets the all plugins.
590      *
591      * @param project   the project
592      * @param lifecycle the lifecycle
593      * @return the all plugins
594      * @throws PluginNotFoundException     the plugin not found exception
595      * @throws LifecycleExecutionException the lifecycle execution exception
596      */
597     private Set<Plugin> getAllPlugins(MavenProject project, Lifecycle lifecycle)
598             throws PluginNotFoundException, LifecycleExecutionException {
599 
600         getLog().debug("RequirePluginVersions.getAllPlugins:");
601 
602         Set<Plugin> plugins = new HashSet<>();
603         // first, bind those associated with the packaging
604         Map<String, String> mappings = findMappingsForLifecycle(project, lifecycle);
605 
606         for (Map.Entry<String, String> entry : mappings.entrySet()) {
607             getLog().debug("  lifecycleMapping = " + entry.getKey());
608             String pluginsForLifecycle = entry.getValue();
609             getLog().debug("  plugins = " + pluginsForLifecycle);
610             if (pluginsForLifecycle != null && !pluginsForLifecycle.isEmpty()) {
611                 String pluginList[] = pluginsForLifecycle.split(",");
612                 for (String plugin : pluginList) {
613                     plugin = StringUtils.strip(plugin);
614                     getLog().debug("    plugin = " + plugin);
615                     String tokens[] = plugin.split(":");
616                     getLog().debug("    GAV = " + Arrays.asList(tokens));
617 
618                     Plugin p = new Plugin();
619                     p.setGroupId(tokens[0]);
620                     p.setArtifactId(tokens[1]);
621                     plugins.add(p);
622                 }
623             }
624         }
625 
626         plugins.addAll(project.getBuildPlugins());
627 
628         return plugins;
629     }
630 
631     /*
632      * NOTE: All the code following this point was scooped from the DefaultLifecycleExecutor. There must be a better way
633      * but for now it should work.
634      */
635 
636     /**
637      * Gets the phase to lifecycle map.
638      *
639      * @return the phase to lifecycle map
640      * @throws LifecycleExecutionException the lifecycle execution exception
641      */
642     public Map<String, Lifecycle> getPhaseToLifecycleMap() throws LifecycleExecutionException {
643         if (phaseToLifecycleMap == null) {
644             phaseToLifecycleMap = new HashMap<>();
645 
646             for (Lifecycle lifecycle : lifecycles) {
647                 List<String> phases = lifecycle.getPhases();
648                 for (String phase : phases) {
649                     getLog().debug("getPhaseToLifecycleMap(): phase: " + phase);
650                     if (phaseToLifecycleMap.containsKey(phase)) {
651                         Lifecycle prevLifecycle = phaseToLifecycleMap.get(phase);
652                         throw new LifecycleExecutionException("Phase '" + phase
653                                 + "' is defined in more than one lifecycle: '" + lifecycle.getId() + "' and '"
654                                 + prevLifecycle.getId() + "'");
655                     } else {
656                         phaseToLifecycleMap.put(phase, lifecycle);
657                     }
658                 }
659             }
660         }
661         return phaseToLifecycleMap;
662     }
663 
664     /**
665      * Gets the lifecycle for phase.
666      *
667      * @param phase the phase
668      * @return the lifecycle for phase
669      * @throws BuildFailureException       the build failure exception
670      * @throws LifecycleExecutionException the lifecycle execution exception
671      */
672     private Lifecycle getLifecycleForPhase(String phase) throws BuildFailureException, LifecycleExecutionException {
673         Lifecycle lifecycle = getPhaseToLifecycleMap().get(phase);
674 
675         if (lifecycle == null) {
676             throw new BuildFailureException("Unable to find lifecycle for phase '" + phase + "'");
677         }
678         return lifecycle;
679     }
680 
681     /**
682      * Find mappings for lifecycle.
683      *
684      * @param project   the project
685      * @param lifecycle the lifecycle
686      * @return the map
687      * @throws LifecycleExecutionException the lifecycle execution exception
688      * @throws PluginNotFoundException     the plugin not found exception
689      */
690     private Map<String, String> findMappingsForLifecycle(MavenProject project, Lifecycle lifecycle)
691             throws LifecycleExecutionException, PluginNotFoundException {
692         String packaging = project.getPackaging();
693         Map<String, String> mappings = null;
694 
695         LifecycleMapping m = (LifecycleMapping) findExtension(
696                 project, LifecycleMapping.ROLE, packaging, session.getSettings(), session.getLocalRepository());
697         if (m != null) {
698             mappings = m.getPhases(lifecycle.getId());
699         }
700 
701         Map<String, String> defaultMappings = lifecycle.getDefaultPhases();
702 
703         if (mappings == null) {
704             try {
705                 m = container.lookup(LifecycleMapping.class, packaging);
706                 mappings = m.getPhases(lifecycle.getId());
707             } catch (ComponentLookupException e) {
708                 if (defaultMappings == null) {
709                     throw new LifecycleExecutionException(
710                             "Cannot find lifecycle mapping for packaging: '" + packaging + "'.", e);
711                 }
712             }
713         }
714 
715         if (mappings == null) {
716             if (defaultMappings == null) {
717                 throw new LifecycleExecutionException(
718                         "Cannot find lifecycle mapping for packaging: '" + packaging + "', and there is no default");
719             } else {
720                 mappings = defaultMappings;
721             }
722         }
723 
724         return mappings;
725     }
726 
727     /**
728      * Find extension.
729      *
730      * @param project         the project
731      * @param role            the role
732      * @param roleHint        the role hint
733      * @param settings        the settings
734      * @param localRepository the local repository
735      * @return the object
736      * @throws LifecycleExecutionException the lifecycle execution exception
737      * @throws PluginNotFoundException     the plugin not found exception
738      */
739     private Object findExtension(
740             MavenProject project, String role, String roleHint, Settings settings, ArtifactRepository localRepository)
741             throws LifecycleExecutionException, PluginNotFoundException {
742         Object pluginComponent = null;
743 
744         List<Plugin> buildPlugins = project.getBuildPlugins();
745         for (Plugin plugin : buildPlugins) {
746             if (plugin.isExtensions()) {
747                 verifyPlugin(plugin, project, settings, localRepository);
748 
749                 // TODO: if moved to the plugin manager we
750                 // already have the descriptor from above
751                 // and so do can lookup the container
752                 // directly
753                 try {
754                     pluginComponent = pluginManager.getPluginComponent(plugin, role, roleHint);
755 
756                     if (pluginComponent != null) {
757                         break;
758                     }
759                 } catch (ComponentLookupException e) {
760                     getLog().debug("Unable to find the lifecycle component in the extension " + e.getMessage());
761                 } catch (PluginManagerException e) {
762                     throw new LifecycleExecutionException(
763                             "Error getting extensions from the plugin '" + plugin.getKey() + "': " + e.getMessage(), e);
764                 }
765             }
766         }
767         return pluginComponent;
768     }
769 
770     /**
771      * Verify plugin.
772      *
773      * @param plugin          the plugin
774      * @param project         the project
775      * @param settings        the settings
776      * @param localRepository the local repository
777      * @return the plugin descriptor
778      * @throws LifecycleExecutionException the lifecycle execution exception
779      * @throws PluginNotFoundException     the plugin not found exception
780      */
781     private void verifyPlugin(
782             Plugin plugin, MavenProject project, Settings settings, ArtifactRepository localRepository)
783             throws LifecycleExecutionException, PluginNotFoundException {
784         try {
785             pluginManager.verifyPlugin(plugin, project, settings, localRepository);
786         } catch (PluginManagerException e) {
787             throw new LifecycleExecutionException(
788                     "Internal error in the plugin manager getting plugin '" + plugin.getKey() + "': " + e.getMessage(),
789                     e);
790         } catch (PluginVersionResolutionException
791                 | InvalidVersionSpecificationException
792                 | InvalidPluginException
793                 | PluginVersionNotFoundException
794                 | org.apache.maven.artifact.resolver.ArtifactResolutionException
795                 | ArtifactNotFoundException e) {
796             throw new LifecycleExecutionException(e.getMessage(), e);
797         }
798     }
799 
800     /**
801      * Gets all plugin entries in build.plugins, build.pluginManagement.plugins, profile.build.plugins, reporting and
802      * profile.reporting in this project and all parents
803      *
804      * @param project the project
805      * @return the all plugin entries wrapped in a PluginWrapper Object
806      */
807     private List<PluginWrapper> getAllPluginEntries(MavenProject project) {
808         List<PluginWrapper> plugins = new ArrayList<>();
809         // now find all the plugin entries, either in
810         // build.plugins or build.pluginManagement.plugins, profiles.plugins and reporting
811 
812         getPlugins(plugins, project.getModel());
813         getReportingPlugins(plugins, project.getModel());
814         getPluginManagementPlugins(plugins, project.getModel());
815         addPluginsInProfiles(plugins, project.getModel());
816 
817         return plugins;
818     }
819 
820     private void addPluginsInProfiles(List<PluginWrapper> plugins, Model model) {
821         List<Profile> profiles = ofNullable(model).map(Model::getProfiles).orElseGet(Collections::emptyList);
822         for (Profile profile : profiles) {
823             getProfilePlugins(plugins, profile);
824             getProfileReportingPlugins(plugins, profile);
825             getProfilePluginManagementPlugins(plugins, profile);
826         }
827     }
828 
829     private void getProfilePluginManagementPlugins(List<PluginWrapper> plugins, Profile profile) {
830         List<Plugin> modelPlugins = ofNullable(profile)
831                 .map(Profile::getBuild)
832                 .map(PluginConfiguration::getPluginManagement)
833                 .map(PluginContainer::getPlugins)
834                 .orElseGet(Collections::emptyList);
835         plugins.addAll(PluginWrapper.addAll(utils.resolvePlugins(modelPlugins), banMavenDefaults));
836     }
837 
838     private void getProfileReportingPlugins(List<PluginWrapper> plugins, Profile profile) {
839         List<ReportPlugin> modelReportPlugins = ofNullable(profile)
840                 .map(ModelBase::getReporting)
841                 .map(Reporting::getPlugins)
842                 .orElseGet(Collections::emptyList);
843         // add the reporting plugins
844         plugins.addAll(PluginWrapper.addAll(utils.resolveReportPlugins(modelReportPlugins), banMavenDefaults));
845     }
846 
847     private void getProfilePlugins(List<PluginWrapper> plugins, Profile profile) {
848         List<Plugin> modelPlugins = ofNullable(profile)
849                 .map(Profile::getBuild)
850                 .map(PluginContainer::getPlugins)
851                 .orElseGet(Collections::emptyList);
852         plugins.addAll(PluginWrapper.addAll(utils.resolvePlugins(modelPlugins), banMavenDefaults));
853     }
854 
855     private void getPlugins(List<PluginWrapper> plugins, Model model) {
856         List<Plugin> modelPlugins = ofNullable(model)
857                 .map(Model::getBuild)
858                 .map(PluginContainer::getPlugins)
859                 .orElseGet(Collections::emptyList);
860         plugins.addAll(PluginWrapper.addAll(utils.resolvePlugins(modelPlugins), banMavenDefaults));
861     }
862 
863     private void getPluginManagementPlugins(List<PluginWrapper> plugins, Model model) {
864         List<Plugin> modelPlugins = ofNullable(model)
865                 .map(Model::getBuild)
866                 .map(PluginConfiguration::getPluginManagement)
867                 .map(PluginContainer::getPlugins)
868                 .orElseGet(Collections::emptyList);
869         plugins.addAll(PluginWrapper.addAll(utils.resolvePlugins(modelPlugins), banMavenDefaults));
870     }
871 
872     private void getReportingPlugins(List<PluginWrapper> plugins, Model model) {
873         List<ReportPlugin> modelReportPlugins = ofNullable(model)
874                 .map(ModelBase::getReporting)
875                 .map(Reporting::getPlugins)
876                 .orElseGet(Collections::emptyList);
877         // add the reporting plugins
878         plugins.addAll(PluginWrapper.addAll(utils.resolveReportPlugins(modelReportPlugins), banMavenDefaults));
879     }
880 
881     /**
882      * Sets the ban latest.
883      *
884      * @param banLatest the banLatest to set
885      */
886     public void setBanLatest(boolean banLatest) {
887         this.banLatest = banLatest;
888     }
889 
890     /**
891      * Sets the ban release.
892      *
893      * @param banRelease the banRelease to set
894      */
895     public void setBanRelease(boolean banRelease) {
896         this.banRelease = banRelease;
897     }
898 
899     /**
900      * Checks if is ban snapshots.
901      *
902      * @return the banSnapshots
903      */
904     public boolean isBanSnapshots() {
905         return this.banSnapshots;
906     }
907 
908     /**
909      * Sets the ban snapshots.
910      *
911      * @param banSnapshots the banSnapshots to set
912      */
913     public void setBanSnapshots(boolean banSnapshots) {
914         this.banSnapshots = banSnapshots;
915     }
916 
917     /**
918      * Sets the ban timestamps.
919      *
920      * @param banTimestamps the banTimestamps to set
921      */
922     public void setBanTimestamps(boolean banTimestamps) {
923         this.banTimestamps = banTimestamps;
924     }
925 
926     @Override
927     public String toString() {
928         return String.format(
929                 "RequirePluginVersions[message=%s, banLatest=%b, banRelease=%b, banSnapshots=%b, banTimestamps=%b, phases=%s, additionalPlugins=%s, unCheckedPluginList=%s, unCheckedPlugins=%s]",
930                 getMessage(),
931                 banLatest,
932                 banRelease,
933                 banSnapshots,
934                 banTimestamps,
935                 phases,
936                 additionalPlugins,
937                 unCheckedPluginList,
938                 unCheckedPlugins);
939     }
940 }