View Javadoc
1   package org.apache.maven.lifecycle.internal;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.util.ArrayList;
23  import java.util.Arrays;
24  import java.util.Collection;
25  import java.util.Collections;
26  import java.util.List;
27  import java.util.Map;
28  import java.util.Set;
29  import java.util.TreeSet;
30  
31  import org.apache.maven.artifact.Artifact;
32  import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
33  import org.apache.maven.artifact.resolver.filter.CumulativeScopeArtifactFilter;
34  import org.apache.maven.execution.ExecutionEvent;
35  import org.apache.maven.execution.MavenSession;
36  import org.apache.maven.lifecycle.LifecycleExecutionException;
37  import org.apache.maven.lifecycle.MissingProjectException;
38  import org.apache.maven.plugin.BuildPluginManager;
39  import org.apache.maven.plugin.MavenPluginManager;
40  import org.apache.maven.plugin.MojoExecution;
41  import org.apache.maven.plugin.MojoExecutionException;
42  import org.apache.maven.plugin.MojoFailureException;
43  import org.apache.maven.plugin.PluginConfigurationException;
44  import org.apache.maven.plugin.PluginIncompatibleException;
45  import org.apache.maven.plugin.PluginManagerException;
46  import org.apache.maven.plugin.descriptor.MojoDescriptor;
47  import org.apache.maven.project.MavenProject;
48  import org.codehaus.plexus.component.annotations.Component;
49  import org.codehaus.plexus.component.annotations.Requirement;
50  import org.codehaus.plexus.util.StringUtils;
51  
52  /**
53   * Executes an individual mojo
54   *
55   * @since 3.0
56   * @author Jason van Zyl
57   * @author Benjamin Bentmann
58   * @author Kristian Rosenvold
59   *         <p/>
60   *         NOTE: This class is not part of any public api and can be changed or deleted without prior notice.
61   */
62  @Component( role = MojoExecutor.class )
63  public class MojoExecutor
64  {
65  
66      @Requirement
67      private BuildPluginManager pluginManager;
68  
69      @Requirement
70      private MavenPluginManager mavenPluginManager;
71  
72      @Requirement
73      private LifecycleDependencyResolver lifeCycleDependencyResolver;
74  
75      @Requirement
76      private ExecutionEventCatapult eventCatapult;
77  
78      public MojoExecutor()
79      {
80      }
81  
82      public DependencyContext newDependencyContext( MavenSession session, List<MojoExecution> mojoExecutions )
83      {
84          Set<String> scopesToCollect = new TreeSet<String>();
85          Set<String> scopesToResolve = new TreeSet<String>();
86  
87          collectDependencyRequirements( scopesToResolve, scopesToCollect, mojoExecutions );
88  
89          return new DependencyContext( session.getCurrentProject(), scopesToCollect, scopesToResolve );
90      }
91  
92      private void collectDependencyRequirements( Set<String> scopesToResolve, Set<String> scopesToCollect,
93                                                  Collection<MojoExecution> mojoExecutions )
94      {
95          for ( MojoExecution mojoExecution : mojoExecutions )
96          {
97              MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor();
98  
99              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.getRequest().isProjectPresent() )
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 }