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   * <p>
54   * Executes an individual mojo
55   * </p>
56   * <strong>NOTE:</strong> This class is not part of any public api and can be changed or deleted without prior notice.
57   *
58   * @author Jason van Zyl
59   * @author Benjamin Bentmann
60   * @author Kristian Rosenvold
61   * @since 3.0
62   */
63  @Component( role = MojoExecutor.class )
64  public class MojoExecutor
65  {
66  
67      @Requirement
68      private BuildPluginManager pluginManager;
69  
70      @Requirement
71      private MavenPluginManager mavenPluginManager;
72  
73      @Requirement
74      private LifecycleDependencyResolver lifeCycleDependencyResolver;
75  
76      @Requirement
77      private ExecutionEventCatapult eventCatapult;
78  
79      public MojoExecutor()
80      {
81      }
82  
83      public DependencyContext newDependencyContext( MavenSession session, List<MojoExecution> mojoExecutions )
84      {
85          Set<String> scopesToCollect = new TreeSet<>();
86          Set<String> scopesToResolve = new TreeSet<>();
87  
88          collectDependencyRequirements( scopesToResolve, scopesToCollect, mojoExecutions );
89  
90          return new DependencyContext( session.getCurrentProject(), scopesToCollect, scopesToResolve );
91      }
92  
93      private void collectDependencyRequirements( Set<String> scopesToResolve, Set<String> scopesToCollect,
94                                                  Collection<MojoExecution> mojoExecutions )
95      {
96          for ( MojoExecution mojoExecution : mojoExecutions )
97          {
98              MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor();
99  
100             scopesToResolve.addAll( toScopes( mojoDescriptor.getDependencyResolutionRequired() ) );
101 
102             scopesToCollect.addAll( toScopes( mojoDescriptor.getDependencyCollectionRequired() ) );
103         }
104     }
105 
106     private Collection<String> toScopes( String classpath )
107     {
108         if ( StringUtils.isNotEmpty( classpath ) )
109         {
110             if ( Artifact.SCOPE_COMPILE.equals( classpath ) )
111             {
112                 return Arrays.asList( Artifact.SCOPE_COMPILE, Artifact.SCOPE_SYSTEM, Artifact.SCOPE_PROVIDED );
113             }
114             else if ( Artifact.SCOPE_RUNTIME.equals( classpath ) )
115             {
116                 return Arrays.asList( Artifact.SCOPE_COMPILE, Artifact.SCOPE_RUNTIME );
117             }
118             else if ( Artifact.SCOPE_COMPILE_PLUS_RUNTIME.equals( classpath ) )
119             {
120                 return Arrays.asList( Artifact.SCOPE_COMPILE, Artifact.SCOPE_SYSTEM, Artifact.SCOPE_PROVIDED,
121                                       Artifact.SCOPE_RUNTIME );
122             }
123             else if ( Artifact.SCOPE_RUNTIME_PLUS_SYSTEM.equals( classpath ) )
124             {
125                 return Arrays.asList( Artifact.SCOPE_COMPILE, Artifact.SCOPE_SYSTEM, Artifact.SCOPE_RUNTIME );
126             }
127             else if ( Artifact.SCOPE_TEST.equals( classpath ) )
128             {
129                 return Arrays.asList( Artifact.SCOPE_COMPILE, Artifact.SCOPE_SYSTEM, Artifact.SCOPE_PROVIDED,
130                                       Artifact.SCOPE_RUNTIME, Artifact.SCOPE_TEST );
131             }
132         }
133         return Collections.emptyList();
134     }
135 
136     public void execute( MavenSession session, List<MojoExecution> mojoExecutions, ProjectIndex projectIndex )
137         throws LifecycleExecutionException
138 
139     {
140         DependencyContext dependencyContext = newDependencyContext( session, mojoExecutions );
141 
142         PhaseRecorder phaseRecorder = new PhaseRecorder( session.getCurrentProject() );
143 
144         for ( MojoExecution mojoExecution : mojoExecutions )
145         {
146             execute( session, mojoExecution, projectIndex, dependencyContext, phaseRecorder );
147         }
148     }
149 
150     public void execute( MavenSession session, MojoExecution mojoExecution, ProjectIndex projectIndex,
151                          DependencyContext dependencyContext, PhaseRecorder phaseRecorder )
152         throws LifecycleExecutionException
153     {
154         execute( session, mojoExecution, projectIndex, dependencyContext );
155         phaseRecorder.observeExecution( mojoExecution );
156     }
157 
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.getRequest().isProjectPresent() )
174         {
175             Throwable cause = new MissingProjectException(
176                 "Goal requires a project to execute" + " but there is no POM in this directory ("
177                     + 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 = new IllegalStateException(
187                     "Goal requires online mode for execution" + " 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 | PluginManagerException | PluginConfigurationException
211                 | MojoExecutionException e )
212             {
213                 throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), e );
214             }
215 
216             eventCatapult.fire( ExecutionEvent.Type.MojoSucceeded, session, mojoExecution );
217         }
218         catch ( LifecycleExecutionException e )
219         {
220             eventCatapult.fire( ExecutionEvent.Type.MojoFailed, session, mojoExecution, e );
221 
222             throw e;
223         }
224         finally
225         {
226             for ( MavenProject forkedProject : forkedProjects )
227             {
228                 forkedProject.setExecutionProject( null );
229             }
230         }
231     }
232 
233     public void ensureDependenciesAreResolved( MojoDescriptor mojoDescriptor, MavenSession session,
234                                                DependencyContext dependencyContext )
235         throws LifecycleExecutionException
236 
237     {
238         MavenProject project = dependencyContext.getProject();
239         boolean aggregating = mojoDescriptor.isAggregator();
240 
241         if ( dependencyContext.isResolutionRequiredForCurrentProject() )
242         {
243             Collection<String> scopesToCollect = dependencyContext.getScopesToCollectForCurrentProject();
244             Collection<String> scopesToResolve = dependencyContext.getScopesToResolveForCurrentProject();
245 
246             lifeCycleDependencyResolver.resolveProjectDependencies( project, scopesToCollect, scopesToResolve, session,
247                                                                     aggregating, Collections.<Artifact>emptySet() );
248 
249             dependencyContext.synchronizeWithProjectState();
250         }
251 
252         if ( aggregating )
253         {
254             Collection<String> scopesToCollect = toScopes( mojoDescriptor.getDependencyCollectionRequired() );
255             Collection<String> scopesToResolve = toScopes( mojoDescriptor.getDependencyResolutionRequired() );
256 
257             if ( dependencyContext.isResolutionRequiredForAggregatedProjects( scopesToCollect, scopesToResolve ) )
258             {
259                 for ( MavenProject aggregatedProject : session.getProjects() )
260                 {
261                     if ( aggregatedProject != project )
262                     {
263                         lifeCycleDependencyResolver.resolveProjectDependencies( aggregatedProject, scopesToCollect,
264                                                                                 scopesToResolve, session, aggregating,
265                                                                                 Collections.<Artifact>emptySet() );
266                     }
267                 }
268             }
269         }
270 
271         ArtifactFilter artifactFilter = getArtifactFilter( mojoDescriptor );
272         List<MavenProject> projectsToResolve =
273             LifecycleDependencyResolver.getProjects( session.getCurrentProject(), session,
274                                                      mojoDescriptor.isAggregator() );
275         for ( MavenProject projectToResolve : projectsToResolve )
276         {
277             projectToResolve.setArtifactFilter( artifactFilter );
278         }
279     }
280 
281     private ArtifactFilter getArtifactFilter( MojoDescriptor mojoDescriptor )
282     {
283         String scopeToResolve = mojoDescriptor.getDependencyResolutionRequired();
284         String scopeToCollect = mojoDescriptor.getDependencyCollectionRequired();
285 
286         List<String> scopes = new ArrayList<>( 2 );
287         if ( StringUtils.isNotEmpty( scopeToCollect ) )
288         {
289             scopes.add( scopeToCollect );
290         }
291         if ( StringUtils.isNotEmpty( scopeToResolve ) )
292         {
293             scopes.add( scopeToResolve );
294         }
295 
296         if ( scopes.isEmpty() )
297         {
298             return null;
299         }
300         else
301         {
302             return new CumulativeScopeArtifactFilter( scopes );
303         }
304     }
305 
306     public List<MavenProject> executeForkedExecutions( MojoExecution mojoExecution, MavenSession session,
307                                                        ProjectIndex projectIndex )
308         throws LifecycleExecutionException
309     {
310         List<MavenProject> forkedProjects = Collections.emptyList();
311 
312         Map<String, List<MojoExecution>> forkedExecutions = mojoExecution.getForkedExecutions();
313 
314         if ( !forkedExecutions.isEmpty() )
315         {
316             eventCatapult.fire( ExecutionEvent.Type.ForkStarted, session, mojoExecution );
317 
318             MavenProject project = session.getCurrentProject();
319 
320             forkedProjects = new ArrayList<>( forkedExecutions.size() );
321 
322             try
323             {
324                 for ( Map.Entry<String, List<MojoExecution>> fork : forkedExecutions.entrySet() )
325                 {
326                     String projectId = fork.getKey();
327 
328                     int index = projectIndex.getIndices().get( projectId );
329 
330                     MavenProject forkedProject = projectIndex.getProjects().get( projectId );
331 
332                     forkedProjects.add( forkedProject );
333 
334                     MavenProject executedProject = forkedProject.clone();
335 
336                     forkedProject.setExecutionProject( executedProject );
337 
338                     List<MojoExecution> mojoExecutions = fork.getValue();
339 
340                     if ( mojoExecutions.isEmpty() )
341                     {
342                         continue;
343                     }
344 
345                     try
346                     {
347                         session.setCurrentProject( executedProject );
348                         session.getProjects().set( index, executedProject );
349                         projectIndex.getProjects().put( projectId, executedProject );
350 
351                         eventCatapult.fire( ExecutionEvent.Type.ForkedProjectStarted, session, mojoExecution );
352 
353                         execute( session, mojoExecutions, projectIndex );
354 
355                         eventCatapult.fire( ExecutionEvent.Type.ForkedProjectSucceeded, session, mojoExecution );
356                     }
357                     catch ( LifecycleExecutionException e )
358                     {
359                         eventCatapult.fire( ExecutionEvent.Type.ForkedProjectFailed, session, mojoExecution, e );
360 
361                         throw e;
362                     }
363                     finally
364                     {
365                         projectIndex.getProjects().put( projectId, forkedProject );
366                         session.getProjects().set( index, forkedProject );
367                         session.setCurrentProject( project );
368                     }
369                 }
370 
371                 eventCatapult.fire( ExecutionEvent.Type.ForkSucceeded, session, mojoExecution );
372             }
373             catch ( LifecycleExecutionException e )
374             {
375                 eventCatapult.fire( ExecutionEvent.Type.ForkFailed, session, mojoExecution, e );
376 
377                 throw e;
378             }
379         }
380 
381         return forkedProjects;
382     }
383 }