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