001    package 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    
022    import org.apache.maven.artifact.Artifact;
023    import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
024    import org.apache.maven.artifact.resolver.filter.CumulativeScopeArtifactFilter;
025    import org.apache.maven.execution.ExecutionEvent;
026    import org.apache.maven.execution.MavenSession;
027    import org.apache.maven.lifecycle.LifecycleExecutionException;
028    import org.apache.maven.lifecycle.MissingProjectException;
029    import org.apache.maven.plugin.BuildPluginManager;
030    import org.apache.maven.plugin.MavenPluginManager;
031    import org.apache.maven.plugin.MojoExecution;
032    import org.apache.maven.plugin.MojoExecutionException;
033    import org.apache.maven.plugin.MojoFailureException;
034    import org.apache.maven.plugin.PluginConfigurationException;
035    import org.apache.maven.plugin.PluginIncompatibleException;
036    import org.apache.maven.plugin.PluginManagerException;
037    import org.apache.maven.plugin.descriptor.MojoDescriptor;
038    import org.apache.maven.project.MavenProject;
039    import org.codehaus.plexus.component.annotations.Component;
040    import org.codehaus.plexus.component.annotations.Requirement;
041    import org.codehaus.plexus.util.StringUtils;
042    
043    import java.util.ArrayList;
044    import java.util.Arrays;
045    import java.util.Collection;
046    import java.util.Collections;
047    import java.util.List;
048    import java.util.Map;
049    import java.util.Set;
050    import 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 )
063    public 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        @SuppressWarnings( { "ThrowableInstanceNeverThrown" } )
158        private void execute( MavenSession session, MojoExecution mojoExecution, ProjectIndex projectIndex,
159                              DependencyContext dependencyContext )
160            throws LifecycleExecutionException
161        {
162            MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor();
163    
164            try
165            {
166                mavenPluginManager.checkRequiredMavenVersion( mojoDescriptor.getPluginDescriptor() );
167            }
168            catch ( PluginIncompatibleException e )
169            {
170                throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), e );
171            }
172    
173            if ( mojoDescriptor.isProjectRequired() && !session.isUsingPOMsFromFilesystem() )
174            {
175                Throwable cause =
176                    new MissingProjectException( "Goal requires a project to execute"
177                        + " but there is no POM in this directory (" + session.getExecutionRootDirectory() + ")."
178                        + " Please verify you invoked Maven from the correct directory." );
179                throw new LifecycleExecutionException( mojoExecution, null, cause );
180            }
181    
182            if ( mojoDescriptor.isOnlineRequired() && session.isOffline() )
183            {
184                if ( MojoExecution.Source.CLI.equals( mojoExecution.getSource() ) )
185                {
186                    Throwable cause =
187                        new IllegalStateException( "Goal requires online mode for execution"
188                            + " but Maven is currently offline." );
189                    throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), cause );
190                }
191                else
192                {
193                    eventCatapult.fire( ExecutionEvent.Type.MojoSkipped, session, mojoExecution );
194    
195                    return;
196                }
197            }
198    
199            List<MavenProject> forkedProjects = executeForkedExecutions( mojoExecution, session, projectIndex );
200    
201            ensureDependenciesAreResolved( mojoDescriptor, session, dependencyContext );
202    
203            eventCatapult.fire( ExecutionEvent.Type.MojoStarted, session, mojoExecution );
204    
205            try
206            {
207                try
208                {
209                    pluginManager.executeMojo( session, mojoExecution );
210                }
211                catch ( MojoFailureException e )
212                {
213                    throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), e );
214                }
215                catch ( MojoExecutionException e )
216                {
217                    throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), e );
218                }
219                catch ( PluginConfigurationException e )
220                {
221                    throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), e );
222                }
223                catch ( PluginManagerException e )
224                {
225                    throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), e );
226                }
227    
228                eventCatapult.fire( ExecutionEvent.Type.MojoSucceeded, session, mojoExecution );
229            }
230            catch ( LifecycleExecutionException e )
231            {
232                eventCatapult.fire( ExecutionEvent.Type.MojoFailed, session, mojoExecution, e );
233    
234                throw e;
235            }
236            finally
237            {
238                for ( MavenProject forkedProject : forkedProjects )
239                {
240                    forkedProject.setExecutionProject( null );
241                }
242            }
243        }
244    
245        public void ensureDependenciesAreResolved( MojoDescriptor mojoDescriptor, MavenSession session,
246                                                    DependencyContext dependencyContext )
247            throws LifecycleExecutionException
248    
249        {
250            MavenProject project = dependencyContext.getProject();
251            boolean aggregating = mojoDescriptor.isAggregator();
252    
253            if ( dependencyContext.isResolutionRequiredForCurrentProject() )
254            {
255                Collection<String> scopesToCollect = dependencyContext.getScopesToCollectForCurrentProject();
256                Collection<String> scopesToResolve = dependencyContext.getScopesToResolveForCurrentProject();
257    
258                lifeCycleDependencyResolver.resolveProjectDependencies( project, scopesToCollect, scopesToResolve, session,
259                                                                        aggregating, Collections.<Artifact> emptySet() );
260    
261                dependencyContext.synchronizeWithProjectState();
262            }
263    
264            if ( aggregating )
265            {
266                Collection<String> scopesToCollect = toScopes( mojoDescriptor.getDependencyCollectionRequired() );
267                Collection<String> scopesToResolve = toScopes( mojoDescriptor.getDependencyResolutionRequired() );
268    
269                if ( dependencyContext.isResolutionRequiredForAggregatedProjects( scopesToCollect, scopesToResolve ) )
270                {
271                    for ( MavenProject aggregatedProject : session.getProjects() )
272                    {
273                        if ( aggregatedProject != project )
274                        {
275                            lifeCycleDependencyResolver.resolveProjectDependencies( aggregatedProject, scopesToCollect,
276                                                                                    scopesToResolve, session, aggregating,
277                                                                                    Collections.<Artifact> emptySet() );
278                        }
279                    }
280                }
281            }
282    
283            ArtifactFilter artifactFilter = getArtifactFilter( mojoDescriptor );
284            List<MavenProject> projectsToResolve =
285                LifecycleDependencyResolver.getProjects( session.getCurrentProject(), session,
286                                                         mojoDescriptor.isAggregator() );
287            for ( MavenProject projectToResolve : projectsToResolve )
288            {
289                projectToResolve.setArtifactFilter( artifactFilter );
290            }
291        }
292    
293        private ArtifactFilter getArtifactFilter( MojoDescriptor mojoDescriptor )
294        {
295            String scopeToResolve = mojoDescriptor.getDependencyResolutionRequired();
296            String scopeToCollect = mojoDescriptor.getDependencyCollectionRequired();
297    
298            List<String> scopes = new ArrayList<String>( 2 );
299            if ( StringUtils.isNotEmpty( scopeToCollect ) )
300            {
301                scopes.add( scopeToCollect );
302            }
303            if ( StringUtils.isNotEmpty( scopeToResolve ) )
304            {
305                scopes.add( scopeToResolve );
306            }
307    
308            if ( scopes.isEmpty() )
309            {
310                return null;
311            }
312            else
313            {
314                return new CumulativeScopeArtifactFilter( scopes );
315            }
316        }
317    
318        public List<MavenProject> executeForkedExecutions( MojoExecution mojoExecution, MavenSession session,
319                                                           ProjectIndex projectIndex )
320            throws LifecycleExecutionException
321        {
322            List<MavenProject> forkedProjects = Collections.emptyList();
323    
324            Map<String, List<MojoExecution>> forkedExecutions = mojoExecution.getForkedExecutions();
325    
326            if ( !forkedExecutions.isEmpty() )
327            {
328                eventCatapult.fire( ExecutionEvent.Type.ForkStarted, session, mojoExecution );
329    
330                MavenProject project = session.getCurrentProject();
331    
332                forkedProjects = new ArrayList<MavenProject>( forkedExecutions.size() );
333    
334                try
335                {
336                    for ( Map.Entry<String, List<MojoExecution>> fork : forkedExecutions.entrySet() )
337                    {
338                        String projectId = fork.getKey();
339    
340                        int index = projectIndex.getIndices().get( projectId );
341    
342                        MavenProject forkedProject = projectIndex.getProjects().get( projectId );
343    
344                        forkedProjects.add( forkedProject );
345    
346                        MavenProject executedProject = forkedProject.clone();
347    
348                        forkedProject.setExecutionProject( executedProject );
349    
350                        List<MojoExecution> mojoExecutions = fork.getValue();
351    
352                        if ( mojoExecutions.isEmpty() )
353                        {
354                            continue;
355                        }
356    
357                        try
358                        {
359                            session.setCurrentProject( executedProject );
360                            session.getProjects().set( index, executedProject );
361                            projectIndex.getProjects().put( projectId, executedProject );
362    
363                            eventCatapult.fire( ExecutionEvent.Type.ForkedProjectStarted, session, mojoExecution );
364    
365                            execute( session, mojoExecutions, projectIndex );
366    
367                            eventCatapult.fire( ExecutionEvent.Type.ForkedProjectSucceeded, session, mojoExecution );
368                        }
369                        catch ( LifecycleExecutionException e )
370                        {
371                            eventCatapult.fire( ExecutionEvent.Type.ForkedProjectFailed, session, mojoExecution, e );
372    
373                            throw e;
374                        }
375                        finally
376                        {
377                            projectIndex.getProjects().put( projectId, forkedProject );
378                            session.getProjects().set( index, forkedProject );
379                            session.setCurrentProject( project );
380                        }
381                    }
382    
383                    eventCatapult.fire( ExecutionEvent.Type.ForkSucceeded, session, mojoExecution );
384                }
385                catch ( LifecycleExecutionException e )
386                {
387                    eventCatapult.fire( ExecutionEvent.Type.ForkFailed, session, mojoExecution, e );
388    
389                    throw e;
390                }
391            }
392    
393            return forkedProjects;
394        }
395    }