001package org.apache.maven.plugins.enforcer;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *  http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import java.io.IOException;
023import java.util.ArrayList;
024import java.util.Arrays;
025import java.util.Collection;
026import java.util.HashMap;
027import java.util.HashSet;
028import java.util.List;
029import java.util.Map;
030import java.util.Set;
031
032import org.apache.maven.BuildFailureException;
033import org.apache.maven.artifact.Artifact;
034import org.apache.maven.artifact.factory.ArtifactFactory;
035import org.apache.maven.artifact.repository.ArtifactRepository;
036import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
037import org.apache.maven.artifact.resolver.ArtifactResolutionException;
038import org.apache.maven.artifact.resolver.ArtifactResolver;
039import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
040import org.apache.maven.artifact.versioning.VersionRange;
041import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
042import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper;
043import org.apache.maven.execution.MavenSession;
044import org.apache.maven.lifecycle.DefaultLifecycles;
045import org.apache.maven.lifecycle.Lifecycle;
046import org.apache.maven.lifecycle.LifecycleExecutionException;
047import org.apache.maven.lifecycle.mapping.LifecycleMapping;
048import org.apache.maven.model.BuildBase;
049import org.apache.maven.model.Model;
050import org.apache.maven.model.Plugin;
051import org.apache.maven.model.Profile;
052import org.apache.maven.model.ReportPlugin;
053import org.apache.maven.plugin.InvalidPluginException;
054import org.apache.maven.plugin.MojoExecutionException;
055import org.apache.maven.plugin.PluginManager;
056import org.apache.maven.plugin.PluginManagerException;
057import org.apache.maven.plugin.PluginNotFoundException;
058import org.apache.maven.plugin.descriptor.PluginDescriptor;
059import org.apache.maven.plugin.logging.Log;
060import org.apache.maven.plugin.version.PluginVersionNotFoundException;
061import org.apache.maven.plugin.version.PluginVersionResolutionException;
062import org.apache.maven.plugins.enforcer.utils.EnforcerRuleUtils;
063import org.apache.maven.plugins.enforcer.utils.PluginWrapper;
064import org.apache.maven.project.MavenProject;
065import org.apache.maven.rtinfo.RuntimeInformation;
066import org.apache.maven.settings.Settings;
067import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
068import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
069import org.codehaus.plexus.util.StringUtils;
070import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
071
072/**
073 * This rule will enforce that all plugins specified in the poms have a version declared.
074 *
075 * @author <a href="mailto:brianf@apache.org">Brian Fox</a>
076 */
077public class RequirePluginVersions
078    extends AbstractNonCacheableEnforcerRule
079{
080
081    private EnforcerRuleHelper helper;
082
083    /**
084     * Don't allow the LATEST identifier.
085     * 
086     * @see {@link #setBanLatest(boolean)}
087     * @see {@link #isBanLatest()}
088     */
089    private boolean banLatest = true;
090
091    /**
092     * Don't allow the RELEASE identifier.
093     * 
094     * @see {@link #setBanRelease(boolean)}
095     * @see {@link #isBanRelease()}
096     */
097    private boolean banRelease = true;
098
099    /**
100     * Don't allow snapshot plugins.
101     * 
102     * @see {@link #setBanSnapshots(boolean)}
103     * @see {@link #isBanSnapshots()}
104     */
105    private boolean banSnapshots = true;
106
107    /**
108     * Don't allow timestamp snapshot plugins.
109     * 
110     * @see {@link #setBanTimestamps(boolean)}
111     * @see {@link #isBanTimestamps()}
112     */
113    private boolean banTimestamps = true;
114
115    /**
116     * @since 3.0.0
117     */
118    private boolean banMavenDefaults = true;
119
120    /**
121     * The comma separated list of phases that should be used to find lifecycle plugin bindings. The default value is
122     * "clean,deploy,site".
123     * 
124     * @see {@link #setPhases(String)}
125     * @see {@link #getPhases()}
126     */
127    private String phases = "clean,deploy,site";
128
129    /**
130     * Additional plugins to enforce have versions. These are plugins that may not be in the poms but are used anyway,
131     * like help, eclipse etc. <br>
132     * The plugins should be specified in the form: <code>group:artifactId</code>.
133     * 
134     * @see {@link #setAdditionalPlugins(List)}
135     * @see {@link #getAdditionalPlugins()}
136     */
137    private List<String> additionalPlugins;
138
139    /**
140     * Plugins to skip for version enforcement. The plugins should be specified in the form:
141     * <code>group:artifactId</code>. NOTE: This is deprecated, use unCheckedPluginList instead.
142     * 
143     * @see {@link #setUnCheckedPlugins(List)}
144     * @see {@link #getUnCheckedPlugins()}
145     */
146    private List<String> unCheckedPlugins;
147
148    /**
149     * Same as unCheckedPlugins but as a comma list to better support properties. Sample form:
150     * <code>group:artifactId,group2:artifactId2</code>
151     * 
152     * @since 1.0-beta-1
153     * @see {@link #setUnCheckedPlugins(List)}
154     * @see {@link #getUnCheckedPlugins()}
155     */
156    private String unCheckedPluginList;
157
158    /** The plugin manager. */
159    private PluginManager pluginManager;
160
161    /** The phase to lifecycle map. */
162    private Map<String, Lifecycle> phaseToLifecycleMap;
163
164    /** The lifecycles. */
165    private Collection<Lifecycle> lifecycles;
166
167    /** The factory. */
168    private ArtifactFactory factory;
169
170    /** The resolver. */
171    private ArtifactResolver resolver;
172
173    /** The local. */
174    private ArtifactRepository local;
175
176    /** The log. */
177    private Log log;
178
179    /** The session. */
180    private MavenSession session;
181
182    /** The utils. */
183    private EnforcerRuleUtils utils;
184
185    private RuntimeInformation runtimeInformation;
186
187    @Override
188    public void execute( EnforcerRuleHelper helper )
189        throws EnforcerRuleException
190    {
191        this.log = helper.getLog();
192        this.helper = helper;
193
194        MavenProject project;
195        try
196        {
197            // get the various expressions out of the helper.
198
199            project = (MavenProject) helper.evaluate( "${project}" );
200
201            runtimeInformation = helper.getComponent( RuntimeInformation.class );
202
203            DefaultLifecycles defaultLifeCycles = helper.getComponent( DefaultLifecycles.class );
204            lifecycles = defaultLifeCycles.getLifeCycles();
205
206            session = (MavenSession) helper.evaluate( "${session}" );
207            pluginManager = helper.getComponent( PluginManager.class );
208            factory = helper.getComponent( ArtifactFactory.class );
209            resolver = helper.getComponent( ArtifactResolver.class );
210            local = (ArtifactRepository) helper.evaluate( "${localRepository}" );
211
212            utils = new EnforcerRuleUtils( helper );
213
214            // get all the plugins that are bound to the specified lifecycles
215            Set<Plugin> allPlugins = getBoundPlugins( project, phases );
216
217            // insert any additional plugins specified by the user.
218            allPlugins = addAdditionalPlugins( allPlugins, additionalPlugins );
219            allPlugins.addAll( getProfilePlugins( project ) );
220
221            // pull out any we should skip
222            allPlugins =
223                removeUncheckedPlugins( combineUncheckedPlugins( unCheckedPlugins, unCheckedPluginList ), allPlugins );
224
225            // there's nothing to do here
226            if ( allPlugins.isEmpty() )
227            {
228                log.info( "No plugin bindings found." );
229                return;
230            }
231            else
232            {
233                log.debug( "All Plugins in use: " + allPlugins );
234            }
235
236            // get all the plugins that are mentioned in the pom (and parents)
237            List<PluginWrapper> pluginWrappers = getAllPluginEntries( project );
238
239            for ( PluginWrapper pluginWrapper : pluginWrappers )
240            {
241                log.debug( "pluginWrappers: " + pluginWrapper.getGroupId() + ":" + pluginWrapper.getArtifactId() + ":"
242                    + pluginWrapper.getVersion() + " source: " + pluginWrapper.getSource() );
243            }
244            // now look for the versions that aren't valid and add to a list.
245            List<Plugin> failures = new ArrayList<Plugin>();
246
247            for ( Plugin plugin : allPlugins )
248            {
249                if ( !hasValidVersionSpecified( helper, plugin, pluginWrappers ) )
250                {
251                    failures.add( plugin );
252                }
253            }
254
255            // if anything was found, log it then append the optional message.
256            if ( !failures.isEmpty() )
257            {
258                handleMessagesToTheUser( project, failures );
259            }
260        }
261        catch ( ExpressionEvaluationException e )
262        {
263            throw new EnforcerRuleException( "Unable to Evaluate an Expression:" + e.getLocalizedMessage() );
264        }
265        catch ( ComponentLookupException e )
266        {
267            throw new EnforcerRuleException( "Unable to lookup a component:" + e.getLocalizedMessage() );
268        }
269        catch ( Exception e )
270        {
271            throw new EnforcerRuleException( e.getLocalizedMessage(), e );
272        }
273    }
274
275    private void handleMessagesToTheUser( MavenProject project, List<Plugin> failures )
276        throws EnforcerRuleException
277    {
278        StringBuilder newMsg = new StringBuilder();
279        newMsg.append( "Some plugins are missing valid versions or depend on Maven "
280            + runtimeInformation.getMavenVersion() + " defaults:" );
281        handleBanMessages( newMsg );
282        newMsg.append( "\n" );
283        for ( Plugin plugin : failures )
284        {
285            newMsg.append( plugin.getGroupId() );
286            newMsg.append( ":" );
287            newMsg.append( plugin.getArtifactId() );
288
289            try
290            {
291                newMsg.append( ". \tThe version currently in use is " );
292
293                Plugin currentPlugin = findCurrentPlugin( plugin, project );
294
295                if ( currentPlugin == null )
296                {
297                    newMsg.append( "unknown" );
298                }
299                else
300                {
301                    newMsg.append( currentPlugin.getVersion() );
302
303                    if ( PluginWrapper.isVersionFromDefaultLifecycleBindings( currentPlugin ).orElse( false ) )
304                    {
305                        newMsg.append( " via default lifecycle bindings" );
306                    }
307                    else 
308                    {
309                        String msg = PluginWrapper.isVersionFromSuperpom( currentPlugin )
310                                        .filter( b -> b )
311                                        .map( t -> " via super POM" )
312                                        // for Maven 3.6.0 or before (MNG-6593 / MNG-6600)
313                                        .orElse( " via super POM or default lifecycle bindings" );
314                        newMsg.append( msg );
315                    }
316                }
317            }
318            catch ( Exception e )
319            {
320                // lots can go wrong here. Don't allow any issues trying to
321                // determine the issue stop me
322                log.debug( "Exception while determining plugin Version.", e );
323                newMsg.append( ". Unable to determine the plugin version." );
324            }
325            newMsg.append( "\n" );
326        }
327        String message = getMessage();
328        if ( StringUtils.isNotEmpty( message ) )
329        {
330            newMsg.append( message );
331        }
332
333        throw new EnforcerRuleException( newMsg.toString() );
334    }
335
336    private void handleBanMessages( StringBuilder newMsg )
337    {
338        if ( banLatest || banRelease || banSnapshots || banTimestamps )
339        {
340            newMsg.append( " (" );
341            if ( banLatest )
342            {
343                newMsg.append( "LATEST " );
344            }
345            if ( banRelease )
346            {
347                newMsg.append( "RELEASE " );
348            }
349            if ( banSnapshots || banTimestamps )
350            {
351                newMsg.append( "SNAPSHOT " );
352            }
353            newMsg.append( "are not allowed)" );
354        }
355    }
356
357    /**
358     * Remove the plugins that the user doesn't want to check.
359     *
360     * @param uncheckedPlugins
361     * @param plugins
362     * @throws MojoExecutionException
363     * @return The plugins which have been removed.
364     */
365    public Set<Plugin> removeUncheckedPlugins( Collection<String> uncheckedPlugins, Set<Plugin> plugins )
366        throws MojoExecutionException
367    {
368        if ( uncheckedPlugins != null && !uncheckedPlugins.isEmpty() )
369        {
370            for ( String pluginKey : uncheckedPlugins )
371            {
372                Plugin plugin = parsePluginString( pluginKey, "UncheckedPlugins" );
373                plugins.remove( plugin );
374            }
375        }
376        return plugins;
377    }
378
379    /**
380     * Combines the old Collection with the new comma separated list.
381     * 
382     * @param uncheckedPlugins
383     * @param uncheckedPluginsList
384     * @return List of unchecked plugins.
385     */
386    // CHECKSTYLE_OFF: LineLength
387    public Collection<String> combineUncheckedPlugins( Collection<String> uncheckedPlugins,
388                                                       String uncheckedPluginsList )
389    // CHECKSTYLE_ON: LineLength
390    {
391        // if the comma list is empty, then there's nothing to do here.
392        if ( StringUtils.isNotEmpty( uncheckedPluginsList ) )
393        {
394            // make sure there is a collection to add to.
395            if ( uncheckedPlugins == null )
396            {
397                uncheckedPlugins = new HashSet<String>();
398            }
399            else if ( !uncheckedPlugins.isEmpty() && log != null )
400            {
401                log.warn( "The parameter 'unCheckedPlugins' is deprecated. Use 'unCheckedPluginList' instead" );
402            }
403
404            uncheckedPlugins.addAll( Arrays.asList( uncheckedPluginsList.split( "," ) ) );
405        }
406        return uncheckedPlugins;
407    }
408
409    /**
410     * Add the additional plugins if they don't exist yet.
411     *
412     * @param existing the existing
413     * @param additional the additional
414     * @return the sets the
415     * @throws MojoExecutionException the mojo execution exception
416     */
417    public Set<Plugin> addAdditionalPlugins( Set<Plugin> existing, List<String> additional )
418        throws MojoExecutionException
419    {
420        if ( additional != null )
421        {
422            for ( String pluginString : additional )
423            {
424                Plugin plugin = parsePluginString( pluginString, "AdditionalPlugins" );
425
426                if ( existing == null )
427                {
428                    existing = new HashSet<>();
429                    existing.add( plugin );
430                }
431                else if ( !existing.contains( plugin ) )
432                {
433                    existing.add( plugin );
434                }
435            }
436        }
437        return existing;
438    }
439
440    /**
441     * Helper method to parse and inject a Plugin.
442     *
443     * @param pluginString
444     * @param field
445     * @throws MojoExecutionException
446     * @return the plugin
447     */
448    protected Plugin parsePluginString( String pluginString, String field )
449        throws MojoExecutionException
450    {
451        if ( pluginString != null )
452        {
453            String[] pluginStrings = pluginString.split( ":" );
454            if ( pluginStrings.length == 2 )
455            {
456                Plugin plugin = new Plugin();
457                plugin.setGroupId( StringUtils.strip( pluginStrings[0] ) );
458                plugin.setArtifactId( StringUtils.strip( pluginStrings[1] ) );
459
460                return plugin;
461            }
462            else
463            {
464                throw new MojoExecutionException( "Invalid " + field + " string: " + pluginString );
465            }
466        }
467        else
468        {
469            throw new MojoExecutionException( "Invalid " + field + " string: " + pluginString );
470        }
471
472    }
473
474    /**
475     * Finds the plugins that are listed in active profiles.
476     *
477     * @param project the project
478     * @return the profile plugins
479     */
480    public Set<Plugin> getProfilePlugins( MavenProject project )
481    {
482        Set<Plugin> result = new HashSet<>();
483        List<Profile> profiles = project.getActiveProfiles();
484        if ( profiles != null && !profiles.isEmpty() )
485        {
486            for ( Profile p : profiles )
487            {
488                BuildBase b = p.getBuild();
489                if ( b != null )
490                {
491                    List<Plugin> plugins = b.getPlugins();
492                    if ( plugins != null )
493                    {
494                        result.addAll( plugins );
495                    }
496                }
497            }
498        }
499        return result;
500    }
501
502    /**
503     * Given a plugin, this will retrieve the matching plugin artifact from the model.
504     *
505     * @param plugin plugin to lookup
506     * @param project project to search
507     * @return matching plugin, <code>null</code> if not found.
508     */
509    protected Plugin findCurrentPlugin( Plugin plugin, MavenProject project )
510    {
511        Plugin found = null;
512        try
513        {
514            Model model = project.getModel();
515            Map<String, Plugin> plugins = model.getBuild().getPluginsAsMap();
516            found = plugins.get( plugin.getKey() );
517        }
518        catch ( NullPointerException e )
519        {
520            // nothing to do here
521        }
522
523        if ( found == null )
524        {
525            found = resolvePlugin( plugin, project );
526        }
527
528        return found;
529    }
530
531    /**
532     * Resolve plugin.
533     *
534     * @param plugin the plugin
535     * @param project the project
536     * @return the plugin
537     */
538    protected Plugin resolvePlugin( Plugin plugin, MavenProject project )
539    {
540
541        List<ArtifactRepository> pluginRepositories = project.getPluginArtifactRepositories();
542        Artifact artifact = factory.createPluginArtifact( plugin.getGroupId(), plugin.getArtifactId(),
543                                                          VersionRange.createFromVersion( "LATEST" ) );
544
545        try
546        {
547            this.resolver.resolve( artifact, pluginRepositories, this.local );
548            plugin.setVersion( artifact.getVersion() );
549        }
550        catch ( ArtifactResolutionException | ArtifactNotFoundException e )
551        {
552            // What does this mean?
553        }
554
555        return plugin;
556    }
557
558    /**
559     * Gets the plugins that are bound to the defined phases. This does not find plugins bound in the pom to a phase
560     * later than the plugin is executing.
561     *
562     * @param project the project
563     * @param thePhases the phases
564     * @return the bound plugins
565     * @throws PluginNotFoundException the plugin not found exception
566     * @throws LifecycleExecutionException the lifecycle execution exception
567     * @throws IllegalAccessException the illegal access exception
568     */
569    protected Set<Plugin> getBoundPlugins( MavenProject project, String thePhases )
570        throws PluginNotFoundException, LifecycleExecutionException, IllegalAccessException
571    {
572
573        Set<Plugin> allPlugins = new HashSet<>();
574
575        // lookup the bindings for all the passed in phases
576        String[] lifecyclePhases = thePhases.split( "," );
577        for ( int i = 0; i < lifecyclePhases.length; i++ )
578        {
579            String lifecyclePhase = lifecyclePhases[i];
580            if ( StringUtils.isNotEmpty( lifecyclePhase ) )
581            {
582                try
583                {
584                    Lifecycle lifecycle = getLifecycleForPhase( lifecyclePhase );
585                    log.debug( "getBoundPlugins(): " + project.getId() + " " + lifecyclePhase + " "
586                        + lifecycle.getId() );
587                    allPlugins.addAll( getAllPlugins( project, lifecycle ) );
588                }
589                catch ( BuildFailureException e )
590                {
591                    // i'm going to swallow this because the
592                    // user may have declared a phase that
593                    // doesn't exist for every module.
594                }
595            }
596        }
597        return allPlugins;
598    }
599
600    /**
601     * Checks for valid version specified. Checks to see if the version is specified for the plugin. Can optionally ban
602     * "RELEASE" or "LATEST" even if specified.
603     *
604     * @param helper the helper
605     * @param source the source
606     * @param pluginWrappers the plugins
607     * @return true, if successful
608     */
609    protected boolean hasValidVersionSpecified( EnforcerRuleHelper helper, Plugin source,
610                                                List<PluginWrapper> pluginWrappers )
611    {
612        boolean found = false;
613        boolean status = false;
614        for ( PluginWrapper plugin : pluginWrappers )
615        {
616            // find the matching plugin entry
617            if ( isMatchingPlugin( source, plugin ) )
618            {
619                found = true;
620                // found the entry. now see if the version is specified
621                String version = plugin.getVersion();
622                try
623                {
624                    version = (String) helper.evaluate( version );
625                }
626                catch ( ExpressionEvaluationException e )
627                {
628                    return false;
629                }
630
631                if ( isValidVersion( version ) )
632                {
633                    helper.getLog().debug( "checking for notEmpty and notIsWhitespace(): " + version );
634                    if ( banRelease && version.equals( "RELEASE" ) )
635                    {
636                        return false;
637                    }
638
639                    if ( banLatest && version.equals( "LATEST" ) )
640                    {
641                        return false;
642                    }
643
644                    if ( banSnapshots && isSnapshot( version ) )
645                    {
646                        return false;
647                    }
648                    // the version was specified and not
649                    // banned. It's ok. Keep looking through the list to make
650                    // sure it's not using a banned version somewhere else.
651
652                    status = true;
653
654                    if ( !banRelease && !banLatest && !banSnapshots )
655                    {
656                        // no need to keep looking
657                        break;
658                    }
659                }
660            }
661        }
662        if ( !found )
663        {
664            helper.getLog().debug( "plugin " + source.getGroupId() + ":" + source.getArtifactId() + " not found" );
665        }
666        return status;
667    }
668
669    private boolean isValidVersion( String version )
670    {
671        return StringUtils.isNotEmpty( version ) && !StringUtils.isWhitespace( version );
672    }
673
674    private boolean isMatchingPlugin( Plugin source, PluginWrapper plugin )
675    {
676        return source.getArtifactId().equals( plugin.getArtifactId() )
677            && source.getGroupId().equals( plugin.getGroupId() );
678    }
679
680    /**
681     * Checks if is snapshot.
682     *
683     * @param baseVersion the base version
684     * @return true, if is snapshot
685     */
686    protected boolean isSnapshot( String baseVersion )
687    {
688        if ( banTimestamps )
689        {
690            return Artifact.VERSION_FILE_PATTERN.matcher( baseVersion ).matches()
691                || baseVersion.endsWith( Artifact.SNAPSHOT_VERSION );
692        }
693        else
694        {
695            return baseVersion.endsWith( Artifact.SNAPSHOT_VERSION );
696        }
697    }
698
699    /*
700     * Uses borrowed lifecycle code to get a list of all plugins bound to the lifecycle.
701     */
702    /**
703     * Gets the all plugins.
704     *
705     * @param project the project
706     * @param lifecycle the lifecycle
707     * @return the all plugins
708     * @throws PluginNotFoundException the plugin not found exception
709     * @throws LifecycleExecutionException the lifecycle execution exception
710     */
711    private Set<Plugin> getAllPlugins( MavenProject project, Lifecycle lifecycle )
712        throws PluginNotFoundException, LifecycleExecutionException
713
714    {
715        log.debug( "RequirePluginVersions.getAllPlugins:" );
716
717        Set<Plugin> plugins = new HashSet<>();
718        // first, bind those associated with the packaging
719        Map<String, String> mappings = findMappingsForLifecycle( project, lifecycle );
720
721        for ( Map.Entry<String, String> entry : mappings.entrySet() )
722        {
723            log.debug( "  lifecycleMapping = " + entry.getKey() );
724            String pluginsForLifecycle = (String) entry.getValue();
725            log.debug( "  plugins = " + pluginsForLifecycle );
726            if ( StringUtils.isNotEmpty( pluginsForLifecycle ) )
727            {
728                String pluginList[] = pluginsForLifecycle.split( "," );
729                for ( String plugin : pluginList )
730                {
731                    plugin = StringUtils.strip( plugin );
732                    log.debug( "    plugin = " + plugin );
733                    String tokens[] = plugin.split( ":" );
734                    log.debug( "    GAV = " + Arrays.asList( tokens ) );
735
736                    Plugin p = new Plugin();
737                    p.setGroupId( tokens[0] );
738                    p.setArtifactId( tokens[1] );
739                    plugins.add( p );
740                }
741            }
742        }
743
744        plugins.addAll( project.getBuildPlugins() );
745
746        return plugins;
747    }
748
749    /*
750     * NOTE: All the code following this point was scooped from the DefaultLifecycleExecutor. There must be a better way
751     * but for now it should work.
752     */
753    /**
754     * Gets the phase to lifecycle map.
755     *
756     * @return the phase to lifecycle map
757     * @throws LifecycleExecutionException the lifecycle execution exception
758     */
759    public Map<String, Lifecycle> getPhaseToLifecycleMap()
760        throws LifecycleExecutionException
761    {
762        if ( phaseToLifecycleMap == null )
763        {
764            phaseToLifecycleMap = new HashMap<>();
765
766            for ( Lifecycle lifecycle : lifecycles )
767            {
768                List<String> phases = lifecycle.getPhases();
769                for ( String phase : phases )
770                {
771                    log.debug( "getPhaseToLifecycleMap(): phase: " + phase );
772                    if ( phaseToLifecycleMap.containsKey( phase ) )
773                    {
774                        Lifecycle prevLifecycle = (Lifecycle) phaseToLifecycleMap.get( phase );
775                        throw new LifecycleExecutionException( "Phase '" + phase
776                            + "' is defined in more than one lifecycle: '" + lifecycle.getId() + "' and '"
777                            + prevLifecycle.getId() + "'" );
778                    }
779                    else
780                    {
781                        phaseToLifecycleMap.put( phase, lifecycle );
782                    }
783                }
784            }
785        }
786        return phaseToLifecycleMap;
787    }
788
789    /**
790     * Gets the lifecycle for phase.
791     *
792     * @param phase the phase
793     * @return the lifecycle for phase
794     * @throws BuildFailureException the build failure exception
795     * @throws LifecycleExecutionException the lifecycle execution exception
796     */
797    private Lifecycle getLifecycleForPhase( String phase )
798        throws BuildFailureException, LifecycleExecutionException
799    {
800        Lifecycle lifecycle = getPhaseToLifecycleMap().get( phase );
801
802        if ( lifecycle == null )
803        {
804            throw new BuildFailureException( "Unable to find lifecycle for phase '" + phase + "'" );
805        }
806        return lifecycle;
807    }
808
809    /**
810     * Find mappings for lifecycle.
811     *
812     * @param project the project
813     * @param lifecycle the lifecycle
814     * @return the map
815     * @throws LifecycleExecutionException the lifecycle execution exception
816     * @throws PluginNotFoundException the plugin not found exception
817     */
818    private Map<String, String> findMappingsForLifecycle( MavenProject project, Lifecycle lifecycle )
819        throws LifecycleExecutionException, PluginNotFoundException
820    {
821        String packaging = project.getPackaging();
822        Map<String, String> mappings = null;
823
824        LifecycleMapping m = (LifecycleMapping) findExtension( project, LifecycleMapping.ROLE, packaging,
825                                                               session.getSettings(), session.getLocalRepository() );
826        if ( m != null )
827        {
828            mappings = m.getPhases( lifecycle.getId() );
829        }
830
831        Map<String, String> defaultMappings = lifecycle.getDefaultPhases();
832
833        if ( mappings == null )
834        {
835            try
836            {
837                m = helper.getComponent( LifecycleMapping.class, packaging );
838                mappings = m.getPhases( lifecycle.getId() );
839            }
840            catch ( ComponentLookupException e )
841            {
842                if ( defaultMappings == null )
843                {
844                    throw new LifecycleExecutionException( "Cannot find lifecycle mapping for packaging: \'" + packaging
845                        + "\'.", e );
846                }
847            }
848        }
849
850        if ( mappings == null )
851        {
852            if ( defaultMappings == null )
853            {
854                throw new LifecycleExecutionException( "Cannot find lifecycle mapping for packaging: \'" + packaging
855                    + "\', and there is no default" );
856            }
857            else
858            {
859                mappings = defaultMappings;
860            }
861        }
862
863        return mappings;
864    }
865
866    /**
867     * Find extension.
868     *
869     * @param project the project
870     * @param role the role
871     * @param roleHint the role hint
872     * @param settings the settings
873     * @param localRepository the local repository
874     * @return the object
875     * @throws LifecycleExecutionException the lifecycle execution exception
876     * @throws PluginNotFoundException the plugin not found exception
877     */
878    private Object findExtension( MavenProject project, String role, String roleHint, Settings settings,
879                                  ArtifactRepository localRepository )
880        throws LifecycleExecutionException, PluginNotFoundException
881    {
882        Object pluginComponent = null;
883
884        List<Plugin> buildPlugins = project.getBuildPlugins();
885        for ( Plugin plugin : buildPlugins )
886        {
887            if ( plugin.isExtensions() )
888            {
889                verifyPlugin( plugin, project, settings, localRepository );
890
891                // TODO: if moved to the plugin manager we
892                // already have the descriptor from above
893                // and so do can lookup the container
894                // directly
895                try
896                {
897                    pluginComponent = pluginManager.getPluginComponent( plugin, role, roleHint );
898
899                    if ( pluginComponent != null )
900                    {
901                        break;
902                    }
903                }
904                catch ( ComponentLookupException e )
905                {
906                    log.debug( "Unable to find the lifecycle component in the extension", e );
907                }
908                catch ( PluginManagerException e )
909                {
910                    throw new LifecycleExecutionException( "Error getting extensions from the plugin '"
911                        + plugin.getKey() + "': " + e.getMessage(), e );
912                }
913            }
914        }
915        return pluginComponent;
916    }
917
918    /**
919     * Verify plugin.
920     *
921     * @param plugin the plugin
922     * @param project the project
923     * @param settings the settings
924     * @param localRepository the local repository
925     * @return the plugin descriptor
926     * @throws LifecycleExecutionException the lifecycle execution exception
927     * @throws PluginNotFoundException the plugin not found exception
928     */
929    private PluginDescriptor verifyPlugin( Plugin plugin, MavenProject project, Settings settings,
930                                           ArtifactRepository localRepository )
931        throws LifecycleExecutionException, PluginNotFoundException
932    {
933        PluginDescriptor pluginDescriptor;
934        try
935        {
936            pluginDescriptor = pluginManager.verifyPlugin( plugin, project, settings, localRepository );
937        }
938        catch ( PluginManagerException e )
939        {
940            throw new LifecycleExecutionException( "Internal error in the plugin manager getting plugin '"
941                + plugin.getKey() + "': " + e.getMessage(), e );
942        }
943        catch ( PluginVersionResolutionException | InvalidVersionSpecificationException | InvalidPluginException
944            | ArtifactNotFoundException | ArtifactResolutionException | PluginVersionNotFoundException e )
945        {
946            throw new LifecycleExecutionException( e.getMessage(), e );
947        }
948        return pluginDescriptor;
949    }
950
951    /**
952     * Gets all plugin entries in build.plugins, build.pluginManagement.plugins, profile.build.plugins, reporting and
953     * profile.reporting in this project and all parents
954     *
955     * @param project the project
956     * @return the all plugin entries wrapped in a PluginWrapper Object
957     * @throws ArtifactResolutionException the artifact resolution exception
958     * @throws ArtifactNotFoundException the artifact not found exception
959     * @throws IOException Signals that an I/O exception has occurred.
960     * @throws XmlPullParserException the xml pull parser exception
961     */
962    protected List<PluginWrapper> getAllPluginEntries( MavenProject project )
963        throws ArtifactResolutionException, ArtifactNotFoundException, IOException, XmlPullParserException
964    {
965        List<PluginWrapper> plugins = new ArrayList<>();
966        // now find all the plugin entries, either in
967        // build.plugins or build.pluginManagement.plugins, profiles.plugins and reporting
968
969        getPlugins( plugins, project.getModel() );
970        getReportingPlugins( plugins, project.getModel() );
971        getPluginManagementPlugins( plugins, project.getModel() );
972        addPluginsInProfiles( plugins, project.getModel() );
973
974        return plugins;
975    }
976
977
978    private void addPluginsInProfiles( List<PluginWrapper> plugins, Model model )
979    {
980        List<Profile> profiles = model.getProfiles();
981        for ( Profile profile : profiles )
982        {
983            getProfilePlugins( plugins, model, profile );
984            getProfileReportingPlugins( plugins, model, profile );
985            getProfilePluginManagementPlugins( plugins, model, profile );
986        }
987    }
988
989    private void getProfilePluginManagementPlugins( List<PluginWrapper> plugins, Model model, Profile profile )
990    {
991        try
992        {
993            List<Plugin> modelPlugins = profile.getBuild().getPluginManagement().getPlugins();
994            plugins.addAll( PluginWrapper.addAll( utils.resolvePlugins( modelPlugins ), banMavenDefaults ) );
995        }
996        catch ( NullPointerException e )
997        {
998            // guess there are no plugins here.
999        }
1000    }
1001
1002    private void getProfileReportingPlugins( List<PluginWrapper> plugins, Model model, Profile profile )
1003    {
1004        try
1005        {
1006            List<ReportPlugin> modelReportPlugins = profile.getReporting().getPlugins();
1007            // add the reporting plugins
1008            plugins.addAll( PluginWrapper.addAll( utils.resolveReportPlugins( modelReportPlugins ),
1009                                                  banMavenDefaults ) );
1010        }
1011        catch ( NullPointerException e )
1012        {
1013            // guess there are no plugins here.
1014        }
1015    }
1016
1017    private void getProfilePlugins( List<PluginWrapper> plugins, Model model, Profile profile )
1018    {
1019        try
1020        {
1021            List<Plugin> modelPlugins = profile.getBuild().getPlugins();
1022            plugins.addAll( PluginWrapper.addAll( utils.resolvePlugins( modelPlugins ), banMavenDefaults ) );
1023        }
1024        catch ( NullPointerException e )
1025        {
1026            // guess there are no plugins here.
1027        }
1028    }
1029
1030    private void getPlugins( List<PluginWrapper> plugins, Model model )
1031    {
1032        try
1033        {
1034            List<Plugin> modelPlugins = model.getBuild().getPlugins();
1035            plugins.addAll( PluginWrapper.addAll( utils.resolvePlugins( modelPlugins ), banMavenDefaults ) );
1036        }
1037        catch ( NullPointerException e )
1038        {
1039            // guess there are no plugins here.
1040        }
1041    }
1042
1043    private void getPluginManagementPlugins( List<PluginWrapper> plugins, Model model )
1044    {
1045        try
1046        {
1047            List<Plugin> modelPlugins = model.getBuild().getPluginManagement().getPlugins();
1048            plugins.addAll( PluginWrapper.addAll( utils.resolvePlugins( modelPlugins ), banMavenDefaults ) );
1049        }
1050        catch ( NullPointerException e )
1051        {
1052            // guess there are no plugins here.
1053        }
1054    }
1055
1056    private void getReportingPlugins( List<PluginWrapper> plugins, Model model )
1057    {
1058        try
1059        {
1060            List<ReportPlugin> modelReportPlugins = model.getReporting().getPlugins();
1061            // add the reporting plugins
1062            plugins.addAll( PluginWrapper.addAll( utils.resolveReportPlugins( modelReportPlugins ),
1063                                                  banMavenDefaults ) );
1064        }
1065        catch ( NullPointerException e )
1066        {
1067            // guess there are no plugins here.
1068        }
1069    }
1070
1071    /**
1072     * Checks if is ban latest.
1073     *
1074     * @return the banLatest
1075     */
1076    protected boolean isBanLatest()
1077    {
1078        return this.banLatest;
1079    }
1080
1081    /**
1082     * Sets the ban latest.
1083     *
1084     * @param theBanLatest the banLatest to set
1085     */
1086    protected void setBanLatest( boolean theBanLatest )
1087    {
1088        this.banLatest = theBanLatest;
1089    }
1090
1091    /**
1092     * Checks if is ban release.
1093     *
1094     * @return the banRelease
1095     */
1096    protected boolean isBanRelease()
1097    {
1098        return this.banRelease;
1099    }
1100
1101    /**
1102     * Sets the ban release.
1103     *
1104     * @param theBanRelease the banRelease to set
1105     */
1106    protected void setBanRelease( boolean theBanRelease )
1107    {
1108        this.banRelease = theBanRelease;
1109    }
1110
1111    /**
1112     * Gets the utils.
1113     *
1114     * @return the utils
1115     */
1116    protected EnforcerRuleUtils getUtils()
1117    {
1118        return this.utils;
1119    }
1120
1121    /**
1122     * Sets the utils.
1123     *
1124     * @param theUtils the utils to set
1125     */
1126    protected void setUtils( EnforcerRuleUtils theUtils )
1127    {
1128        this.utils = theUtils;
1129    }
1130
1131    /**
1132     * Checks if is ban snapshots.
1133     *
1134     * @return the banSnapshots
1135     */
1136    public boolean isBanSnapshots()
1137    {
1138        return this.banSnapshots;
1139    }
1140
1141    /**
1142     * Sets the ban snapshots.
1143     *
1144     * @param theBanSnapshots the banSnapshots to set
1145     */
1146    public void setBanSnapshots( boolean theBanSnapshots )
1147    {
1148        this.banSnapshots = theBanSnapshots;
1149    }
1150
1151    /**
1152     * Checks if is ban timestamps.
1153     *
1154     * @return the banTimestamps
1155     */
1156    public boolean isBanTimestamps()
1157    {
1158        return this.banTimestamps;
1159    }
1160
1161    /**
1162     * Sets the ban timestamps.
1163     *
1164     * @param theBanTimestamps the banTimestamps to set
1165     */
1166    public void setBanTimestamps( boolean theBanTimestamps )
1167    {
1168        this.banTimestamps = theBanTimestamps;
1169    }
1170
1171    public List<String> getUnCheckedPlugins()
1172    {
1173        return unCheckedPlugins;
1174    }
1175
1176    public void setUnCheckedPlugins( List<String> unCheckedPlugins )
1177    {
1178        this.unCheckedPlugins = unCheckedPlugins;
1179    }
1180
1181    public final void setPhases( String phases )
1182    {
1183        this.phases = phases;
1184    }
1185
1186    public final String getPhases()
1187    {
1188        return phases;
1189    }
1190
1191    public final void setAdditionalPlugins( List<String> additionalPlugins )
1192    {
1193        this.additionalPlugins = additionalPlugins;
1194    }
1195
1196    public final List<String> getAdditionalPlugins()
1197    {
1198        return additionalPlugins;
1199    }
1200}