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 org.apache.maven.artifact.Artifact;
23  import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
24  import org.apache.maven.artifact.resolver.filter.CumulativeScopeArtifactFilter;
25  import org.apache.maven.execution.ExecutionEvent;
26  import org.apache.maven.execution.MavenSession;
27  import org.apache.maven.lifecycle.LifecycleExecutionException;
28  import org.apache.maven.lifecycle.MissingProjectException;
29  import org.apache.maven.plugin.BuildPluginManager;
30  import org.apache.maven.plugin.MavenPluginManager;
31  import org.apache.maven.plugin.MojoExecution;
32  import org.apache.maven.plugin.MojoExecutionException;
33  import org.apache.maven.plugin.MojoFailureException;
34  import org.apache.maven.plugin.PluginConfigurationException;
35  import org.apache.maven.plugin.PluginIncompatibleException;
36  import org.apache.maven.plugin.PluginManagerException;
37  import org.apache.maven.plugin.descriptor.MojoDescriptor;
38  import org.apache.maven.project.MavenProject;
39  import org.codehaus.plexus.component.annotations.Component;
40  import org.codehaus.plexus.component.annotations.Requirement;
41  import org.codehaus.plexus.util.StringUtils;
42  
43  import java.util.ArrayList;
44  import java.util.Arrays;
45  import java.util.Collection;
46  import java.util.Collections;
47  import java.util.List;
48  import java.util.Map;
49  import java.util.Set;
50  import java.util.TreeSet;
51  
52  /**
53   * Executes an individual mojo
54   *
55   * @author Jason van Zyl
56   * @author Benjamin Bentmann
57   * @author Kristian Rosenvold
58   *         <p/>
59   *         NOTE: This class is not part of any public api and can be changed or deleted without prior notice.
60   * @since 3.0
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<>();
85          Set<String> scopesToResolve = new TreeSet<>();
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 = new MissingProjectException(
175                 "Goal requires a project to execute" + " but there is no POM in this directory ("
176                     + 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 = new IllegalStateException(
186                     "Goal requires online mode for execution" + " but Maven is currently offline." );
187                 throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), cause );
188             }
189             else
190             {
191                 eventCatapult.fire( ExecutionEvent.Type.MojoSkipped, session, mojoExecution );
192 
193                 return;
194             }
195         }
196 
197         List<MavenProject> forkedProjects = executeForkedExecutions( mojoExecution, session, projectIndex );
198 
199         ensureDependenciesAreResolved( mojoDescriptor, session, dependencyContext );
200 
201         eventCatapult.fire( ExecutionEvent.Type.MojoStarted, session, mojoExecution );
202 
203         try
204         {
205             try
206             {
207                 pluginManager.executeMojo( session, mojoExecution );
208             }
209             catch ( MojoFailureException | PluginManagerException | PluginConfigurationException
210                 | MojoExecutionException e )
211             {
212                 throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), e );
213             }
214 
215             eventCatapult.fire( ExecutionEvent.Type.MojoSucceeded, session, mojoExecution );
216         }
217         catch ( LifecycleExecutionException e )
218         {
219             eventCatapult.fire( ExecutionEvent.Type.MojoFailed, session, mojoExecution, e );
220 
221             throw e;
222         }
223         finally
224         {
225             for ( MavenProject forkedProject : forkedProjects )
226             {
227                 forkedProject.setExecutionProject( null );
228             }
229         }
230     }
231 
232     public void ensureDependenciesAreResolved( MojoDescriptor mojoDescriptor, MavenSession session,
233                                                DependencyContext dependencyContext )
234         throws LifecycleExecutionException
235 
236     {
237         MavenProject project = dependencyContext.getProject();
238         boolean aggregating = mojoDescriptor.isAggregator();
239 
240         if ( dependencyContext.isResolutionRequiredForCurrentProject() )
241         {
242             Collection<String> scopesToCollect = dependencyContext.getScopesToCollectForCurrentProject();
243             Collection<String> scopesToResolve = dependencyContext.getScopesToResolveForCurrentProject();
244 
245             lifeCycleDependencyResolver.resolveProjectDependencies( project, scopesToCollect, scopesToResolve, session,
246                                                                     aggregating, Collections.<Artifact>emptySet() );
247 
248             dependencyContext.synchronizeWithProjectState();
249         }
250 
251         if ( aggregating )
252         {
253             Collection<String> scopesToCollect = toScopes( mojoDescriptor.getDependencyCollectionRequired() );
254             Collection<String> scopesToResolve = toScopes( mojoDescriptor.getDependencyResolutionRequired() );
255 
256             if ( dependencyContext.isResolutionRequiredForAggregatedProjects( scopesToCollect, scopesToResolve ) )
257             {
258                 for ( MavenProject aggregatedProject : session.getProjects() )
259                 {
260                     if ( aggregatedProject != project )
261                     {
262                         lifeCycleDependencyResolver.resolveProjectDependencies( aggregatedProject, scopesToCollect,
263                                                                                 scopesToResolve, session, aggregating,
264                                                                                 Collections.<Artifact>emptySet() );
265                     }
266                 }
267             }
268         }
269 
270         ArtifactFilter artifactFilter = getArtifactFilter( mojoDescriptor );
271         List<MavenProject> projectsToResolve =
272             LifecycleDependencyResolver.getProjects( session.getCurrentProject(), session,
273                                                      mojoDescriptor.isAggregator() );
274         for ( MavenProject projectToResolve : projectsToResolve )
275         {
276             projectToResolve.setArtifactFilter( artifactFilter );
277         }
278     }
279 
280     private ArtifactFilter getArtifactFilter( MojoDescriptor mojoDescriptor )
281     {
282         String scopeToResolve = mojoDescriptor.getDependencyResolutionRequired();
283         String scopeToCollect = mojoDescriptor.getDependencyCollectionRequired();
284 
285         List<String> scopes = new ArrayList<>( 2 );
286         if ( StringUtils.isNotEmpty( scopeToCollect ) )
287         {
288             scopes.add( scopeToCollect );
289         }
290         if ( StringUtils.isNotEmpty( scopeToResolve ) )
291         {
292             scopes.add( scopeToResolve );
293         }
294 
295         if ( scopes.isEmpty() )
296         {
297             return null;
298         }
299         else
300         {
301             return new CumulativeScopeArtifactFilter( scopes );
302         }
303     }
304 
305     public List<MavenProject> executeForkedExecutions( MojoExecution mojoExecution, MavenSession session,
306                                                        ProjectIndex projectIndex )
307         throws LifecycleExecutionException
308     {
309         List<MavenProject> forkedProjects = Collections.emptyList();
310 
311         Map<String, List<MojoExecution>> forkedExecutions = mojoExecution.getForkedExecutions();
312 
313         if ( !forkedExecutions.isEmpty() )
314         {
315             eventCatapult.fire( ExecutionEvent.Type.ForkStarted, session, mojoExecution );
316 
317             MavenProject project = session.getCurrentProject();
318 
319             forkedProjects = new ArrayList<>( forkedExecutions.size() );
320 
321             try
322             {
323                 for ( Map.Entry<String, List<MojoExecution>> fork : forkedExecutions.entrySet() )
324                 {
325                     String projectId = fork.getKey();
326 
327                     int index = projectIndex.getIndices().get( projectId );
328 
329                     MavenProject forkedProject = projectIndex.getProjects().get( projectId );
330 
331                     forkedProjects.add( forkedProject );
332 
333                     MavenProject executedProject = forkedProject.clone();
334 
335                     forkedProject.setExecutionProject( executedProject );
336 
337                     List<MojoExecution> mojoExecutions = fork.getValue();
338 
339                     if ( mojoExecutions.isEmpty() )
340                     {
341                         continue;
342                     }
343 
344                     try
345                     {
346                         session.setCurrentProject( executedProject );
347                         session.getProjects().set( index, executedProject );
348                         projectIndex.getProjects().put( projectId, executedProject );
349 
350                         eventCatapult.fire( ExecutionEvent.Type.ForkedProjectStarted, session, mojoExecution );
351 
352                         execute( session, mojoExecutions, projectIndex );
353 
354                         eventCatapult.fire( ExecutionEvent.Type.ForkedProjectSucceeded, session, mojoExecution );
355                     }
356                     catch ( LifecycleExecutionException e )
357                     {
358                         eventCatapult.fire( ExecutionEvent.Type.ForkedProjectFailed, session, mojoExecution, e );
359 
360                         throw e;
361                     }
362                     finally
363                     {
364                         projectIndex.getProjects().put( projectId, forkedProject );
365                         session.getProjects().set( index, forkedProject );
366                         session.setCurrentProject( project );
367                     }
368                 }
369 
370                 eventCatapult.fire( ExecutionEvent.Type.ForkSucceeded, session, mojoExecution );
371             }
372             catch ( LifecycleExecutionException e )
373             {
374                 eventCatapult.fire( ExecutionEvent.Type.ForkFailed, session, mojoExecution, e );
375 
376                 throw e;
377             }
378         }
379 
380         return forkedProjects;
381     }
382 }