001package org.apache.maven.lifecycle.internal;
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 org.apache.maven.artifact.Artifact;
023import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
024import org.apache.maven.artifact.resolver.filter.CumulativeScopeArtifactFilter;
025import org.apache.maven.execution.ExecutionEvent;
026import org.apache.maven.execution.MavenSession;
027import org.apache.maven.lifecycle.LifecycleExecutionException;
028import org.apache.maven.lifecycle.MissingProjectException;
029import org.apache.maven.plugin.BuildPluginManager;
030import org.apache.maven.plugin.MavenPluginManager;
031import org.apache.maven.plugin.MojoExecution;
032import org.apache.maven.plugin.MojoExecutionException;
033import org.apache.maven.plugin.MojoFailureException;
034import org.apache.maven.plugin.PluginConfigurationException;
035import org.apache.maven.plugin.PluginIncompatibleException;
036import org.apache.maven.plugin.PluginManagerException;
037import org.apache.maven.plugin.descriptor.MojoDescriptor;
038import org.apache.maven.project.MavenProject;
039import org.codehaus.plexus.component.annotations.Component;
040import org.codehaus.plexus.component.annotations.Requirement;
041import org.codehaus.plexus.util.StringUtils;
042
043import java.util.ArrayList;
044import java.util.Arrays;
045import java.util.Collection;
046import java.util.Collections;
047import java.util.List;
048import java.util.Map;
049import java.util.Set;
050import java.util.TreeSet;
051
052/**
053 * Executes an individual mojo
054 * 
055 * @since 3.0
056 * @author Jason van Zyl
057 * @author Benjamin Bentmann
058 * @author Kristian Rosenvold
059 *         <p/>
060 *         NOTE: This class is not part of any public api and can be changed or deleted without prior notice.
061 */
062@Component( role = MojoExecutor.class )
063public class MojoExecutor
064{
065
066    @Requirement
067    private BuildPluginManager pluginManager;
068
069    @Requirement
070    private MavenPluginManager mavenPluginManager;
071
072    @Requirement
073    private LifecycleDependencyResolver lifeCycleDependencyResolver;
074
075    @Requirement
076    private ExecutionEventCatapult eventCatapult;
077
078    public MojoExecutor()
079    {
080    }
081
082    public DependencyContext newDependencyContext( MavenSession session, List<MojoExecution> mojoExecutions )
083    {
084        Set<String> scopesToCollect = new TreeSet<String>();
085        Set<String> scopesToResolve = new TreeSet<String>();
086
087        collectDependencyRequirements( scopesToResolve, scopesToCollect, mojoExecutions );
088
089        return new DependencyContext( session.getCurrentProject(), scopesToCollect, scopesToResolve );
090    }
091
092    private void collectDependencyRequirements( Set<String> scopesToResolve, Set<String> scopesToCollect,
093                                                Collection<MojoExecution> mojoExecutions )
094    {
095        for ( MojoExecution mojoExecution : mojoExecutions )
096        {
097            MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor();
098
099            scopesToResolve.addAll( toScopes( mojoDescriptor.getDependencyResolutionRequired() ) );
100
101            scopesToCollect.addAll( toScopes( mojoDescriptor.getDependencyCollectionRequired() ) );
102        }
103    }
104
105    private Collection<String> toScopes( String classpath )
106    {
107        if ( StringUtils.isNotEmpty( classpath ) )
108        {
109            if ( Artifact.SCOPE_COMPILE.equals( classpath ) )
110            {
111                return Arrays.asList( Artifact.SCOPE_COMPILE, Artifact.SCOPE_SYSTEM, Artifact.SCOPE_PROVIDED );
112            }
113            else if ( Artifact.SCOPE_RUNTIME.equals( classpath ) )
114            {
115                return Arrays.asList( Artifact.SCOPE_COMPILE, Artifact.SCOPE_RUNTIME );
116            }
117            else if ( Artifact.SCOPE_COMPILE_PLUS_RUNTIME.equals( classpath ) )
118            {
119                return Arrays.asList( Artifact.SCOPE_COMPILE, Artifact.SCOPE_SYSTEM, Artifact.SCOPE_PROVIDED,
120                                      Artifact.SCOPE_RUNTIME );
121            }
122            else if ( Artifact.SCOPE_RUNTIME_PLUS_SYSTEM.equals( classpath ) )
123            {
124                return Arrays.asList( Artifact.SCOPE_COMPILE, Artifact.SCOPE_SYSTEM, Artifact.SCOPE_RUNTIME );
125            }
126            else if ( Artifact.SCOPE_TEST.equals( classpath ) )
127            {
128                return Arrays.asList( Artifact.SCOPE_COMPILE, Artifact.SCOPE_SYSTEM, Artifact.SCOPE_PROVIDED,
129                                      Artifact.SCOPE_RUNTIME, Artifact.SCOPE_TEST );
130            }
131        }
132        return Collections.emptyList();
133    }
134
135    public void execute( MavenSession session, List<MojoExecution> mojoExecutions, ProjectIndex projectIndex )
136        throws LifecycleExecutionException
137
138    {
139        DependencyContext dependencyContext = newDependencyContext( session, mojoExecutions );
140
141        PhaseRecorder phaseRecorder = new PhaseRecorder( session.getCurrentProject() );
142
143        for ( MojoExecution mojoExecution : mojoExecutions )
144        {
145            execute( session, mojoExecution, projectIndex, dependencyContext, phaseRecorder );
146        }
147    }
148
149    public void execute( MavenSession session, MojoExecution mojoExecution, ProjectIndex projectIndex,
150                         DependencyContext dependencyContext, PhaseRecorder phaseRecorder )
151        throws LifecycleExecutionException
152    {
153        execute( session, mojoExecution, projectIndex, dependencyContext );
154        phaseRecorder.observeExecution( mojoExecution );
155    }
156
157    private void execute( MavenSession session, MojoExecution mojoExecution, ProjectIndex projectIndex,
158                          DependencyContext dependencyContext )
159        throws LifecycleExecutionException
160    {
161        MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor();
162
163        try
164        {
165            mavenPluginManager.checkRequiredMavenVersion( mojoDescriptor.getPluginDescriptor() );
166        }
167        catch ( PluginIncompatibleException e )
168        {
169            throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), e );
170        }
171
172        if ( mojoDescriptor.isProjectRequired() && !session.isUsingPOMsFromFilesystem() )
173        {
174            Throwable cause =
175                new MissingProjectException( "Goal requires a project to execute"
176                    + " but there is no POM in this directory (" + session.getExecutionRootDirectory() + ")."
177                    + " Please verify you invoked Maven from the correct directory." );
178            throw new LifecycleExecutionException( mojoExecution, null, cause );
179        }
180
181        if ( mojoDescriptor.isOnlineRequired() && session.isOffline() )
182        {
183            if ( MojoExecution.Source.CLI.equals( mojoExecution.getSource() ) )
184            {
185                Throwable cause =
186                    new IllegalStateException( "Goal requires online mode for execution"
187                        + " but Maven is currently offline." );
188                throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), cause );
189            }
190            else
191            {
192                eventCatapult.fire( ExecutionEvent.Type.MojoSkipped, session, mojoExecution );
193
194                return;
195            }
196        }
197
198        List<MavenProject> forkedProjects = executeForkedExecutions( mojoExecution, session, projectIndex );
199
200        ensureDependenciesAreResolved( mojoDescriptor, session, dependencyContext );
201
202        eventCatapult.fire( ExecutionEvent.Type.MojoStarted, session, mojoExecution );
203
204        try
205        {
206            try
207            {
208                pluginManager.executeMojo( session, mojoExecution );
209            }
210            catch ( MojoFailureException e )
211            {
212                throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), e );
213            }
214            catch ( MojoExecutionException e )
215            {
216                throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), e );
217            }
218            catch ( PluginConfigurationException e )
219            {
220                throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), e );
221            }
222            catch ( PluginManagerException e )
223            {
224                throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), e );
225            }
226
227            eventCatapult.fire( ExecutionEvent.Type.MojoSucceeded, session, mojoExecution );
228        }
229        catch ( LifecycleExecutionException e )
230        {
231            eventCatapult.fire( ExecutionEvent.Type.MojoFailed, session, mojoExecution, e );
232
233            throw e;
234        }
235        finally
236        {
237            for ( MavenProject forkedProject : forkedProjects )
238            {
239                forkedProject.setExecutionProject( null );
240            }
241        }
242    }
243
244    public void ensureDependenciesAreResolved( MojoDescriptor mojoDescriptor, MavenSession session,
245                                                DependencyContext dependencyContext )
246        throws LifecycleExecutionException
247
248    {
249        MavenProject project = dependencyContext.getProject();
250        boolean aggregating = mojoDescriptor.isAggregator();
251
252        if ( dependencyContext.isResolutionRequiredForCurrentProject() )
253        {
254            Collection<String> scopesToCollect = dependencyContext.getScopesToCollectForCurrentProject();
255            Collection<String> scopesToResolve = dependencyContext.getScopesToResolveForCurrentProject();
256
257            lifeCycleDependencyResolver.resolveProjectDependencies( project, scopesToCollect, scopesToResolve, session,
258                                                                    aggregating, Collections.<Artifact> emptySet() );
259
260            dependencyContext.synchronizeWithProjectState();
261        }
262
263        if ( aggregating )
264        {
265            Collection<String> scopesToCollect = toScopes( mojoDescriptor.getDependencyCollectionRequired() );
266            Collection<String> scopesToResolve = toScopes( mojoDescriptor.getDependencyResolutionRequired() );
267
268            if ( dependencyContext.isResolutionRequiredForAggregatedProjects( scopesToCollect, scopesToResolve ) )
269            {
270                for ( MavenProject aggregatedProject : session.getProjects() )
271                {
272                    if ( aggregatedProject != project )
273                    {
274                        lifeCycleDependencyResolver.resolveProjectDependencies( aggregatedProject, scopesToCollect,
275                                                                                scopesToResolve, session, aggregating,
276                                                                                Collections.<Artifact> emptySet() );
277                    }
278                }
279            }
280        }
281
282        ArtifactFilter artifactFilter = getArtifactFilter( mojoDescriptor );
283        List<MavenProject> projectsToResolve =
284            LifecycleDependencyResolver.getProjects( session.getCurrentProject(), session,
285                                                     mojoDescriptor.isAggregator() );
286        for ( MavenProject projectToResolve : projectsToResolve )
287        {
288            projectToResolve.setArtifactFilter( artifactFilter );
289        }
290    }
291
292    private ArtifactFilter getArtifactFilter( MojoDescriptor mojoDescriptor )
293    {
294        String scopeToResolve = mojoDescriptor.getDependencyResolutionRequired();
295        String scopeToCollect = mojoDescriptor.getDependencyCollectionRequired();
296
297        List<String> scopes = new ArrayList<String>( 2 );
298        if ( StringUtils.isNotEmpty( scopeToCollect ) )
299        {
300            scopes.add( scopeToCollect );
301        }
302        if ( StringUtils.isNotEmpty( scopeToResolve ) )
303        {
304            scopes.add( scopeToResolve );
305        }
306
307        if ( scopes.isEmpty() )
308        {
309            return null;
310        }
311        else
312        {
313            return new CumulativeScopeArtifactFilter( scopes );
314        }
315    }
316
317    public List<MavenProject> executeForkedExecutions( MojoExecution mojoExecution, MavenSession session,
318                                                       ProjectIndex projectIndex )
319        throws LifecycleExecutionException
320    {
321        List<MavenProject> forkedProjects = Collections.emptyList();
322
323        Map<String, List<MojoExecution>> forkedExecutions = mojoExecution.getForkedExecutions();
324
325        if ( !forkedExecutions.isEmpty() )
326        {
327            eventCatapult.fire( ExecutionEvent.Type.ForkStarted, session, mojoExecution );
328
329            MavenProject project = session.getCurrentProject();
330
331            forkedProjects = new ArrayList<MavenProject>( forkedExecutions.size() );
332
333            try
334            {
335                for ( Map.Entry<String, List<MojoExecution>> fork : forkedExecutions.entrySet() )
336                {
337                    String projectId = fork.getKey();
338
339                    int index = projectIndex.getIndices().get( projectId );
340
341                    MavenProject forkedProject = projectIndex.getProjects().get( projectId );
342
343                    forkedProjects.add( forkedProject );
344
345                    MavenProject executedProject = forkedProject.clone();
346
347                    forkedProject.setExecutionProject( executedProject );
348
349                    List<MojoExecution> mojoExecutions = fork.getValue();
350
351                    if ( mojoExecutions.isEmpty() )
352                    {
353                        continue;
354                    }
355
356                    try
357                    {
358                        session.setCurrentProject( executedProject );
359                        session.getProjects().set( index, executedProject );
360                        projectIndex.getProjects().put( projectId, executedProject );
361
362                        eventCatapult.fire( ExecutionEvent.Type.ForkedProjectStarted, session, mojoExecution );
363
364                        execute( session, mojoExecutions, projectIndex );
365
366                        eventCatapult.fire( ExecutionEvent.Type.ForkedProjectSucceeded, session, mojoExecution );
367                    }
368                    catch ( LifecycleExecutionException e )
369                    {
370                        eventCatapult.fire( ExecutionEvent.Type.ForkedProjectFailed, session, mojoExecution, e );
371
372                        throw e;
373                    }
374                    finally
375                    {
376                        projectIndex.getProjects().put( projectId, forkedProject );
377                        session.getProjects().set( index, forkedProject );
378                        session.setCurrentProject( project );
379                    }
380                }
381
382                eventCatapult.fire( ExecutionEvent.Type.ForkSucceeded, session, mojoExecution );
383            }
384            catch ( LifecycleExecutionException e )
385            {
386                eventCatapult.fire( ExecutionEvent.Type.ForkFailed, session, mojoExecution, e );
387
388                throw e;
389            }
390        }
391
392        return forkedProjects;
393    }
394}