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         Collection<String> scopes = Collections.emptyList();
109 
110         if ( StringUtils.isNotEmpty( classpath ) )
111         {
112             if ( Artifact.SCOPE_COMPILE.equals( classpath ) )
113             {
114                 scopes = Arrays.asList( Artifact.SCOPE_COMPILE, Artifact.SCOPE_SYSTEM, Artifact.SCOPE_PROVIDED );
115             }
116             else if ( Artifact.SCOPE_RUNTIME.equals( classpath ) )
117             {
118                 scopes = Arrays.asList( Artifact.SCOPE_COMPILE, Artifact.SCOPE_RUNTIME );
119             }
120             else if ( Artifact.SCOPE_COMPILE_PLUS_RUNTIME.equals( classpath ) )
121             {
122                 scopes = Arrays.asList( Artifact.SCOPE_COMPILE, Artifact.SCOPE_SYSTEM, Artifact.SCOPE_PROVIDED,
123                                         Artifact.SCOPE_RUNTIME );
124             }
125             else if ( Artifact.SCOPE_RUNTIME_PLUS_SYSTEM.equals( classpath ) )
126             {
127                 scopes = Arrays.asList( Artifact.SCOPE_COMPILE, Artifact.SCOPE_SYSTEM, Artifact.SCOPE_RUNTIME );
128             }
129             else if ( Artifact.SCOPE_TEST.equals( classpath ) )
130             {
131                 scopes = Arrays.asList( Artifact.SCOPE_COMPILE, Artifact.SCOPE_SYSTEM, Artifact.SCOPE_PROVIDED,
132                                         Artifact.SCOPE_RUNTIME, Artifact.SCOPE_TEST );
133             }
134         }
135         return Collections.unmodifiableCollection( scopes );
136     }
137 
138     public void execute( MavenSession session, List<MojoExecution> mojoExecutions, ProjectIndex projectIndex )
139         throws LifecycleExecutionException
140 
141     {
142         DependencyContext dependencyContext = newDependencyContext( session, mojoExecutions );
143 
144         PhaseRecorder phaseRecorder = new PhaseRecorder( session.getCurrentProject() );
145 
146         for ( MojoExecution mojoExecution : mojoExecutions )
147         {
148             execute( session, mojoExecution, projectIndex, dependencyContext, phaseRecorder );
149         }
150     }
151 
152     public void execute( MavenSession session, MojoExecution mojoExecution, ProjectIndex projectIndex,
153                          DependencyContext dependencyContext, PhaseRecorder phaseRecorder )
154         throws LifecycleExecutionException
155     {
156         execute( session, mojoExecution, projectIndex, dependencyContext );
157         phaseRecorder.observeExecution( mojoExecution );
158     }
159 
160     private void execute( MavenSession session, MojoExecution mojoExecution, ProjectIndex projectIndex,
161                           DependencyContext dependencyContext )
162         throws LifecycleExecutionException
163     {
164         MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor();
165 
166         try
167         {
168             mavenPluginManager.checkRequiredMavenVersion( mojoDescriptor.getPluginDescriptor() );
169         }
170         catch ( PluginIncompatibleException e )
171         {
172             throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), e );
173         }
174 
175         if ( mojoDescriptor.isProjectRequired() && !session.getRequest().isProjectPresent() )
176         {
177             Throwable cause = new MissingProjectException(
178                 "Goal requires a project to execute" + " but there is no POM in this directory ("
179                     + session.getExecutionRootDirectory() + ")."
180                     + " Please verify you invoked Maven from the correct directory." );
181             throw new LifecycleExecutionException( mojoExecution, null, cause );
182         }
183 
184         if ( mojoDescriptor.isOnlineRequired() && session.isOffline() )
185         {
186             if ( MojoExecution.Source.CLI.equals( mojoExecution.getSource() ) )
187             {
188                 Throwable cause = new IllegalStateException(
189                     "Goal requires online mode for execution" + " but Maven is currently offline." );
190                 throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), cause );
191             }
192             else
193             {
194                 eventCatapult.fire( ExecutionEvent.Type.MojoSkipped, session, mojoExecution );
195 
196                 return;
197             }
198         }
199 
200         List<MavenProject> forkedProjects = executeForkedExecutions( mojoExecution, session, projectIndex );
201 
202         ensureDependenciesAreResolved( mojoDescriptor, session, dependencyContext );
203 
204         eventCatapult.fire( ExecutionEvent.Type.MojoStarted, session, mojoExecution );
205 
206         try
207         {
208             try
209             {
210                 pluginManager.executeMojo( session, mojoExecution );
211             }
212             catch ( MojoFailureException | PluginManagerException | PluginConfigurationException
213                 | MojoExecutionException e )
214             {
215                 throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), e );
216             }
217 
218             eventCatapult.fire( ExecutionEvent.Type.MojoSucceeded, session, mojoExecution );
219         }
220         catch ( LifecycleExecutionException e )
221         {
222             eventCatapult.fire( ExecutionEvent.Type.MojoFailed, session, mojoExecution, e );
223 
224             throw e;
225         }
226         finally
227         {
228             for ( MavenProject forkedProject : forkedProjects )
229             {
230                 forkedProject.setExecutionProject( null );
231             }
232         }
233     }
234 
235     public void ensureDependenciesAreResolved( MojoDescriptor mojoDescriptor, MavenSession session,
236                                                DependencyContext dependencyContext )
237         throws LifecycleExecutionException
238 
239     {
240         MavenProject project = dependencyContext.getProject();
241         boolean aggregating = mojoDescriptor.isAggregator();
242 
243         if ( dependencyContext.isResolutionRequiredForCurrentProject() )
244         {
245             Collection<String> scopesToCollect = dependencyContext.getScopesToCollectForCurrentProject();
246             Collection<String> scopesToResolve = dependencyContext.getScopesToResolveForCurrentProject();
247 
248             lifeCycleDependencyResolver.resolveProjectDependencies( project, scopesToCollect, scopesToResolve, session,
249                                                                     aggregating, Collections.<Artifact>emptySet() );
250 
251             dependencyContext.synchronizeWithProjectState();
252         }
253 
254         if ( aggregating )
255         {
256             Collection<String> scopesToCollect = toScopes( mojoDescriptor.getDependencyCollectionRequired() );
257             Collection<String> scopesToResolve = toScopes( mojoDescriptor.getDependencyResolutionRequired() );
258 
259             if ( dependencyContext.isResolutionRequiredForAggregatedProjects( scopesToCollect, scopesToResolve ) )
260             {
261                 for ( MavenProject aggregatedProject : session.getProjects() )
262                 {
263                     if ( aggregatedProject != project )
264                     {
265                         lifeCycleDependencyResolver.resolveProjectDependencies( aggregatedProject, scopesToCollect,
266                                                                                 scopesToResolve, session, aggregating,
267                                                                                 Collections.<Artifact>emptySet() );
268                     }
269                 }
270             }
271         }
272 
273         ArtifactFilter artifactFilter = getArtifactFilter( mojoDescriptor );
274         List<MavenProject> projectsToResolve =
275             LifecycleDependencyResolver.getProjects( session.getCurrentProject(), session,
276                                                      mojoDescriptor.isAggregator() );
277         for ( MavenProject projectToResolve : projectsToResolve )
278         {
279             projectToResolve.setArtifactFilter( artifactFilter );
280         }
281     }
282 
283     private ArtifactFilter getArtifactFilter( MojoDescriptor mojoDescriptor )
284     {
285         String scopeToResolve = mojoDescriptor.getDependencyResolutionRequired();
286         String scopeToCollect = mojoDescriptor.getDependencyCollectionRequired();
287 
288         List<String> scopes = new ArrayList<>( 2 );
289         if ( StringUtils.isNotEmpty( scopeToCollect ) )
290         {
291             scopes.add( scopeToCollect );
292         }
293         if ( StringUtils.isNotEmpty( scopeToResolve ) )
294         {
295             scopes.add( scopeToResolve );
296         }
297 
298         if ( scopes.isEmpty() )
299         {
300             return null;
301         }
302         else
303         {
304             return new CumulativeScopeArtifactFilter( scopes );
305         }
306     }
307 
308     public List<MavenProject> executeForkedExecutions( MojoExecution mojoExecution, MavenSession session,
309                                                        ProjectIndex projectIndex )
310         throws LifecycleExecutionException
311     {
312         List<MavenProject> forkedProjects = Collections.emptyList();
313 
314         Map<String, List<MojoExecution>> forkedExecutions = mojoExecution.getForkedExecutions();
315 
316         if ( !forkedExecutions.isEmpty() )
317         {
318             eventCatapult.fire( ExecutionEvent.Type.ForkStarted, session, mojoExecution );
319 
320             MavenProject project = session.getCurrentProject();
321 
322             forkedProjects = new ArrayList<>( forkedExecutions.size() );
323 
324             try
325             {
326                 for ( Map.Entry<String, List<MojoExecution>> fork : forkedExecutions.entrySet() )
327                 {
328                     String projectId = fork.getKey();
329 
330                     int index = projectIndex.getIndices().get( projectId );
331 
332                     MavenProject forkedProject = projectIndex.getProjects().get( projectId );
333 
334                     forkedProjects.add( forkedProject );
335 
336                     MavenProject executedProject = forkedProject.clone();
337 
338                     forkedProject.setExecutionProject( executedProject );
339 
340                     List<MojoExecution> mojoExecutions = fork.getValue();
341 
342                     if ( mojoExecutions.isEmpty() )
343                     {
344                         continue;
345                     }
346 
347                     try
348                     {
349                         session.setCurrentProject( executedProject );
350                         session.getProjects().set( index, executedProject );
351                         projectIndex.getProjects().put( projectId, executedProject );
352 
353                         eventCatapult.fire( ExecutionEvent.Type.ForkedProjectStarted, session, mojoExecution );
354 
355                         execute( session, mojoExecutions, projectIndex );
356 
357                         eventCatapult.fire( ExecutionEvent.Type.ForkedProjectSucceeded, session, mojoExecution );
358                     }
359                     catch ( LifecycleExecutionException e )
360                     {
361                         eventCatapult.fire( ExecutionEvent.Type.ForkedProjectFailed, session, mojoExecution, e );
362 
363                         throw e;
364                     }
365                     finally
366                     {
367                         projectIndex.getProjects().put( projectId, forkedProject );
368                         session.getProjects().set( index, forkedProject );
369                         session.setCurrentProject( project );
370                     }
371                 }
372 
373                 eventCatapult.fire( ExecutionEvent.Type.ForkSucceeded, session, mojoExecution );
374             }
375             catch ( LifecycleExecutionException e )
376             {
377                 eventCatapult.fire( ExecutionEvent.Type.ForkFailed, session, mojoExecution, e );
378 
379                 throw e;
380             }
381         }
382 
383         return forkedProjects;
384     }
385 }