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