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