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.descriptor.PluginDescriptor;
67  import org.apache.maven.plugin.version.PluginVersionNotFoundException;
68  import org.apache.maven.plugin.version.PluginVersionResolutionException;
69  import org.apache.maven.project.MavenProject;
70  import org.apache.maven.rtinfo.RuntimeInformation;
71  import org.apache.maven.settings.Settings;
72  import org.codehaus.plexus.PlexusContainer;
73  import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
74  import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
75  import org.codehaus.plexus.util.StringUtils;
76  import org.eclipse.aether.RepositorySystem;
77  import org.eclipse.aether.resolution.ArtifactRequest;
78  import org.eclipse.aether.resolution.ArtifactResolutionException;
79  
80  import static java.util.Optional.ofNullable;
81  
82  /**
83   * This rule will enforce that all plugins specified in the poms have a version declared.
84   *
85   * @author <a href="mailto:brianf@apache.org">Brian Fox</a>
86   */
87  @Named("requirePluginVersions")
88  public final class RequirePluginVersions extends AbstractStandardEnforcerRule {
89  
90      /**
91       * Don't allow the LATEST identifier.
92       */
93      private boolean banLatest = true;
94  
95      /**
96       * Don't allow the RELEASE identifier.
97       */
98      private boolean banRelease = true;
99  
100     /**
101      * Don't allow snapshot plugins.
102      */
103     private boolean banSnapshots = true;
104 
105     /**
106      * Don't allow timestamp snapshot plugins.
107      */
108     private boolean banTimestamps = true;
109 
110     /**
111      * @since 3.0.0
112      */
113     private boolean banMavenDefaults = true;
114 
115     /**
116      * The comma separated list of phases that should be used to find lifecycle plugin bindings. The default value is
117      * "clean,deploy,site".
118      */
119     private String phases = "clean,deploy,site";
120 
121     /**
122      * Additional plugins to enforce have versions. These are plugins that may not be in the poms but are used anyway,
123      * like help, eclipse etc. <br>
124      * The plugins should be specified in the form: <code>group:artifactId</code>.
125      */
126     private List<String> additionalPlugins;
127 
128     /**
129      * Plugins to skip for version enforcement. The plugins should be specified in the form:
130      * <code>group:artifactId</code>. NOTE: This is deprecated, use unCheckedPluginList instead.
131      */
132     private List<String> unCheckedPlugins;
133 
134     /**
135      * Same as unCheckedPlugins but as a comma list to better support properties. Sample form:
136      * <code>group:artifactId,group2:artifactId2</code>
137      *
138      * @since 1.0-beta-1
139      */
140     private String unCheckedPluginList;
141 
142     /** The phase to lifecycle map. */
143     private Map<String, Lifecycle> phaseToLifecycleMap;
144 
145     /** The lifecycles. */
146     private Collection<Lifecycle> lifecycles;
147 
148     /** The plugin manager. */
149     private final PluginManager pluginManager;
150 
151     /** The factory. */
152     private final ArtifactFactory factory;
153 
154     private final RepositorySystem repositorySystem;
155 
156     /** The session. */
157     private final MavenSession session;
158 
159     /** The utils. */
160     private final EnforcerRuleUtils utils;
161 
162     private final RuntimeInformation runtimeInformation;
163 
164     private final DefaultLifecycles defaultLifeCycles;
165 
166     private final MavenProject project;
167 
168     private final ExpressionEvaluator evaluator;
169 
170     private final PlexusContainer container;
171 
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
367      * @param additional the additional
368      * @return the sets the
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             for (String pluginString : additional) {
374                 Plugin plugin = parsePluginString(pluginString, "AdditionalPlugins");
375 
376                 if (existing == null) {
377                     existing = new HashSet<>();
378                     existing.add(plugin);
379                 } else if (!existing.contains(plugin)) {
380                     existing.add(plugin);
381                 }
382             }
383         }
384         return existing;
385     }
386 
387     /**
388      * Helper method to parse and inject a Plugin.
389      *
390      * @param pluginString a plugin description to parse
391      * @param field        a source of pluginString
392      * @return the prepared plugin
393      */
394     private Plugin parsePluginString(String pluginString, String field) throws EnforcerRuleError {
395         if (pluginString != null) {
396             String[] pluginStrings = pluginString.split(":");
397             if (pluginStrings.length == 2) {
398                 Plugin plugin = new Plugin();
399                 plugin.setGroupId(StringUtils.strip(pluginStrings[0]));
400                 plugin.setArtifactId(StringUtils.strip(pluginStrings[1]));
401 
402                 return plugin;
403             } else {
404                 throw new EnforcerRuleError("Invalid " + field + " string: " + pluginString);
405             }
406         } else {
407             throw new EnforcerRuleError("Invalid " + field + " string: " + pluginString);
408         }
409     }
410 
411     /**
412      * Finds the plugins that are listed in active profiles.
413      *
414      * @param project the project
415      * @return the profile plugins
416      */
417     public Set<Plugin> getProfilePlugins(MavenProject project) {
418         Set<Plugin> result = new HashSet<>();
419         List<Profile> profiles = project.getActiveProfiles();
420         if (profiles != null && !profiles.isEmpty()) {
421             for (Profile p : profiles) {
422                 BuildBase b = p.getBuild();
423                 if (b != null) {
424                     List<Plugin> plugins = b.getPlugins();
425                     if (plugins != null) {
426                         result.addAll(plugins);
427                     }
428                 }
429             }
430         }
431         return result;
432     }
433 
434     /**
435      * Given a plugin, this will retrieve the matching plugin artifact from the model.
436      *
437      * @param plugin  plugin to lookup
438      * @param project project to search
439      * @return matching plugin, <code>null</code> if not found.
440      */
441     private Plugin findCurrentPlugin(Plugin plugin, MavenProject project) throws EnforcerRuleException {
442         Plugin found = null;
443         try {
444             Model model = project.getModel();
445             Map<String, Plugin> plugins = model.getBuild().getPluginsAsMap();
446             found = plugins.get(plugin.getKey());
447         } catch (NullPointerException e) {
448             // nothing to do here
449         }
450 
451         if (found == null) {
452             Artifact artifact = factory.createPluginArtifact(
453                     plugin.getGroupId(), plugin.getArtifactId(), VersionRange.createFromVersion("LATEST"));
454 
455             try {
456                 repositorySystem.resolveArtifact(
457                         session.getRepositorySession(),
458                         new ArtifactRequest(
459                                 RepositoryUtils.toArtifact(artifact),
460                                 session.getCurrentProject().getRemotePluginRepositories(),
461                                 "resolvePlugin"));
462             } catch (ArtifactResolutionException e) {
463                 throw new EnforcerRuleException("Unable to resolve the plugin " + artifact.getArtifactId(), e);
464             }
465             plugin.setVersion(artifact.getVersion());
466 
467             found = plugin;
468         }
469 
470         return found;
471     }
472 
473     /**
474      * Gets the plugins that are bound to the defined phases. This does not find plugins bound in the pom to a phase
475      * later than the plugin is executing.
476      *
477      * @param project   the project
478      * @param thePhases the phases
479      * @return the bound plugins
480      * @throws PluginNotFoundException     the plugin not found exception
481      * @throws LifecycleExecutionException the lifecycle execution exception
482      */
483     private Set<Plugin> getBoundPlugins(MavenProject project, String thePhases)
484             throws PluginNotFoundException, LifecycleExecutionException {
485 
486         Set<Plugin> allPlugins = new HashSet<>();
487 
488         // lookup the bindings for all the passed in phases
489         String[] lifecyclePhases = thePhases.split(",");
490         for (int i = 0; i < lifecyclePhases.length; i++) {
491             String lifecyclePhase = lifecyclePhases[i];
492             if (lifecyclePhase != null && !lifecyclePhase.isEmpty()) {
493                 try {
494                     Lifecycle lifecycle = getLifecycleForPhase(lifecyclePhase);
495                     getLog().debug("getBoundPlugins(): " + project.getId() + " " + lifecyclePhase + " "
496                             + lifecycle.getId());
497                     allPlugins.addAll(getAllPlugins(project, lifecycle));
498                 } catch (BuildFailureException e) {
499                     // i'm going to swallow this because the
500                     // user may have declared a phase that
501                     // doesn't exist for every module.
502                 }
503             }
504         }
505         return allPlugins;
506     }
507 
508     /**
509      * Checks for valid version specified. Checks to see if the version is specified for the plugin. Can optionally ban
510      * "RELEASE" or "LATEST" even if specified.
511      *
512      * @param source         the source
513      * @param pluginWrappers the plugins
514      * @return true, if successful
515      */
516     public boolean hasValidVersionSpecified(Plugin source, List<PluginWrapper> pluginWrappers) {
517         boolean found = false;
518         boolean status = false;
519         for (PluginWrapper plugin : pluginWrappers) {
520             // find the matching plugin entry
521             if (isMatchingPlugin(source, plugin)) {
522                 found = true;
523                 // found the entry. now see if the version is specified
524                 String version = plugin.getVersion();
525                 try {
526                     version = (String) evaluator.evaluate(version);
527                 } catch (ExpressionEvaluationException e) {
528                     return false;
529                 }
530 
531                 if (isValidVersion(version)) {
532                     getLog().debug("checking for notEmpty and notIsWhitespace(): " + version);
533                     if (banRelease && version.equals("RELEASE")) {
534                         return false;
535                     }
536 
537                     if (banLatest && version.equals("LATEST")) {
538                         return false;
539                     }
540 
541                     if (banSnapshots && isSnapshot(version)) {
542                         return false;
543                     }
544                     // the version was specified and not
545                     // banned. It's ok. Keep looking through the list to make
546                     // sure it's not using a banned version somewhere else.
547 
548                     status = true;
549 
550                     if (!banRelease && !banLatest && !banSnapshots) {
551                         // no need to keep looking
552                         break;
553                     }
554                 }
555             }
556         }
557         if (!found) {
558             getLog().debug("plugin " + source.getGroupId() + ":" + source.getArtifactId() + " not found");
559         }
560         return status;
561     }
562 
563     private boolean isValidVersion(String version) {
564         return (version != null && !version.isEmpty()) && !StringUtils.isWhitespace(version);
565     }
566 
567     private boolean isMatchingPlugin(Plugin source, PluginWrapper plugin) {
568         return source.getArtifactId().equals(plugin.getArtifactId())
569                 && source.getGroupId().equals(plugin.getGroupId());
570     }
571 
572     /**
573      * Checks if is snapshot.
574      *
575      * @param baseVersion the base version
576      * @return true, if is snapshot
577      */
578     private boolean isSnapshot(String baseVersion) {
579         if (banTimestamps) {
580             return Artifact.VERSION_FILE_PATTERN.matcher(baseVersion).matches()
581                     || baseVersion.endsWith(Artifact.SNAPSHOT_VERSION);
582         } else {
583             return baseVersion.endsWith(Artifact.SNAPSHOT_VERSION);
584         }
585     }
586 
587     /*
588      * Uses borrowed lifecycle code to get a list of all plugins bound to the lifecycle.
589      */
590 
591     /**
592      * Gets the all plugins.
593      *
594      * @param project   the project
595      * @param lifecycle the lifecycle
596      * @return the all plugins
597      * @throws PluginNotFoundException     the plugin not found exception
598      * @throws LifecycleExecutionException the lifecycle execution exception
599      */
600     private Set<Plugin> getAllPlugins(MavenProject project, Lifecycle lifecycle)
601             throws PluginNotFoundException, LifecycleExecutionException {
602 
603         getLog().debug("RequirePluginVersions.getAllPlugins:");
604 
605         Set<Plugin> plugins = new HashSet<>();
606         // first, bind those associated with the packaging
607         Map<String, String> mappings = findMappingsForLifecycle(project, lifecycle);
608 
609         for (Map.Entry<String, String> entry : mappings.entrySet()) {
610             getLog().debug("  lifecycleMapping = " + entry.getKey());
611             String pluginsForLifecycle = (String) entry.getValue();
612             getLog().debug("  plugins = " + pluginsForLifecycle);
613             if (pluginsForLifecycle != null && !pluginsForLifecycle.isEmpty()) {
614                 String pluginList[] = pluginsForLifecycle.split(",");
615                 for (String plugin : pluginList) {
616                     plugin = StringUtils.strip(plugin);
617                     getLog().debug("    plugin = " + plugin);
618                     String tokens[] = plugin.split(":");
619                     getLog().debug("    GAV = " + Arrays.asList(tokens));
620 
621                     Plugin p = new Plugin();
622                     p.setGroupId(tokens[0]);
623                     p.setArtifactId(tokens[1]);
624                     plugins.add(p);
625                 }
626             }
627         }
628 
629         plugins.addAll(project.getBuildPlugins());
630 
631         return plugins;
632     }
633 
634     /*
635      * NOTE: All the code following this point was scooped from the DefaultLifecycleExecutor. There must be a better way
636      * but for now it should work.
637      */
638 
639     /**
640      * Gets the phase to lifecycle map.
641      *
642      * @return the phase to lifecycle map
643      * @throws LifecycleExecutionException the lifecycle execution exception
644      */
645     public Map<String, Lifecycle> getPhaseToLifecycleMap() throws LifecycleExecutionException {
646         if (phaseToLifecycleMap == null) {
647             phaseToLifecycleMap = new HashMap<>();
648 
649             for (Lifecycle lifecycle : lifecycles) {
650                 List<String> phases = lifecycle.getPhases();
651                 for (String phase : phases) {
652                     getLog().debug("getPhaseToLifecycleMap(): phase: " + phase);
653                     if (phaseToLifecycleMap.containsKey(phase)) {
654                         Lifecycle prevLifecycle = (Lifecycle) phaseToLifecycleMap.get(phase);
655                         throw new LifecycleExecutionException("Phase '" + phase
656                                 + "' is defined in more than one lifecycle: '" + lifecycle.getId() + "' and '"
657                                 + prevLifecycle.getId() + "'");
658                     } else {
659                         phaseToLifecycleMap.put(phase, lifecycle);
660                     }
661                 }
662             }
663         }
664         return phaseToLifecycleMap;
665     }
666 
667     /**
668      * Gets the lifecycle for phase.
669      *
670      * @param phase the phase
671      * @return the lifecycle for phase
672      * @throws BuildFailureException       the build failure exception
673      * @throws LifecycleExecutionException the lifecycle execution exception
674      */
675     private Lifecycle getLifecycleForPhase(String phase) throws BuildFailureException, LifecycleExecutionException {
676         Lifecycle lifecycle = getPhaseToLifecycleMap().get(phase);
677 
678         if (lifecycle == null) {
679             throw new BuildFailureException("Unable to find lifecycle for phase '" + phase + "'");
680         }
681         return lifecycle;
682     }
683 
684     /**
685      * Find mappings for lifecycle.
686      *
687      * @param project   the project
688      * @param lifecycle the lifecycle
689      * @return the map
690      * @throws LifecycleExecutionException the lifecycle execution exception
691      * @throws PluginNotFoundException     the plugin not found exception
692      */
693     private Map<String, String> findMappingsForLifecycle(MavenProject project, Lifecycle lifecycle)
694             throws LifecycleExecutionException, PluginNotFoundException {
695         String packaging = project.getPackaging();
696         Map<String, String> mappings = null;
697 
698         LifecycleMapping m = (LifecycleMapping) findExtension(
699                 project, LifecycleMapping.ROLE, packaging, session.getSettings(), session.getLocalRepository());
700         if (m != null) {
701             mappings = m.getPhases(lifecycle.getId());
702         }
703 
704         Map<String, String> defaultMappings = lifecycle.getDefaultPhases();
705 
706         if (mappings == null) {
707             try {
708                 m = container.lookup(LifecycleMapping.class, packaging);
709                 mappings = m.getPhases(lifecycle.getId());
710             } catch (ComponentLookupException e) {
711                 if (defaultMappings == null) {
712                     throw new LifecycleExecutionException(
713                             "Cannot find lifecycle mapping for packaging: \'" + packaging + "\'.", e);
714                 }
715             }
716         }
717 
718         if (mappings == null) {
719             if (defaultMappings == null) {
720                 throw new LifecycleExecutionException(
721                         "Cannot find lifecycle mapping for packaging: \'" + packaging + "\', and there is no default");
722             } else {
723                 mappings = defaultMappings;
724             }
725         }
726 
727         return mappings;
728     }
729 
730     /**
731      * Find extension.
732      *
733      * @param project         the project
734      * @param role            the role
735      * @param roleHint        the role hint
736      * @param settings        the settings
737      * @param localRepository the local repository
738      * @return the object
739      * @throws LifecycleExecutionException the lifecycle execution exception
740      * @throws PluginNotFoundException     the plugin not found exception
741      */
742     private Object findExtension(
743             MavenProject project, String role, String roleHint, Settings settings, ArtifactRepository localRepository)
744             throws LifecycleExecutionException, PluginNotFoundException {
745         Object pluginComponent = null;
746 
747         List<Plugin> buildPlugins = project.getBuildPlugins();
748         for (Plugin plugin : buildPlugins) {
749             if (plugin.isExtensions()) {
750                 verifyPlugin(plugin, project, settings, localRepository);
751 
752                 // TODO: if moved to the plugin manager we
753                 // already have the descriptor from above
754                 // and so do can lookup the container
755                 // directly
756                 try {
757                     pluginComponent = pluginManager.getPluginComponent(plugin, role, roleHint);
758 
759                     if (pluginComponent != null) {
760                         break;
761                     }
762                 } catch (ComponentLookupException e) {
763                     getLog().debug("Unable to find the lifecycle component in the extension " + e.getMessage());
764                 } catch (PluginManagerException e) {
765                     throw new LifecycleExecutionException(
766                             "Error getting extensions from the plugin '" + plugin.getKey() + "': " + e.getMessage(), e);
767                 }
768             }
769         }
770         return pluginComponent;
771     }
772 
773     /**
774      * Verify plugin.
775      *
776      * @param plugin          the plugin
777      * @param project         the project
778      * @param settings        the settings
779      * @param localRepository the local repository
780      * @return the plugin descriptor
781      * @throws LifecycleExecutionException the lifecycle execution exception
782      * @throws PluginNotFoundException     the plugin not found exception
783      */
784     private PluginDescriptor verifyPlugin(
785             Plugin plugin, MavenProject project, Settings settings, ArtifactRepository localRepository)
786             throws LifecycleExecutionException, PluginNotFoundException {
787         PluginDescriptor pluginDescriptor;
788         try {
789             pluginDescriptor = pluginManager.verifyPlugin(plugin, project, settings, localRepository);
790         } catch (PluginManagerException e) {
791             throw new LifecycleExecutionException(
792                     "Internal error in the plugin manager getting plugin '" + plugin.getKey() + "': " + e.getMessage(),
793                     e);
794         } catch (PluginVersionResolutionException
795                 | InvalidVersionSpecificationException
796                 | InvalidPluginException
797                 | PluginVersionNotFoundException
798                 | org.apache.maven.artifact.resolver.ArtifactResolutionException
799                 | ArtifactNotFoundException e) {
800             throw new LifecycleExecutionException(e.getMessage(), e);
801         }
802         return pluginDescriptor;
803     }
804 
805     /**
806      * Gets all plugin entries in build.plugins, build.pluginManagement.plugins, profile.build.plugins, reporting and
807      * profile.reporting in this project and all parents
808      *
809      * @param project the project
810      * @return the all plugin entries wrapped in a PluginWrapper Object
811      */
812     private List<PluginWrapper> getAllPluginEntries(MavenProject project) {
813         List<PluginWrapper> plugins = new ArrayList<>();
814         // now find all the plugin entries, either in
815         // build.plugins or build.pluginManagement.plugins, profiles.plugins and reporting
816 
817         getPlugins(plugins, project.getModel());
818         getReportingPlugins(plugins, project.getModel());
819         getPluginManagementPlugins(plugins, project.getModel());
820         addPluginsInProfiles(plugins, project.getModel());
821 
822         return plugins;
823     }
824 
825     private void addPluginsInProfiles(List<PluginWrapper> plugins, Model model) {
826         List<Profile> profiles = ofNullable(model).map(Model::getProfiles).orElseGet(Collections::emptyList);
827         for (Profile profile : profiles) {
828             getProfilePlugins(plugins, profile);
829             getProfileReportingPlugins(plugins, profile);
830             getProfilePluginManagementPlugins(plugins, profile);
831         }
832     }
833 
834     private void getProfilePluginManagementPlugins(List<PluginWrapper> plugins, Profile profile) {
835         List<Plugin> modelPlugins = ofNullable(profile)
836                 .map(Profile::getBuild)
837                 .map(PluginConfiguration::getPluginManagement)
838                 .map(PluginContainer::getPlugins)
839                 .orElseGet(Collections::emptyList);
840         plugins.addAll(PluginWrapper.addAll(utils.resolvePlugins(modelPlugins), banMavenDefaults));
841     }
842 
843     private void getProfileReportingPlugins(List<PluginWrapper> plugins, Profile profile) {
844         List<ReportPlugin> modelReportPlugins = ofNullable(profile)
845                 .map(ModelBase::getReporting)
846                 .map(Reporting::getPlugins)
847                 .orElseGet(Collections::emptyList);
848         // add the reporting plugins
849         plugins.addAll(PluginWrapper.addAll(utils.resolveReportPlugins(modelReportPlugins), banMavenDefaults));
850     }
851 
852     private void getProfilePlugins(List<PluginWrapper> plugins, Profile profile) {
853         List<Plugin> modelPlugins = ofNullable(profile)
854                 .map(Profile::getBuild)
855                 .map(PluginContainer::getPlugins)
856                 .orElseGet(Collections::emptyList);
857         plugins.addAll(PluginWrapper.addAll(utils.resolvePlugins(modelPlugins), banMavenDefaults));
858     }
859 
860     private void getPlugins(List<PluginWrapper> plugins, Model model) {
861         List<Plugin> modelPlugins = ofNullable(model)
862                 .map(Model::getBuild)
863                 .map(PluginContainer::getPlugins)
864                 .orElseGet(Collections::emptyList);
865         plugins.addAll(PluginWrapper.addAll(utils.resolvePlugins(modelPlugins), banMavenDefaults));
866     }
867 
868     private void getPluginManagementPlugins(List<PluginWrapper> plugins, Model model) {
869         List<Plugin> modelPlugins = ofNullable(model)
870                 .map(Model::getBuild)
871                 .map(PluginConfiguration::getPluginManagement)
872                 .map(PluginContainer::getPlugins)
873                 .orElseGet(Collections::emptyList);
874         plugins.addAll(PluginWrapper.addAll(utils.resolvePlugins(modelPlugins), banMavenDefaults));
875     }
876 
877     private void getReportingPlugins(List<PluginWrapper> plugins, Model model) {
878         List<ReportPlugin> modelReportPlugins = ofNullable(model)
879                 .map(ModelBase::getReporting)
880                 .map(Reporting::getPlugins)
881                 .orElseGet(Collections::emptyList);
882         // add the reporting plugins
883         plugins.addAll(PluginWrapper.addAll(utils.resolveReportPlugins(modelReportPlugins), banMavenDefaults));
884     }
885 
886     /**
887      * Sets the ban latest.
888      *
889      * @param theBanLatest the banLatest to set
890      */
891     public void setBanLatest(boolean theBanLatest) {
892         this.banLatest = theBanLatest;
893     }
894 
895     /**
896      * Sets the ban release.
897      *
898      * @param theBanRelease the banRelease to set
899      */
900     public void setBanRelease(boolean theBanRelease) {
901         this.banRelease = theBanRelease;
902     }
903 
904     /**
905      * Checks if is ban snapshots.
906      *
907      * @return the banSnapshots
908      */
909     public boolean isBanSnapshots() {
910         return this.banSnapshots;
911     }
912 
913     /**
914      * Sets the ban snapshots.
915      *
916      * @param theBanSnapshots the banSnapshots to set
917      */
918     public void setBanSnapshots(boolean theBanSnapshots) {
919         this.banSnapshots = theBanSnapshots;
920     }
921 
922     /**
923      * Sets the ban timestamps.
924      *
925      * @param theBanTimestamps the banTimestamps to set
926      */
927     public void setBanTimestamps(boolean theBanTimestamps) {
928         this.banTimestamps = theBanTimestamps;
929     }
930 
931     @Override
932     public String toString() {
933         return String.format(
934                 "RequirePluginVersions[message=%s, banLatest=%b, banRelease=%b, banSnapshots=%b, banTimestamps=%b, phases=%s, additionalPlugins=%s, unCheckedPluginList=%s, unCheckedPlugins=%s]",
935                 getMessage(),
936                 banLatest,
937                 banRelease,
938                 banSnapshots,
939                 banTimestamps,
940                 phases,
941                 additionalPlugins,
942                 unCheckedPluginList,
943                 unCheckedPlugins);
944     }
945 }