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        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    }