Package org.apache.maven.lifecycle

Source Code of org.apache.maven.lifecycle.DefaultLifecycleExecutor

package org.apache.maven.lifecycle;

/*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional information regarding
* copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License. You may obtain a
* copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeMap;
import java.util.TreeSet;

import org.apache.maven.ProjectDependenciesResolver;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.ArtifactUtils;
import org.apache.maven.artifact.repository.DefaultRepositoryRequest;
import org.apache.maven.artifact.repository.RepositoryRequest;
import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
import org.apache.maven.artifact.resolver.ArtifactResolutionException;
import org.apache.maven.artifact.resolver.MultipleArtifactsNotFoundException;
import org.apache.maven.execution.BuildFailure;
import org.apache.maven.execution.BuildSuccess;
import org.apache.maven.execution.DefaultLifecycleEvent;
import org.apache.maven.execution.ExecutionEvent;
import org.apache.maven.execution.ExecutionListener;
import org.apache.maven.execution.MavenExecutionRequest;
import org.apache.maven.execution.MavenExecutionResult;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.lifecycle.mapping.LifecycleMapping;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.Plugin;
import org.apache.maven.model.PluginExecution;
import org.apache.maven.plugin.InvalidPluginDescriptorException;
import org.apache.maven.plugin.MojoExecution;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.MojoNotFoundException;
import org.apache.maven.plugin.PluginConfigurationException;
import org.apache.maven.plugin.PluginDescriptorParsingException;
import org.apache.maven.plugin.BuildPluginManager;
import org.apache.maven.plugin.PluginManagerException;
import org.apache.maven.plugin.PluginNotFoundException;
import org.apache.maven.plugin.PluginResolutionException;
import org.apache.maven.plugin.descriptor.MojoDescriptor;
import org.apache.maven.plugin.descriptor.Parameter;
import org.apache.maven.plugin.descriptor.PluginDescriptor;
import org.apache.maven.plugin.lifecycle.Execution;
import org.apache.maven.plugin.lifecycle.Phase;
import org.apache.maven.plugin.prefix.DefaultPluginPrefixRequest;
import org.apache.maven.plugin.prefix.NoPluginFoundForPrefixException;
import org.apache.maven.plugin.prefix.PluginPrefixRequest;
import org.apache.maven.plugin.prefix.PluginPrefixResolver;
import org.apache.maven.plugin.prefix.PluginPrefixResult;
import org.apache.maven.plugin.version.DefaultPluginVersionRequest;
import org.apache.maven.plugin.version.PluginVersionRequest;
import org.apache.maven.plugin.version.PluginVersionResolutionException;
import org.apache.maven.plugin.version.PluginVersionResolver;
import org.apache.maven.project.MavenProject;
import org.apache.maven.repository.RepositorySystem;
import org.codehaus.plexus.classworlds.realm.ClassRealm;
import org.codehaus.plexus.component.annotations.Requirement;
import org.codehaus.plexus.configuration.PlexusConfiguration;
import org.codehaus.plexus.logging.Logger;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable;
import org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException;
import org.codehaus.plexus.util.StringUtils;
import org.codehaus.plexus.util.xml.Xpp3Dom;
import org.codehaus.plexus.util.xml.pull.XmlPullParserException;

//TODO: The configuration for the lifecycle needs to be externalized so that I can use the annotations properly for the wiring and reference and external source for the lifecycle configuration.
//TODO: check for online status in the build plan and die if necessary

/**
* @author Jason van Zyl
*/
public class DefaultLifecycleExecutor
    implements LifecycleExecutor, Initializable
{
    @Requirement
    private Logger logger;

    @Requirement
    private BuildPluginManager pluginManager;

    @Requirement
    protected RepositorySystem repositorySystem;

    @Requirement
    private ProjectDependenciesResolver projectDependenciesResolver;

    @Requirement
    private PluginVersionResolver pluginVersionResolver;

    @Requirement
    private PluginPrefixResolver pluginPrefixResolver;
           
    // @Configuration(source="org/apache/maven/lifecycle/lifecycles.xml")   
    private List<Lifecycle> lifecycles;

    /**
     * We use this to display all the lifecycles available and their phases to users. Currently this is primarily
     * used in the IDE integrations where a UI is presented to the user and they can select the lifecycle phase
     * they would like to execute.
     */
    private Map<String,Lifecycle> lifecycleMap;
   
    /**
     * We use this to map all phases to the lifecycle that contains it. This is used so that a user can specify the
     * phase they want to execute and we can easily determine what lifecycle we need to run.
     */
    private Map<String, Lifecycle> phaseToLifecycleMap;

    /**
     * These mappings correspond to packaging types, like WAR packaging, which configure a particular mojos
     * to run in a given phase.
     */
    @Requirement
    private Map<String, LifecycleMapping> lifecycleMappings;

    private void fireEvent( MavenSession session, MojoExecution mojoExecution, LifecycleEventCatapult catapult )
    {
        ExecutionListener listener = session.getRequest().getExecutionListener();

        if ( listener != null )
        {
            ExecutionEvent event = new DefaultLifecycleEvent( session, mojoExecution );

            catapult.fire( listener, event );
        }
    }

    private static String getKey( MavenProject project )
    {
        return project.getGroupId() + ':' + project.getArtifactId() + ':' + project.getVersion();
    }

    private void debugReactorPlan( List<ProjectBuild> projectBuilds )
    {
        logger.debug( "=== REACTOR BUILD PLAN ================================================" );

        for ( Iterator<ProjectBuild> it = projectBuilds.iterator(); it.hasNext(); )
        {
            ProjectBuild projectBuild = it.next();

            logger.debug( "Project: " + projectBuild.project.getId() );
            logger.debug( "Tasks:   " + projectBuild.taskSegment.tasks );
            logger.debug( "Style:   " + ( projectBuild.taskSegment.aggregating ? "Aggregating" : "Regular" ) );

            if ( it.hasNext() )
            {
                logger.debug( "-----------------------------------------------------------------------" );
            }
        }

        logger.debug( "=======================================================================" );
    }

    private void debugProjectPlan( MavenProject currentProject, MavenExecutionPlan executionPlan )
    {
        logger.debug( "=== PROJECT BUILD PLAN ================================================" );
        logger.debug( "Project:       " + getKey( currentProject ) );
        logger.debug( "Dependencies (collect): " + executionPlan.getRequiredCollectionScopes() );
        logger.debug( "Dependencies (resolve): " + executionPlan.getRequiredResolutionScopes() );

        for ( MojoExecution mojoExecution : executionPlan.getExecutions() )
        {
            debugMojoExecution( mojoExecution );
        }

        logger.debug( "=======================================================================" );
    }

    private void debugMojoExecution( MojoExecution mojoExecution )
    {
        String mojoExecId =
            mojoExecution.getGroupId() + ':' + mojoExecution.getArtifactId() + ':' + mojoExecution.getVersion() + ':'
                + mojoExecution.getGoal() + " (" + mojoExecution.getExecutionId() + ')';

        Map<String, List<MojoExecution>> forkedExecutions = mojoExecution.getForkedExecutions();
        if ( !forkedExecutions.isEmpty() )
        {
            for ( Map.Entry<String, List<MojoExecution>> fork : forkedExecutions.entrySet() )
            {
                logger.debug( "--- init fork of " + fork.getKey() + " for " + mojoExecId + " ---" );

                for ( MojoExecution forkedExecution : fork.getValue() )
                {
                    debugMojoExecution( forkedExecution );
                }

                logger.debug( "--- exit fork of " + fork.getKey() + " for " + mojoExecId + " ---" );
            }
        }

        logger.debug( "-----------------------------------------------------------------------" );
        logger.debug( "Goal:          " + mojoExecId );
        logger.debug( "Style:         "
            + ( mojoExecution.getMojoDescriptor().isAggregating() ? "Aggregating" : "Regular" ) );
        logger.debug( "Configuration: " + mojoExecution.getConfiguration() );
    }

    public void execute( MavenSession session )
    {
        fireEvent( session, null, LifecycleEventCatapult.SESSION_STARTED );

        MavenExecutionResult result = session.getResult();

        List<ProjectBuild> projectBuilds;

        ProjectIndex projectIndex;

        try
        {
            projectBuilds = calculateProjectBuilds( session );

            projectIndex = new ProjectIndex( session.getProjects() );
        }
        catch ( Exception e )
        {
            result.addException( e );

            fireEvent( session, null, LifecycleEventCatapult.SESSION_ENDED );

            return;
        }

        if ( logger.isDebugEnabled() )
        {
            debugReactorPlan( projectBuilds );
        }

        ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader();

        for ( ProjectBuild projectBuild : projectBuilds )
        {
            MavenProject currentProject = projectBuild.project;

            long buildStartTime = System.currentTimeMillis();

            try
            {
                session.setCurrentProject( currentProject );

                if ( session.isBlackListed( currentProject ) )
                {
                    fireEvent( session, null, LifecycleEventCatapult.PROJECT_SKIPPED );

                    continue;
                }

                fireEvent( session, null, LifecycleEventCatapult.PROJECT_STARTED );

                ClassRealm projectRealm = currentProject.getClassRealm();
                if ( projectRealm != null )
                {
                    Thread.currentThread().setContextClassLoader( projectRealm );
                }

                MavenExecutionPlan executionPlan =
                    calculateExecutionPlan( session, currentProject, projectBuild.taskSegment );

                if ( logger.isDebugEnabled() )
                {
                    debugProjectPlan( currentProject, executionPlan );
                }

                // TODO: once we have calculated the build plan then we should accurately be able to download
                // the project dependencies. Having it happen in the plugin manager is a tangled mess. We can optimize
                // this later by looking at the build plan. Would be better to just batch download everything required
                // by the reactor.

                List<MavenProject> projectsToResolve;

                if ( projectBuild.taskSegment.aggregating )
                {
                    projectsToResolve = session.getProjects();
                }
                else
                {
                    projectsToResolve = Collections.singletonList( currentProject );
                }

                for ( MavenProject project : projectsToResolve )
                {
                    resolveProjectDependencies( project, executionPlan.getRequiredCollectionScopes(),
                                                executionPlan.getRequiredResolutionScopes(), session,
                                                projectBuild.taskSegment.aggregating );
                }

                DependencyContext dependencyContext =
                    new DependencyContext( executionPlan, projectBuild.taskSegment.aggregating );

                for ( MojoExecution mojoExecution : executionPlan.getExecutions() )
                {
                    execute( session, mojoExecution, projectIndex, dependencyContext );
                }

                long buildEndTime = System.currentTimeMillis();

                result.addBuildSummary( new BuildSuccess( currentProject, buildEndTime - buildStartTime ) );

                fireEvent( session, null, LifecycleEventCatapult.PROJECT_SUCCEEDED );
            }
            catch ( Exception e )
            {
                result.addException( e );

                long buildEndTime = System.currentTimeMillis();

                result.addBuildSummary( new BuildFailure( currentProject, buildEndTime - buildStartTime, e ) );

                fireEvent( session, null, LifecycleEventCatapult.PROJECT_FAILED );

                if ( MavenExecutionRequest.REACTOR_FAIL_NEVER.equals( session.getReactorFailureBehavior() ) )
                {
                    // continue the build
                }
                else if ( MavenExecutionRequest.REACTOR_FAIL_AT_END.equals( session.getReactorFailureBehavior() ) )
                {
                    // continue the build but ban all projects that depend on the failed one
                    session.blackList( currentProject );
                }
                else if ( MavenExecutionRequest.REACTOR_FAIL_FAST.equals( session.getReactorFailureBehavior() ) )
                {
                    // abort the build
                    break;
                }
                else
                {
                    throw new IllegalArgumentException( "invalid reactor failure behavior "
                        + session.getReactorFailureBehavior() );
                }
            }
            finally
            {
                session.setCurrentProject( null );

                Thread.currentThread().setContextClassLoader( oldContextClassLoader );
            }
        }

        fireEvent( session, null, LifecycleEventCatapult.SESSION_ENDED );
    }

    private void resolveProjectDependencies( MavenProject project, Collection<String> scopesToCollect,
                                             Collection<String> scopesToResolve, MavenSession session,
                                             boolean aggregating )
        throws LifecycleExecutionException
    {
        Set<Artifact> artifacts;

        try
        {
            try
            {
                artifacts = projectDependenciesResolver.resolve( project, scopesToCollect, scopesToResolve, session );
            }
            catch ( MultipleArtifactsNotFoundException e )
            {
                /*
                 * MNG-2277, the check below compensates for our bad plugin support where we ended up with aggregator
                 * plugins that require dependency resolution although they usually run in phases of the build where project
                 * artifacts haven't been assembled yet. The prime example of this is "mvn release:prepare".
                 */
                if ( aggregating && areAllArtifactsInReactor( session.getProjects(), e.getMissingArtifacts() ) )
                {
                    logger.warn( "The following artifacts could not be resolved at this point of the build"
                        + " but seem to be part of the reactor:" );

                    for ( Artifact artifact : e.getMissingArtifacts() )
                    {
                        logger.warn( "o " + artifact.getId() );
                    }

                    logger.warn( "Try running the build up to the lifecycle phase \"package\"" );

                    artifacts = new LinkedHashSet<Artifact>( e.getResolvedArtifacts() );
                }
                else
                {
                    throw e;
                }
            }
        }
        catch ( ArtifactResolutionException e )
        {
            throw new LifecycleExecutionException( null, project, e );
        }
        catch ( ArtifactNotFoundException e )
        {
            throw new LifecycleExecutionException( null, project, e );
        }

        project.setArtifacts( artifacts );

        if ( project.getDependencyArtifacts() == null )
        {
            Set<String> directDependencies = new HashSet<String>( project.getDependencies().size() * 2 );
            for ( Dependency dependency : project.getDependencies() )
            {
                directDependencies.add( dependency.getManagementKey() );
            }

            Set<Artifact> dependencyArtifacts = new LinkedHashSet<Artifact>( project.getDependencies().size() * 2 );
            for ( Artifact artifact : artifacts )
            {
                if ( directDependencies.contains( artifact.getDependencyConflictId() ) )
                {
                    dependencyArtifacts.add( artifact );
                }
            }
            project.setDependencyArtifacts( dependencyArtifacts );
        }
    }

    private boolean areAllArtifactsInReactor( Collection<MavenProject> projects, Collection<Artifact> artifacts )
    {
        Set<String> projectKeys = new HashSet<String>( projects.size() * 2 );
        for ( MavenProject project : projects )
        {
            String key = ArtifactUtils.key( project.getGroupId(), project.getArtifactId(), project.getVersion() );
            projectKeys.add( key );
        }

        for ( Artifact artifact : artifacts )
        {
            String key = ArtifactUtils.key( artifact );
            if ( !projectKeys.contains( key ) )
            {
                return false;
            }
        }

        return true;
    }

    private class DependencyContext
    {

        private final Collection<String> scopesToCollect;

        private final Collection<String> scopesToResolve;

        private final boolean aggregating;

        private MavenProject lastProject;

        private Collection<?> lastDependencyArtifacts;

        private int lastDependencyArtifactCount;

        DependencyContext( Collection<String> scopesToCollect, Collection<String> scopesToResolve, boolean aggregating )
        {
            this.scopesToCollect = scopesToCollect;
            this.scopesToResolve = scopesToResolve;
            this.aggregating = aggregating;
        }

        DependencyContext( MavenExecutionPlan executionPlan, boolean aggregating )
        {
            this.scopesToCollect = executionPlan.getRequiredCollectionScopes();
            this.scopesToResolve = executionPlan.getRequiredResolutionScopes();
            this.aggregating = aggregating;
        }

        DependencyContext( MojoExecution mojoExecution )
        {
            this.scopesToCollect = new TreeSet<String>();
            this.scopesToResolve = new TreeSet<String>();
            collectDependencyRequirements( scopesToResolve, scopesToCollect, mojoExecution );
            this.aggregating = mojoExecution.getMojoDescriptor().isAggregating();
        }

        public DependencyContext clone()
        {
            return new DependencyContext( scopesToCollect, scopesToResolve, aggregating );
        }

        void checkForUpdate( MavenSession session )
            throws LifecycleExecutionException
        {
            if ( lastProject == session.getCurrentProject() )
            {
                if ( lastDependencyArtifacts != lastProject.getDependencyArtifacts()
                    || ( lastDependencyArtifacts != null && lastDependencyArtifactCount != lastDependencyArtifacts.size() ) )
                {
                    logger.debug( "Re-resolving dependencies for project " + lastProject.getId()
                        + " to account for updates by previous goal execution" );
                    resolveProjectDependencies( lastProject, scopesToCollect, scopesToResolve, session, aggregating );
                }
            }

            lastProject = session.getCurrentProject();
            lastDependencyArtifacts = lastProject.getDependencyArtifacts();
            lastDependencyArtifactCount = ( lastDependencyArtifacts != null ) ? lastDependencyArtifacts.size() : 0;
        }
    }

    private void execute( MavenSession session, MojoExecution mojoExecution, ProjectIndex projectIndex,
                          DependencyContext dependencyContext )
        throws LifecycleExecutionException
    {
        MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor();

        if ( mojoDescriptor.isProjectRequired() && !session.isUsingPOMsFromFilesystem() )
        {
            Throwable cause =
                new IllegalStateException( "Goal requires a project to execute but there is no POM in this build." );
            throw new LifecycleExecutionException( mojoExecution, null, cause );
        }

        if ( mojoDescriptor.isOnlineRequired() && session.isOffline() )
        {
            if ( MojoExecution.Source.CLI.equals( mojoExecution.getSource() ) )
            {
                Throwable cause =
                    new IllegalStateException( "Goal requires online mode for execution"
                        + " but Maven is currently offline." );
                throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), cause );
            }
            else
            {
                fireEvent( session, mojoExecution, LifecycleEventCatapult.MOJO_SKIPPED );

                return;
            }
        }

        dependencyContext.checkForUpdate( session );

        List<MavenProject> forkedProjects =
            executeForkedExecutions( mojoExecution, session, projectIndex, dependencyContext );

        fireEvent( session, mojoExecution, LifecycleEventCatapult.MOJO_STARTED );

        try
        {
            try
            {
                pluginManager.executeMojo( session, mojoExecution );
            }
            catch ( MojoFailureException e )
            {
                throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), e );
            }
            catch ( MojoExecutionException e )
            {
                throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), e );
            }
            catch ( PluginConfigurationException e )
            {
                throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), e );
            }
            catch ( PluginManagerException e )
            {
                throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), e );
            }

            fireEvent( session, mojoExecution, LifecycleEventCatapult.MOJO_SUCCEEDED );
        }
        catch ( LifecycleExecutionException e )
        {
            fireEvent( session, mojoExecution, LifecycleEventCatapult.MOJO_FAILED );

            throw e;
        }
        finally
        {
            for ( MavenProject forkedProject : forkedProjects )
            {
                forkedProject.setExecutionProject( null );
            }
        }
    }

    public List<MavenProject> executeForkedExecutions( MojoExecution mojoExecution, MavenSession session )
        throws LifecycleExecutionException
    {
        return executeForkedExecutions( mojoExecution, session, new ProjectIndex( session.getProjects() ),
                                        new DependencyContext( mojoExecution ) );
    }

    private List<MavenProject> executeForkedExecutions( MojoExecution mojoExecution, MavenSession session,
                                                        ProjectIndex projectIndex, DependencyContext dependencyContext )
        throws LifecycleExecutionException
    {
        List<MavenProject> forkedProjects = Collections.emptyList();

        Map<String, List<MojoExecution>> forkedExecutions = mojoExecution.getForkedExecutions();

        if ( !forkedExecutions.isEmpty() )
        {
            fireEvent( session, mojoExecution, LifecycleEventCatapult.FORK_STARTED );

            MavenProject project = session.getCurrentProject();

            forkedProjects = new ArrayList<MavenProject>( forkedExecutions.size() );

            dependencyContext = dependencyContext.clone();

            try
            {
                for ( Map.Entry<String, List<MojoExecution>> fork : forkedExecutions.entrySet() )
                {
                    int index = projectIndex.indices.get( fork.getKey() );

                    MavenProject forkedProject = projectIndex.projects.get( fork.getKey() );

                    forkedProjects.add( forkedProject );

                    MavenProject executedProject = forkedProject.clone();

                    forkedProject.setExecutionProject( executedProject );

                    try
                    {
                        session.setCurrentProject( executedProject );
                        session.getProjects().set( index, executedProject );
                        projectIndex.projects.put( fork.getKey(), executedProject );

                        for ( MojoExecution forkedExecution : fork.getValue() )
                        {
                            execute( session, forkedExecution, projectIndex, dependencyContext );
                        }
                    }
                    finally
                    {
                        projectIndex.projects.put( fork.getKey(), forkedProject );
                        session.getProjects().set( index, forkedProject );
                        session.setCurrentProject( project );
                    }
                }

                fireEvent( session, mojoExecution, LifecycleEventCatapult.FORK_SUCCEEDED );
            }
            catch ( LifecycleExecutionException e )
            {
                fireEvent( session, mojoExecution, LifecycleEventCatapult.FORK_FAILED );

                throw e;
            }
        }

        return forkedProjects;
    }

    private static final class ProjectIndex
    {

        Map<String, MavenProject> projects;

        Map<String, Integer> indices;

        ProjectIndex( List<MavenProject> projects )
        {
            this.projects = new HashMap<String, MavenProject>( projects.size() * 2 );
            this.indices = new HashMap<String, Integer>( projects.size() * 2 );

            for ( int i = 0; i < projects.size(); i++ )
            {
                MavenProject project = projects.get( i );
                String key = getKey( project );

                this.projects.put( key, project );
                this.indices.put( key, Integer.valueOf( i ) );
            }
        }

    }

    private List<ProjectBuild> calculateProjectBuilds( MavenSession session )
        throws PluginNotFoundException, PluginResolutionException, PluginDescriptorParsingException,
        MojoNotFoundException, NoPluginFoundForPrefixException, InvalidPluginDescriptorException,
        PluginVersionResolutionException
    {
        List<ProjectBuild> projectBuilds = new ArrayList<ProjectBuild>();

        MavenProject rootProject = session.getTopLevelProject();

        List<String> tasks = session.getGoals();

        if ( tasks == null || tasks.isEmpty() )
        {
            if ( !StringUtils.isEmpty( rootProject.getDefaultGoal() ) )
            {
                tasks = Collections.singletonList( rootProject.getDefaultGoal() );
            }
        }

        List<TaskSegment> taskSegments = calculateTaskSegments( session, tasks );

        for ( TaskSegment taskSegment : taskSegments )
        {
            List<MavenProject> projects;

            if ( taskSegment.aggregating )
            {
                projects = Collections.singletonList( rootProject );
            }
            else
            {
                projects = session.getProjects();
            }

            for ( MavenProject project : projects )
            {
                projectBuilds.add( new ProjectBuild( project, taskSegment ) );
            }
        }

        return projectBuilds;
    }

    private MavenExecutionPlan calculateExecutionPlan( MavenSession session, MavenProject project,
                                                       TaskSegment taskSegment )
        throws PluginNotFoundException, PluginResolutionException, LifecyclePhaseNotFoundException,
        PluginDescriptorParsingException, MojoNotFoundException, InvalidPluginDescriptorException,
        NoPluginFoundForPrefixException, LifecycleNotFoundException, PluginVersionResolutionException
    {
        resolveMissingPluginVersions( project, session );

        List<MojoExecution> mojoExecutions = new ArrayList<MojoExecution>();

        Set<String> requiredDependencyResolutionScopes = new TreeSet<String>();

        Set<String> requiredDependencyCollectionScopes = new TreeSet<String>();

        for ( Object task : taskSegment.tasks )
        {
            if ( task instanceof GoalTask )
            {
                MojoDescriptor mojoDescriptor = ( (GoalTask) task ).mojoDescriptor;

                MojoExecution mojoExecution =
                    new MojoExecution( mojoDescriptor, "default-cli", MojoExecution.Source.CLI );

                mojoExecutions.add( mojoExecution );
            }
            else if ( task instanceof LifecycleTask )
            {
                String lifecyclePhase = ( (LifecycleTask) task ).lifecyclePhase;

                Map<String, List<MojoExecution>> phaseToMojoMapping =
                    calculateLifecycleMappings( session, project, lifecyclePhase );

                for ( List<MojoExecution> mojoExecutionsFromLifecycle : phaseToMojoMapping.values() )
                {
                    mojoExecutions.addAll( mojoExecutionsFromLifecycle );
                }
            }
            else
            {
                throw new IllegalStateException( "unexpected task " + task );
            }
        }

        for ( MojoExecution mojoExecution : mojoExecutions )
        {
            MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor();

            if ( mojoDescriptor == null )
            {
                mojoDescriptor =
                    pluginManager.getMojoDescriptor( mojoExecution.getPlugin(), mojoExecution.getGoal(),
                                                     getRepositoryRequest( session, project ) );

                mojoExecution.setMojoDescriptor( mojoDescriptor );
            }

            populateMojoExecutionConfiguration( project, mojoExecution,
                                                MojoExecution.Source.CLI.equals( mojoExecution.getSource() ) );

            finalizeMojoConfiguration( mojoExecution );

            calculateForkedExecutions( mojoExecution, session, project, new HashSet<MojoDescriptor>() );

            collectDependencyRequirements( requiredDependencyResolutionScopes, requiredDependencyCollectionScopes,
                                           mojoExecution );
        }

        return new MavenExecutionPlan( mojoExecutions, requiredDependencyResolutionScopes,
                                       requiredDependencyCollectionScopes );
    }

    private List<TaskSegment> calculateTaskSegments( MavenSession session, List<String> tasks )
        throws PluginNotFoundException, PluginResolutionException, PluginDescriptorParsingException,
        MojoNotFoundException, NoPluginFoundForPrefixException, InvalidPluginDescriptorException,
        PluginVersionResolutionException
    {
        List<TaskSegment> taskSegments = new ArrayList<TaskSegment>( tasks.size() );

        TaskSegment currentSegment = null;

        for ( String task : tasks )
        {
            if ( isGoalSpecification( task ) )
            {
                // "pluginPrefix:goal" or "groupId:artifactId[:version]:goal"

                MojoDescriptor mojoDescriptor = getMojoDescriptor( task, session, session.getTopLevelProject() );

                boolean aggregating = mojoDescriptor.isAggregating();

                if ( currentSegment == null || currentSegment.aggregating != aggregating )
                {
                    currentSegment = new TaskSegment( aggregating );
                    taskSegments.add( currentSegment );
                }

                currentSegment.tasks.add( new GoalTask( mojoDescriptor ) );
            }
            else
            {
                // lifecycle phase

                if ( currentSegment == null || currentSegment.aggregating )
                {
                    currentSegment = new TaskSegment( false );
                    taskSegments.add( currentSegment );
                }

                currentSegment.tasks.add( new LifecycleTask( task ) );
            }
        }

        return taskSegments;
    }

    private boolean isGoalSpecification( String task )
    {
        return task.indexOf( ':' ) >= 0;
    }

    private static final class ProjectBuild
    {

        final MavenProject project;

        final TaskSegment taskSegment;

        ProjectBuild( MavenProject project, TaskSegment taskSegment )
        {
            this.project = project;
            this.taskSegment = taskSegment;
        }

        @Override
        public String toString()
        {
            return project.getId() + " -> " + taskSegment;
        }

    }

    private static final class TaskSegment
    {

        final List<Object> tasks;

        final boolean aggregating;

        TaskSegment( boolean aggregating )
        {
            this.aggregating = aggregating;
            tasks = new ArrayList<Object>();
        }

        @Override
        public String toString()
        {
            return tasks.toString();
        }

    }

    private static final class GoalTask
    {

        final MojoDescriptor mojoDescriptor;

        GoalTask( MojoDescriptor mojoDescriptor )
        {
            this.mojoDescriptor = mojoDescriptor;
        }

        @Override
        public String toString()
        {
            return mojoDescriptor.getId();
        }

    }

    private static final class LifecycleTask
    {

        final String lifecyclePhase;

        LifecycleTask( String lifecyclePhase )
        {
            this.lifecyclePhase = lifecyclePhase;
        }

        @Override
        public String toString()
        {
            return lifecyclePhase;
        }

    }

    public MavenExecutionPlan calculateExecutionPlan( MavenSession session, String... tasks )
        throws PluginNotFoundException, PluginResolutionException, PluginDescriptorParsingException,
        MojoNotFoundException, NoPluginFoundForPrefixException, InvalidPluginDescriptorException,
        PluginManagerException, LifecyclePhaseNotFoundException, LifecycleNotFoundException,
        PluginVersionResolutionException
    {
        List<TaskSegment> taskSegments = calculateTaskSegments( session, Arrays.asList( tasks ) );

        TaskSegment mergedSegment = new TaskSegment( false );

        for ( TaskSegment taskSegment : taskSegments )
        {
            mergedSegment.tasks.addAll( taskSegment.tasks );
        }

        return calculateExecutionPlan( session, session.getCurrentProject(), mergedSegment );
    }

    private RepositoryRequest getRepositoryRequest( MavenSession session, MavenProject project )
    {
        RepositoryRequest request = new DefaultRepositoryRequest();

        request.setCache( session.getRepositoryCache() );
        request.setLocalRepository( session.getLocalRepository() );
        if ( project != null )
        {
            request.setRemoteRepositories( project.getPluginArtifactRepositories() );
        }
        request.setOffline( session.isOffline() );
        request.setTransferListener( session.getRequest().getTransferListener() );

        return request;
    }

    private void collectDependencyRequirements( Collection<String> requiredDependencyResolutionScopes,
                                                    Collection<String> requiredDependencyCollectionScopes,
                                                    MojoExecution mojoExecution )
    {
        MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor();

        String requiredDependencyResolutionScope = mojoDescriptor.getDependencyResolutionRequired();

        if ( StringUtils.isNotEmpty( requiredDependencyResolutionScope ) )
        {
            requiredDependencyResolutionScopes.add( requiredDependencyResolutionScope );
        }

        String requiredDependencyCollectionScope = mojoDescriptor.getDependencyCollectionRequired();

        if ( StringUtils.isNotEmpty( requiredDependencyCollectionScope ) )
        {
            requiredDependencyCollectionScopes.add( requiredDependencyCollectionScope );
        }

        for ( List<MojoExecution> forkedExecutions : mojoExecution.getForkedExecutions().values() )
        {
            for ( MojoExecution forkedExecution : forkedExecutions )
            {
                collectDependencyRequirements( requiredDependencyResolutionScopes,
                                                   requiredDependencyCollectionScopes, forkedExecution );
            }
        }
    }

    private Map<String, List<MojoExecution>> calculateLifecycleMappings( MavenSession session, MavenProject project,
                                                                         String lifecyclePhase )
        throws LifecyclePhaseNotFoundException, PluginNotFoundException, PluginResolutionException,
        PluginDescriptorParsingException, MojoNotFoundException, InvalidPluginDescriptorException
    {
        /*
         * Determine the lifecycle that corresponds to the given phase.
         */

        Lifecycle lifecycle = phaseToLifecycleMap.get( lifecyclePhase );

        if ( lifecycle == null )
        {
            throw new LifecyclePhaseNotFoundException( lifecyclePhase );
        }

        /*
         * Initialize mapping from lifecycle phase to bound mojos. The key set of this map denotes the phases the caller
         * is interested in, i.e. all phases up to and including the specified phase.
         */

        Map<String, Map<Integer, List<MojoExecution>>> mappings =
            new LinkedHashMap<String, Map<Integer, List<MojoExecution>>>();

        for ( String phase : lifecycle.getPhases() )
        {
            Map<Integer, List<MojoExecution>> phaseBindings = new TreeMap<Integer, List<MojoExecution>>();

            mappings.put( phase, phaseBindings );

            if ( phase.equals( lifecyclePhase ) )
            {
                break;
            }
        }

        /*
         * Grab plugin executions that are bound to the selected lifecycle phases from project. The effective model of
         * the project already contains the plugin executions induced by the project's packaging type. Remember, all
         * phases of interest and only those are in the lifecyle mapping, if a phase has no value in the map, we are not
         * interested in any of the executions bound to it.
         */

        for ( Plugin plugin : project.getBuild().getPlugins() )
        {
            for ( PluginExecution execution : plugin.getExecutions() )
            {
                // if the phase is specified then I don't have to go fetch the plugin yet and pull it down
                // to examine the phase it is associated to.
                if ( execution.getPhase() != null )
                {
                    Map<Integer, List<MojoExecution>> phaseBindings = mappings.get( execution.getPhase() );
                    if ( phaseBindings != null )
                    {
                        for ( String goal : execution.getGoals() )
                        {
                            MojoExecution mojoExecution = new MojoExecution( plugin, goal, execution.getId() );
                            addMojoExecution( phaseBindings, mojoExecution, execution.getPriority() );
                        }
                    }
                }
                // if not then i need to grab the mojo descriptor and look at the phase that is specified
                else
                {
                    for ( String goal : execution.getGoals() )
                    {
                        MojoDescriptor mojoDescriptor =
                            pluginManager.getMojoDescriptor( plugin, goal, getRepositoryRequest( session, project ) );

                        Map<Integer, List<MojoExecution>> phaseBindings = mappings.get( mojoDescriptor.getPhase() );
                        if ( phaseBindings != null )
                        {
                            MojoExecution mojoExecution = new MojoExecution( mojoDescriptor, execution.getId() );
                            addMojoExecution( phaseBindings, mojoExecution, execution.getPriority() );
                        }
                    }
                }
            }
        }

        Map<String, List<MojoExecution>> lifecycleMappings = new LinkedHashMap<String, List<MojoExecution>>();

        for ( Map.Entry<String, Map<Integer, List<MojoExecution>>> entry : mappings.entrySet() )
        {
            List<MojoExecution> mojoExecutions = new ArrayList<MojoExecution>();

            for ( List<MojoExecution> executions : entry.getValue().values() )
            {
                mojoExecutions.addAll( executions );
            }

            lifecycleMappings.put( entry.getKey(), mojoExecutions );
        }

        return lifecycleMappings;
    }

    private void addMojoExecution( Map<Integer, List<MojoExecution>> phaseBindings, MojoExecution mojoExecution,
                                   int priority )
    {
        List<MojoExecution> mojoExecutions = phaseBindings.get( priority );

        if ( mojoExecutions == null )
        {
            mojoExecutions = new ArrayList<MojoExecution>();
            phaseBindings.put( priority, mojoExecutions );
        }

        mojoExecutions.add( mojoExecution );
    }

    public void calculateForkedExecutions( MojoExecution mojoExecution, MavenSession session )
        throws MojoNotFoundException, PluginNotFoundException, PluginResolutionException,
        PluginDescriptorParsingException, NoPluginFoundForPrefixException, InvalidPluginDescriptorException,
        LifecyclePhaseNotFoundException, LifecycleNotFoundException, PluginVersionResolutionException
    {
        calculateForkedExecutions( mojoExecution, session, session.getCurrentProject(), new HashSet<MojoDescriptor>() );
    }

    private void calculateForkedExecutions( MojoExecution mojoExecution, MavenSession session, MavenProject project,
                                            Collection<MojoDescriptor> alreadyForkedExecutions )
        throws MojoNotFoundException, PluginNotFoundException, PluginResolutionException,
        PluginDescriptorParsingException, NoPluginFoundForPrefixException, InvalidPluginDescriptorException,
        LifecyclePhaseNotFoundException, LifecycleNotFoundException, PluginVersionResolutionException
    {
        MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor();

        if ( !mojoDescriptor.isForking() )
        {
            return;
        }

        if ( !alreadyForkedExecutions.add( mojoDescriptor ) )
        {
            return;
        }

        List<MavenProject> forkedProjects;

        if ( mojoDescriptor.isAggregating() )
        {
            forkedProjects = session.getProjects();
        }
        else
        {
            forkedProjects = Collections.singletonList( project );
        }

        for ( MavenProject forkedProject : forkedProjects )
        {
            List<MojoExecution> forkedExecutions;

            if ( StringUtils.isNotEmpty( mojoDescriptor.getExecutePhase() ) )
            {
                forkedExecutions =
                    calculateForkedLifecycle( mojoExecution, session, forkedProject, alreadyForkedExecutions );
            }
            else
            {
                forkedExecutions = calculateForkedGoal( mojoExecution, session, forkedProject, alreadyForkedExecutions );
            }

            mojoExecution.setForkedExecutions( getKey( forkedProject ), forkedExecutions );
        }

        alreadyForkedExecutions.remove( mojoDescriptor );
    }

    private List<MojoExecution> calculateForkedGoal( MojoExecution mojoExecution, MavenSession session,
                                                     MavenProject project,
                                                     Collection<MojoDescriptor> alreadyForkedExecutions )
        throws MojoNotFoundException, PluginNotFoundException, PluginResolutionException,
        PluginDescriptorParsingException, NoPluginFoundForPrefixException, InvalidPluginDescriptorException,
        LifecyclePhaseNotFoundException, LifecycleNotFoundException, PluginVersionResolutionException
    {
        MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor();

        PluginDescriptor pluginDescriptor = mojoDescriptor.getPluginDescriptor();

        String forkedGoal = mojoDescriptor.getExecuteGoal();

        MojoDescriptor forkedMojoDescriptor = pluginDescriptor.getMojo( forkedGoal );
        if ( forkedMojoDescriptor == null )
        {
            throw new MojoNotFoundException( forkedGoal, pluginDescriptor );
        }

        if ( alreadyForkedExecutions.contains( forkedMojoDescriptor ) )
        {
            return Collections.emptyList();
        }

        MojoExecution forkedExecution = new MojoExecution( forkedMojoDescriptor, forkedGoal );

        populateMojoExecutionConfiguration( project, forkedExecution, true );

        finalizeMojoConfiguration( forkedExecution );

        calculateForkedExecutions( forkedExecution, session, project, alreadyForkedExecutions );

        return Collections.singletonList( forkedExecution );
    }

    private List<MojoExecution> calculateForkedLifecycle( MojoExecution mojoExecution, MavenSession session,
                                                          MavenProject project,
                                                          Collection<MojoDescriptor> alreadyForkedExecutions )
        throws MojoNotFoundException, PluginNotFoundException, PluginResolutionException,
        PluginDescriptorParsingException, NoPluginFoundForPrefixException, InvalidPluginDescriptorException,
        LifecyclePhaseNotFoundException, LifecycleNotFoundException, PluginVersionResolutionException
    {
        MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor();

        String forkedPhase = mojoDescriptor.getExecutePhase();

        Map<String, List<MojoExecution>> lifecycleMappings = calculateLifecycleMappings( session, project, forkedPhase );

        for ( List<MojoExecution> forkedExecutions : lifecycleMappings.values() )
        {
            for ( MojoExecution forkedExecution : forkedExecutions )
            {
                if ( forkedExecution.getMojoDescriptor() == null )
                {
                    MojoDescriptor forkedMojoDescriptor =
                        pluginManager.getMojoDescriptor( forkedExecution.getPlugin(), forkedExecution.getGoal(),
                                                         getRepositoryRequest( session, project ) );

                    forkedExecution.setMojoDescriptor( forkedMojoDescriptor );
                }

                populateMojoExecutionConfiguration( project, forkedExecution, false );
            }
        }

        injectLifecycleOverlay( lifecycleMappings, mojoExecution, session, project );

        List<MojoExecution> mojoExecutions = new ArrayList<MojoExecution>();

        for ( List<MojoExecution> forkedExecutions : lifecycleMappings.values() )
        {
            for ( MojoExecution forkedExecution : forkedExecutions )
            {
                if ( !alreadyForkedExecutions.contains( forkedExecution.getMojoDescriptor() ) )
                {
                    finalizeMojoConfiguration( forkedExecution );

                    calculateForkedExecutions( forkedExecution, session, project, alreadyForkedExecutions );

                    mojoExecutions.add( forkedExecution );
                }
            }
        }

        return mojoExecutions;
    }

    private void injectLifecycleOverlay( Map<String, List<MojoExecution>> lifecycleMappings,
                                         MojoExecution mojoExecution, MavenSession session, MavenProject project )
        throws PluginDescriptorParsingException, LifecycleNotFoundException, MojoNotFoundException,
        PluginNotFoundException, PluginResolutionException, NoPluginFoundForPrefixException,
        InvalidPluginDescriptorException, PluginVersionResolutionException
    {
        MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor();

        PluginDescriptor pluginDescriptor = mojoDescriptor.getPluginDescriptor();

        String forkedLifecycle = mojoDescriptor.getExecuteLifecycle();

        if ( StringUtils.isEmpty( forkedLifecycle ) )
        {
            return;
        }

        org.apache.maven.plugin.lifecycle.Lifecycle lifecycleOverlay;

        try
        {
            lifecycleOverlay = pluginDescriptor.getLifecycleMapping( forkedLifecycle );
        }
        catch ( IOException e )
        {
            throw new PluginDescriptorParsingException( pluginDescriptor.getPlugin(), pluginDescriptor.getSource(), e );
        }
        catch ( XmlPullParserException e )
        {
            throw new PluginDescriptorParsingException( pluginDescriptor.getPlugin(), pluginDescriptor.getSource(), e );
        }

        if ( lifecycleOverlay == null )
        {
            throw new LifecycleNotFoundException( forkedLifecycle );
        }

        for ( Phase phase : lifecycleOverlay.getPhases() )
        {
            List<MojoExecution> forkedExecutions = lifecycleMappings.get( phase.getId() );

            if ( forkedExecutions != null )
            {
                for ( Execution execution : phase.getExecutions() )
                {
                    for ( String goal : execution.getGoals() )
                    {
                        MojoDescriptor forkedMojoDescriptor;

                        if ( goal.indexOf( ':' ) < 0 )
                        {
                            forkedMojoDescriptor = pluginDescriptor.getMojo( goal );
                            if ( forkedMojoDescriptor == null )
                            {
                                throw new MojoNotFoundException( goal, pluginDescriptor );
                            }
                        }
                        else
                        {
                            forkedMojoDescriptor = getMojoDescriptor( goal, session, project );
                        }

                        MojoExecution forkedExecution =
                            new MojoExecution( forkedMojoDescriptor, mojoExecution.getExecutionId() );

                        Xpp3Dom forkedConfiguration = (Xpp3Dom) execution.getConfiguration();

                        forkedExecution.setConfiguration( forkedConfiguration );

                        populateMojoExecutionConfiguration( project, forkedExecution, true );

                        forkedExecutions.add( forkedExecution );
                    }
                }

                Xpp3Dom phaseConfiguration = (Xpp3Dom) phase.getConfiguration();

                if ( phaseConfiguration != null )
                {
                    for ( MojoExecution forkedExecution : forkedExecutions )
                    {
                        Xpp3Dom forkedConfiguration = forkedExecution.getConfiguration();

                        forkedConfiguration = Xpp3Dom.mergeXpp3Dom( phaseConfiguration, forkedConfiguration );

                        forkedExecution.setConfiguration( forkedConfiguration );
                    }
                }
            }
        }
    }

    private void populateMojoExecutionConfiguration( MavenProject project, MojoExecution mojoExecution,
                                                     boolean allowPluginLevelConfig )
    {
        String g = mojoExecution.getGroupId();

        String a = mojoExecution.getArtifactId();

        Plugin plugin = findPlugin( g, a, project.getBuildPlugins() );

        if ( plugin == null && project.getPluginManagement() != null )
        {
            plugin = findPlugin( g, a, project.getPluginManagement().getPlugins() );
        }

        if ( plugin != null )
        {
            PluginExecution pluginExecution =
                findPluginExecution( mojoExecution.getExecutionId(), plugin.getExecutions() );

            Xpp3Dom pomConfiguration = null;

            if ( pluginExecution != null )
            {
                pomConfiguration = (Xpp3Dom) pluginExecution.getConfiguration();
            }
            else if ( allowPluginLevelConfig )
            {
                pomConfiguration = (Xpp3Dom) plugin.getConfiguration();
            }

            Xpp3Dom mojoConfiguration = ( pomConfiguration != null ) ? new Xpp3Dom( pomConfiguration ) : null;

            mojoConfiguration = Xpp3Dom.mergeXpp3Dom( mojoExecution.getConfiguration(), mojoConfiguration );

            mojoExecution.setConfiguration( mojoConfiguration );
        }
    }

    /**
     * Post-processes the effective configuration for the specified mojo execution. This step discards all parameters
     * from the configuration that are not applicable to the mojo and injects the default values for any missing
     * parameters.
     *
     * @param mojoExecution The mojo execution whose configuration should be finalized, must not be {@code null}.
     */
    private void finalizeMojoConfiguration( MojoExecution mojoExecution )
    {
        MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor();

        Xpp3Dom executionConfiguration = mojoExecution.getConfiguration();
        if ( executionConfiguration == null )
        {
            executionConfiguration = new Xpp3Dom( "configuration" );
        }

        Xpp3Dom defaultConfiguration = getMojoConfiguration( mojoDescriptor );

        Xpp3Dom finalConfiguration = new Xpp3Dom( "configuration" );

        if ( mojoDescriptor.getParameters() != null )
        {
            for ( Parameter parameter : mojoDescriptor.getParameters() )
            {
                Xpp3Dom parameterConfiguration = executionConfiguration.getChild( parameter.getName() );

                if ( parameterConfiguration == null )
                {
                    parameterConfiguration = executionConfiguration.getChild( parameter.getAlias() );
                }

                Xpp3Dom parameterDefaults = defaultConfiguration.getChild( parameter.getName() );

                parameterConfiguration = Xpp3Dom.mergeXpp3Dom( parameterConfiguration, parameterDefaults, Boolean.TRUE );

                if ( parameterConfiguration != null )
                {
                    parameterConfiguration = new Xpp3Dom( parameterConfiguration, parameter.getName() );

                    if ( StringUtils.isEmpty( parameterConfiguration.getAttribute( "implementation" ) )
                        && StringUtils.isNotEmpty( parameter.getImplementation() ) )
                    {
                        parameterConfiguration.setAttribute( "implementation", parameter.getImplementation() );
                    }

                    finalConfiguration.addChild( parameterConfiguration );
                }
            }
        }

        mojoExecution.setConfiguration( finalConfiguration );
    }

    // org.apache.maven.plugins:maven-remote-resources-plugin:1.0:process
    MojoDescriptor getMojoDescriptor( String task, MavenSession session, MavenProject project )
        throws PluginNotFoundException, PluginResolutionException, PluginDescriptorParsingException, MojoNotFoundException, NoPluginFoundForPrefixException, InvalidPluginDescriptorException, PluginVersionResolutionException
    {       
        String goal = null;
       
        Plugin plugin = null;

        StringTokenizer tok = new StringTokenizer( task, ":" );
       
        int numTokens = tok.countTokens();
       
        if ( numTokens == 4 )
        {
            // We have everything that we need
            //
            // org.apache.maven.plugins:maven-remote-resources-plugin:1.0:process
            //
            // groupId
            // artifactId
            // version
            // goal
            //
            plugin = new Plugin();
            plugin.setGroupId( tok.nextToken() );
            plugin.setArtifactId( tok.nextToken() );
            plugin.setVersion( tok.nextToken() );
            goal = tok.nextToken();
           
        }
        else if ( numTokens == 3 )
        {
            // We have everything that we need except the version
            //
            // org.apache.maven.plugins:maven-remote-resources-plugin:???:process
            //
            // groupId
            // artifactId
            // ???
            // goal
            //
            plugin = new Plugin();
            plugin.setGroupId( tok.nextToken() );
            plugin.setArtifactId( tok.nextToken() );
            goal = tok.nextToken();
        }
        else if ( numTokens == 2 )
        {
            // We have a prefix and goal
            //
            // idea:idea
            //
            String prefix = tok.nextToken();
            goal = tok.nextToken();

            // This is the case where someone has executed a single goal from the command line
            // of the form:
            //
            // mvn remote-resources:process
            //
            // From the metadata stored on the server which has been created as part of a standard
            // Maven plugin deployment we will find the right PluginDescriptor from the remote
            // repository.
           
            plugin = findPluginForPrefix( prefix, session );
        }

        injectPluginDeclarationFromProject( plugin, project );

        RepositoryRequest repositoryRequest = getRepositoryRequest( session, project );

        // If there is no version to be found then we need to look in the repository metadata for
        // this plugin and see what's specified as the latest release.
        //
        if ( plugin.getVersion() == null )
        {
            resolvePluginVersion( plugin, repositoryRequest );
        }

        return pluginManager.getMojoDescriptor( plugin, goal, repositoryRequest );
    }

    private void resolvePluginVersion( Plugin plugin, RepositoryRequest repositoryRequest )
        throws PluginVersionResolutionException
    {
        PluginVersionRequest versionRequest = new DefaultPluginVersionRequest( plugin, repositoryRequest );
        plugin.setVersion( pluginVersionResolver.resolve( versionRequest ).getVersion() );
    }

    private void injectPluginDeclarationFromProject( Plugin plugin, MavenProject project )
    {
        Plugin pluginInPom = findPlugin( plugin, project.getBuildPlugins() );

        if ( pluginInPom == null && project.getPluginManagement() != null )
        {
            pluginInPom = findPlugin( plugin, project.getPluginManagement().getPlugins() );
        }

        if ( pluginInPom != null )
        {
            if ( plugin.getVersion() == null )
            {
                plugin.setVersion( pluginInPom.getVersion() );
            }

            plugin.setDependencies( new ArrayList<Dependency>( pluginInPom.getDependencies() ) );
        }
    }

    private Plugin findPlugin( Plugin plugin, Collection<Plugin> plugins )
    {
        return findPlugin( plugin.getGroupId(), plugin.getArtifactId(), plugins );
    }

    private Plugin findPlugin( String groupId, String artifactId, Collection<Plugin> plugins )
    {
        for ( Plugin plugin : plugins )
        {
            if ( artifactId.equals( plugin.getArtifactId() ) && groupId.equals( plugin.getGroupId() ) )
            {
                return plugin;
            }
        }

        return null;
    }

    private PluginExecution findPluginExecution( String executionId, Collection<PluginExecution> executions )
    {
        if ( StringUtils.isNotEmpty( executionId ) )
        {
            for ( PluginExecution execution : executions )
            {
                if ( executionId.equals( execution.getId() ) )
                {
                    return execution;
                }
            }
        }

        return null;
    }

    public void initialize()
        throws InitializationException
    {
        lifecycleMap = new HashMap<String,Lifecycle>();
       
        // If people are going to make their own lifecycles then we need to tell people how to namespace them correctly so
        // that they don't interfere with internally defined lifecycles.

        phaseToLifecycleMap = new HashMap<String,Lifecycle>();

        for ( Lifecycle lifecycle : lifecycles )
        {                       
            for ( String phase : lifecycle.getPhases() )
            {               
                // The first definition wins.
                if ( !phaseToLifecycleMap.containsKey( phase ) )
                {
                    phaseToLifecycleMap.put( phase, lifecycle );
                }
            }
           
            lifecycleMap.put( lifecycle.getId(), lifecycle );
        }
    }
       
    // These methods deal with construction intact Plugin object that look like they come from a standard
    // <plugin/> block in a Maven POM. We have to do some wiggling to pull the sources of information
    // together and this really shows the problem of constructing a sensible default configuration but
    // it's all encapsulated here so it appears normalized to the POM builder.
   
    // We are going to take the project packaging and find all plugin in the default lifecycle and create
    // fully populated Plugin objects, including executions with goals and default configuration taken
    // from the plugin.xml inside a plugin.
    //
    public Set<Plugin> getPluginsBoundByDefaultToAllLifecycles( String packaging )
    {
        LifecycleMapping lifecycleMappingForPackaging = lifecycleMappings.get( packaging );

        if ( lifecycleMappingForPackaging == null )
        {
            return null;
        }

        Map<Plugin, Plugin> plugins = new LinkedHashMap<Plugin, Plugin>();

        for ( Lifecycle lifecycle : lifecycles )
        {
            org.apache.maven.lifecycle.mapping.Lifecycle lifecycleConfiguration =
                lifecycleMappingForPackaging.getLifecycles().get( lifecycle.getId() );

            Map<String, String> phaseToGoalMapping = null;

            if ( lifecycleConfiguration != null )
            {
                phaseToGoalMapping = lifecycleConfiguration.getPhases();
            }
            else if ( lifecycle.getDefaultPhases() != null )
            {
                phaseToGoalMapping = lifecycle.getDefaultPhases();
            }
           
            if ( phaseToGoalMapping != null )
            {
                // These are of the form:
                //
                // compile -> org.apache.maven.plugins:maven-compiler-plugin:compile[,gid:aid:goal,...]
                //
                for ( Map.Entry<String, String> goalsForLifecyclePhase : phaseToGoalMapping.entrySet() )
                {
                    String phase = goalsForLifecyclePhase.getKey();
                    String goals = goalsForLifecyclePhase.getValue();
                    if ( goals != null )
                    {
                        parseLifecyclePhaseDefinitions( plugins, phase, goals );
                    }
                }
            }
        }

        return plugins.keySet();
    }

    private void parseLifecyclePhaseDefinitions( Map<Plugin, Plugin> plugins, String phase, String goals )
    {
        String[] mojos = StringUtils.split( goals, "," );

        for ( int i = 0; i < mojos.length; i++ )
        {
            // either <groupId>:<artifactId>:<goal> or <groupId>:<artifactId>:<version>:<goal>
            String goal = mojos[i].trim();
            String[] p = StringUtils.split( goal, ":" );

            PluginExecution execution = new PluginExecution();
            execution.setId( "default-" + p[p.length - 1] );
            execution.setPhase( phase );
            execution.setPriority( i - mojos.length );
            execution.getGoals().add( p[p.length - 1] );

            Plugin plugin = new Plugin();
            plugin.setGroupId( p[0] );
            plugin.setArtifactId( p[1] );
            if ( p.length >= 4 )
            {
                plugin.setVersion( p[2] );
            }

            Plugin existing = plugins.get( plugin );
            if ( existing != null )
            {
                plugin = existing;
            }
            else
            {
                plugins.put( plugin, plugin );
            }

            plugin.getExecutions().add( execution );
        }
    }

    private void resolveMissingPluginVersions( MavenProject project, MavenSession session )
        throws PluginVersionResolutionException
    {
        for ( Plugin plugin : project.getBuildPlugins() )
        {
            if ( plugin.getVersion() == null )
            {
                PluginVersionRequest request = new DefaultPluginVersionRequest( plugin, session );
                plugin.setVersion( pluginVersionResolver.resolve( request ).getVersion() );
            }
        }
    }

    public Xpp3Dom getMojoConfiguration( MojoDescriptor mojoDescriptor )
    {
        return convert( mojoDescriptor );
    }
       
    Xpp3Dom convert( MojoDescriptor mojoDescriptor  )
    {
        Xpp3Dom dom = new Xpp3Dom( "configuration" );

        PlexusConfiguration c = mojoDescriptor.getMojoConfiguration();

        PlexusConfiguration[] ces = c.getChildren();

        if ( ces != null )
        {
            for ( PlexusConfiguration ce : ces )
            {
                String value = ce.getValue( null );
                String defaultValue = ce.getAttribute( "default-value", null );
                if ( value != null || defaultValue != null )
                {
                    Xpp3Dom e = new Xpp3Dom( ce.getName() );
                    e.setValue( value );
                    if ( defaultValue != null )
                    {
                        e.setAttribute( "default-value", defaultValue );
                    }
                    dom.addChild( e );
                }
            }
        }

        return dom;
    }

    //TODO: take repo mans into account as one may be aggregating prefixes of many
    //TODO: collect at the root of the repository, read the one at the root, and fetch remote if something is missing
    //      or the user forces the issue
    Plugin findPluginForPrefix( String prefix, MavenSession session )
        throws NoPluginFoundForPrefixException
    {       
        // [prefix]:[goal]
       
        PluginPrefixRequest prefixRequest = new DefaultPluginPrefixRequest( prefix, session );
        PluginPrefixResult prefixResult = pluginPrefixResolver.resolve( prefixRequest );
       
        Plugin plugin = new Plugin();
        plugin.setGroupId( prefixResult.getGroupId() );
        plugin.setArtifactId( prefixResult.getArtifactId() );

        return plugin;
    }

    // These are checks that should be available in real time to IDEs

    /*
    checkRequiredMavenVersion( plugin, localRepository, project.getRemoteArtifactRepositories() );
        // Validate against non-editable (@readonly) parameters, to make sure users aren't trying to override in the POM.
        //validatePomConfiguration( mojoDescriptor, pomConfiguration );           
        //checkDeprecatedParameters( mojoDescriptor, pomConfiguration );
        //checkRequiredParameters( mojoDescriptor, pomConfiguration, expressionEvaluator );       
   
    public void checkRequiredMavenVersion( Plugin plugin, ArtifactRepository localRepository, List<ArtifactRepository> remoteRepositories )
        throws PluginVersionResolutionException, InvalidPluginException
    {
        // if we don't have the required Maven version, then ignore an update
        if ( ( pluginProject.getPrerequisites() != null ) && ( pluginProject.getPrerequisites().getMaven() != null ) )
        {
            DefaultArtifactVersion requiredVersion = new DefaultArtifactVersion( pluginProject.getPrerequisites().getMaven() );

            if ( runtimeInformation.getApplicationInformation().getVersion().compareTo( requiredVersion ) < 0 )
            {
                throw new PluginVersionResolutionException( plugin.getGroupId(), plugin.getArtifactId(), "Plugin requires Maven version " + requiredVersion );
            }
        }
    }
   
   private void checkDeprecatedParameters( MojoDescriptor mojoDescriptor, PlexusConfiguration extractedMojoConfiguration )
        throws PlexusConfigurationException
    {
        if ( ( extractedMojoConfiguration == null ) || ( extractedMojoConfiguration.getChildCount() < 1 ) )
        {
            return;
        }

        List<Parameter> parameters = mojoDescriptor.getParameters();

        if ( ( parameters != null ) && !parameters.isEmpty() )
        {
            for ( Parameter param : parameters )
            {
                if ( param.getDeprecated() != null )
                {
                    boolean warnOfDeprecation = false;
                    PlexusConfiguration child = extractedMojoConfiguration.getChild( param.getName() );

                    if ( ( child != null ) && ( child.getValue() != null ) )
                    {
                        warnOfDeprecation = true;
                    }
                    else if ( param.getAlias() != null )
                    {
                        child = extractedMojoConfiguration.getChild( param.getAlias() );
                        if ( ( child != null ) && ( child.getValue() != null ) )
                        {
                            warnOfDeprecation = true;
                        }
                    }

                    if ( warnOfDeprecation )
                    {
                        StringBuilder buffer = new StringBuilder( 128 );
                        buffer.append( "In mojo: " ).append( mojoDescriptor.getGoal() ).append( ", parameter: " ).append( param.getName() );

                        if ( param.getAlias() != null )
                        {
                            buffer.append( " (alias: " ).append( param.getAlias() ).append( ")" );
                        }

                        buffer.append( " is deprecated:" ).append( "\n\n" ).append( param.getDeprecated() ).append( "\n" );

                        logger.warn( buffer.toString() );
                    }
                }
            }
        }
    }
   
   private void checkRequiredParameters( MojoDescriptor goal, PlexusConfiguration configuration, ExpressionEvaluator expressionEvaluator )
        throws PluginConfigurationException
    {
        // TODO: this should be built in to the configurator, as we presently double process the expressions

        List<Parameter> parameters = goal.getParameters();

        if ( parameters == null )
        {
            return;
        }

        List<Parameter> invalidParameters = new ArrayList<Parameter>();

        for ( int i = 0; i < parameters.size(); i++ )
        {
            Parameter parameter = parameters.get( i );

            if ( parameter.isRequired() )
            {
                // the key for the configuration map we're building.
                String key = parameter.getName();

                Object fieldValue = null;
                String expression = null;
                PlexusConfiguration value = configuration.getChild( key, false );
                try
                {
                    if ( value != null )
                    {
                        expression = value.getValue( null );

                        fieldValue = expressionEvaluator.evaluate( expression );

                        if ( fieldValue == null )
                        {
                            fieldValue = value.getAttribute( "default-value", null );
                        }
                    }

                    if ( ( fieldValue == null ) && StringUtils.isNotEmpty( parameter.getAlias() ) )
                    {
                        value = configuration.getChild( parameter.getAlias(), false );
                        if ( value != null )
                        {
                            expression = value.getValue( null );
                            fieldValue = expressionEvaluator.evaluate( expression );
                            if ( fieldValue == null )
                            {
                                fieldValue = value.getAttribute( "default-value", null );
                            }
                        }
                    }
                }
                catch ( ExpressionEvaluationException e )
                {
                    throw new PluginConfigurationException( goal.getPluginDescriptor(), e.getMessage(), e );
                }

                // only mark as invalid if there are no child nodes
                if ( ( fieldValue == null ) && ( ( value == null ) || ( value.getChildCount() == 0 ) ) )
                {
                    parameter.setExpression( expression );
                    invalidParameters.add( parameter );
                }
            }
        }

        if ( !invalidParameters.isEmpty() )
        {
            throw new PluginParameterException( goal, invalidParameters );
        }
    }

    private void validatePomConfiguration( MojoDescriptor goal, PlexusConfiguration pomConfiguration )
        throws PluginConfigurationException
    {
        List<Parameter> parameters = goal.getParameters();

        if ( parameters == null )
        {
            return;
        }

        for ( int i = 0; i < parameters.size(); i++ )
        {
            Parameter parameter = parameters.get( i );

            // the key for the configuration map we're building.
            String key = parameter.getName();

            PlexusConfiguration value = pomConfiguration.getChild( key, false );

            if ( ( value == null ) && StringUtils.isNotEmpty( parameter.getAlias() ) )
            {
                key = parameter.getAlias();
                value = pomConfiguration.getChild( key, false );
            }

            if ( value != null )
            {
                // Make sure the parameter is either editable/configurable, or else is NOT specified in the POM
                if ( !parameter.isEditable() )
                {
                    StringBuilder errorMessage = new StringBuilder( 128 ).append( "ERROR: Cannot override read-only parameter: " );
                    errorMessage.append( key );
                    errorMessage.append( " in goal: " ).append( goal.getFullGoalName() );

                    throw new PluginConfigurationException( goal.getPluginDescriptor(), errorMessage.toString() );
                }

                String deprecated = parameter.getDeprecated();
                if ( StringUtils.isNotEmpty( deprecated ) )
                {
                    logger.warn( "DEPRECATED [" + parameter.getName() + "]: " + deprecated );
                }
            }
        }
    }
   
    */   

    // USED BY MAVEN HELP PLUGIN
    @Deprecated
    public Map<String, Lifecycle> getPhaseToLifecycleMap()
    {
        return phaseToLifecycleMap;
    }

}
TOP

Related Classes of org.apache.maven.lifecycle.DefaultLifecycleExecutor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.