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
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 = 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 = 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 void verifyPlugin(
785             Plugin plugin, MavenProject project, Settings settings, ArtifactRepository localRepository)
786             throws LifecycleExecutionException, PluginNotFoundException {
787         try {
788             pluginManager.verifyPlugin(plugin, project, settings, localRepository);
789         } catch (PluginManagerException e) {
790             throw new LifecycleExecutionException(
791                     "Internal error in the plugin manager getting plugin '" + plugin.getKey() + "': " + e.getMessage(),
792                     e);
793         } catch (PluginVersionResolutionException
794                 | InvalidVersionSpecificationException
795                 | InvalidPluginException
796                 | PluginVersionNotFoundException
797                 | org.apache.maven.artifact.resolver.ArtifactResolutionException
798                 | ArtifactNotFoundException e) {
799             throw new LifecycleExecutionException(e.getMessage(), e);
800         }
801     }
802 
803     /**
804      * Gets all plugin entries in build.plugins, build.pluginManagement.plugins, profile.build.plugins, reporting and
805      * profile.reporting in this project and all parents
806      *
807      * @param project the project
808      * @return the all plugin entries wrapped in a PluginWrapper Object
809      */
810     private List<PluginWrapper> getAllPluginEntries(MavenProject project) {
811         List<PluginWrapper> plugins = new ArrayList<>();
812         // now find all the plugin entries, either in
813         // build.plugins or build.pluginManagement.plugins, profiles.plugins and reporting
814 
815         getPlugins(plugins, project.getModel());
816         getReportingPlugins(plugins, project.getModel());
817         getPluginManagementPlugins(plugins, project.getModel());
818         addPluginsInProfiles(plugins, project.getModel());
819 
820         return plugins;
821     }
822 
823     private void addPluginsInProfiles(List<PluginWrapper> plugins, Model model) {
824         List<Profile> profiles = ofNullable(model).map(Model::getProfiles).orElseGet(Collections::emptyList);
825         for (Profile profile : profiles) {
826             getProfilePlugins(plugins, profile);
827             getProfileReportingPlugins(plugins, profile);
828             getProfilePluginManagementPlugins(plugins, profile);
829         }
830     }
831 
832     private void getProfilePluginManagementPlugins(List<PluginWrapper> plugins, Profile profile) {
833         List<Plugin> modelPlugins = ofNullable(profile)
834                 .map(Profile::getBuild)
835                 .map(PluginConfiguration::getPluginManagement)
836                 .map(PluginContainer::getPlugins)
837                 .orElseGet(Collections::emptyList);
838         plugins.addAll(PluginWrapper.addAll(utils.resolvePlugins(modelPlugins), banMavenDefaults));
839     }
840 
841     private void getProfileReportingPlugins(List<PluginWrapper> plugins, Profile profile) {
842         List<ReportPlugin> modelReportPlugins = ofNullable(profile)
843                 .map(ModelBase::getReporting)
844                 .map(Reporting::getPlugins)
845                 .orElseGet(Collections::emptyList);
846         // add the reporting plugins
847         plugins.addAll(PluginWrapper.addAll(utils.resolveReportPlugins(modelReportPlugins), banMavenDefaults));
848     }
849 
850     private void getProfilePlugins(List<PluginWrapper> plugins, Profile profile) {
851         List<Plugin> modelPlugins = ofNullable(profile)
852                 .map(Profile::getBuild)
853                 .map(PluginContainer::getPlugins)
854                 .orElseGet(Collections::emptyList);
855         plugins.addAll(PluginWrapper.addAll(utils.resolvePlugins(modelPlugins), banMavenDefaults));
856     }
857 
858     private void getPlugins(List<PluginWrapper> plugins, Model model) {
859         List<Plugin> modelPlugins = ofNullable(model)
860                 .map(Model::getBuild)
861                 .map(PluginContainer::getPlugins)
862                 .orElseGet(Collections::emptyList);
863         plugins.addAll(PluginWrapper.addAll(utils.resolvePlugins(modelPlugins), banMavenDefaults));
864     }
865 
866     private void getPluginManagementPlugins(List<PluginWrapper> plugins, Model model) {
867         List<Plugin> modelPlugins = ofNullable(model)
868                 .map(Model::getBuild)
869                 .map(PluginConfiguration::getPluginManagement)
870                 .map(PluginContainer::getPlugins)
871                 .orElseGet(Collections::emptyList);
872         plugins.addAll(PluginWrapper.addAll(utils.resolvePlugins(modelPlugins), banMavenDefaults));
873     }
874 
875     private void getReportingPlugins(List<PluginWrapper> plugins, Model model) {
876         List<ReportPlugin> modelReportPlugins = ofNullable(model)
877                 .map(ModelBase::getReporting)
878                 .map(Reporting::getPlugins)
879                 .orElseGet(Collections::emptyList);
880         // add the reporting plugins
881         plugins.addAll(PluginWrapper.addAll(utils.resolveReportPlugins(modelReportPlugins), banMavenDefaults));
882     }
883 
884     /**
885      * Sets the ban latest.
886      *
887      * @param theBanLatest the banLatest to set
888      */
889     public void setBanLatest(boolean theBanLatest) {
890         this.banLatest = theBanLatest;
891     }
892 
893     /**
894      * Sets the ban release.
895      *
896      * @param theBanRelease the banRelease to set
897      */
898     public void setBanRelease(boolean theBanRelease) {
899         this.banRelease = theBanRelease;
900     }
901 
902     /**
903      * Checks if is ban snapshots.
904      *
905      * @return the banSnapshots
906      */
907     public boolean isBanSnapshots() {
908         return this.banSnapshots;
909     }
910 
911     /**
912      * Sets the ban snapshots.
913      *
914      * @param theBanSnapshots the banSnapshots to set
915      */
916     public void setBanSnapshots(boolean theBanSnapshots) {
917         this.banSnapshots = theBanSnapshots;
918     }
919 
920     /**
921      * Sets the ban timestamps.
922      *
923      * @param theBanTimestamps the banTimestamps to set
924      */
925     public void setBanTimestamps(boolean theBanTimestamps) {
926         this.banTimestamps = theBanTimestamps;
927     }
928 
929     @Override
930     public String toString() {
931         return String.format(
932                 "RequirePluginVersions[message=%s, banLatest=%b, banRelease=%b, banSnapshots=%b, banTimestamps=%b, phases=%s, additionalPlugins=%s, unCheckedPluginList=%s, unCheckedPlugins=%s]",
933                 getMessage(),
934                 banLatest,
935                 banRelease,
936                 banSnapshots,
937                 banTimestamps,
938                 phases,
939                 additionalPlugins,
940                 unCheckedPluginList,
941                 unCheckedPlugins);
942     }
943 }