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   * @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     @SuppressWarnings( { "ThrowableInstanceNeverThrown" } )
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.isUsingPOMsFromFilesystem() )
174         {
175             Throwable cause =
176                 new MissingProjectException( "Goal requires a project to execute"
177                     + " but there is no POM in this directory (" + 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 =
187                     new IllegalStateException( "Goal requires online mode for execution"
188                         + " but Maven is currently offline." );
189                 throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), cause );
190             }
191             else
192             {
193                 eventCatapult.fire( ExecutionEvent.Type.MojoSkipped, session, mojoExecution );
194 
195                 return;
196             }
197         }
198 
199         List<MavenProject> forkedProjects = executeForkedExecutions( mojoExecution, session, projectIndex );
200 
201         ensureDependenciesAreResolved( mojoDescriptor, session, dependencyContext );
202 
203         eventCatapult.fire( ExecutionEvent.Type.MojoStarted, session, mojoExecution );
204 
205         try
206         {
207             try
208             {
209                 pluginManager.executeMojo( session, mojoExecution );
210             }
211             catch ( MojoFailureException e )
212             {
213                 throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), e );
214             }
215             catch ( MojoExecutionException e )
216             {
217                 throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), e );
218             }
219             catch ( PluginConfigurationException e )
220             {
221                 throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), e );
222             }
223             catch ( PluginManagerException e )
224             {
225                 throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), e );
226             }
227 
228             eventCatapult.fire( ExecutionEvent.Type.MojoSucceeded, session, mojoExecution );
229         }
230         catch ( LifecycleExecutionException e )
231         {
232             eventCatapult.fire( ExecutionEvent.Type.MojoFailed, session, mojoExecution, e );
233 
234             throw e;
235         }
236         finally
237         {
238             for ( MavenProject forkedProject : forkedProjects )
239             {
240                 forkedProject.setExecutionProject( null );
241             }
242         }
243     }
244 
245     private void ensureDependenciesAreResolved( MojoDescriptor mojoDescriptor, MavenSession session,
246                                                 DependencyContext dependencyContext )
247         throws LifecycleExecutionException
248 
249     {
250         MavenProject project = dependencyContext.getProject();
251         boolean aggregating = mojoDescriptor.isAggregator();
252 
253         if ( dependencyContext.isResolutionRequiredForCurrentProject() )
254         {
255             Collection<String> scopesToCollect = dependencyContext.getScopesToCollectForCurrentProject();
256             Collection<String> scopesToResolve = dependencyContext.getScopesToResolveForCurrentProject();
257 
258             lifeCycleDependencyResolver.resolveProjectDependencies( project, scopesToCollect, scopesToResolve, session,
259                                                                     aggregating, Collections.<Artifact> emptySet() );
260 
261             dependencyContext.synchronizeWithProjectState();
262         }
263 
264         if ( aggregating )
265         {
266             Collection<String> scopesToCollect = toScopes( mojoDescriptor.getDependencyCollectionRequired() );
267             Collection<String> scopesToResolve = toScopes( mojoDescriptor.getDependencyResolutionRequired() );
268 
269             if ( dependencyContext.isResolutionRequiredForAggregatedProjects( scopesToCollect, scopesToResolve ) )
270             {
271                 for ( MavenProject aggregatedProject : session.getProjects() )
272                 {
273                     if ( aggregatedProject != project )
274                     {
275                         lifeCycleDependencyResolver.resolveProjectDependencies( aggregatedProject, scopesToCollect,
276                                                                                 scopesToResolve, session, aggregating,
277                                                                                 Collections.<Artifact> emptySet() );
278                     }
279                 }
280             }
281         }
282 
283         ArtifactFilter artifactFilter = getArtifactFilter( mojoDescriptor );
284         List<MavenProject> projectsToResolve =
285             LifecycleDependencyResolver.getProjects( session.getCurrentProject(), session,
286                                                      mojoDescriptor.isAggregator() );
287         for ( MavenProject projectToResolve : projectsToResolve )
288         {
289             projectToResolve.setArtifactFilter( artifactFilter );
290         }
291     }
292 
293     private ArtifactFilter getArtifactFilter( MojoDescriptor mojoDescriptor )
294     {
295         String scopeToResolve = mojoDescriptor.getDependencyResolutionRequired();
296         String scopeToCollect = mojoDescriptor.getDependencyCollectionRequired();
297 
298         List<String> scopes = new ArrayList<String>( 2 );
299         if ( StringUtils.isNotEmpty( scopeToCollect ) )
300         {
301             scopes.add( scopeToCollect );
302         }
303         if ( StringUtils.isNotEmpty( scopeToResolve ) )
304         {
305             scopes.add( scopeToResolve );
306         }
307 
308         if ( scopes.isEmpty() )
309         {
310             return null;
311         }
312         else
313         {
314             return new CumulativeScopeArtifactFilter( scopes );
315         }
316     }
317 
318     public List<MavenProject> executeForkedExecutions( MojoExecution mojoExecution, MavenSession session,
319                                                        ProjectIndex projectIndex )
320         throws LifecycleExecutionException
321     {
322         List<MavenProject> forkedProjects = Collections.emptyList();
323 
324         Map<String, List<MojoExecution>> forkedExecutions = mojoExecution.getForkedExecutions();
325 
326         if ( !forkedExecutions.isEmpty() )
327         {
328             eventCatapult.fire( ExecutionEvent.Type.ForkStarted, session, mojoExecution );
329 
330             MavenProject project = session.getCurrentProject();
331 
332             forkedProjects = new ArrayList<MavenProject>( forkedExecutions.size() );
333 
334             try
335             {
336                 for ( Map.Entry<String, List<MojoExecution>> fork : forkedExecutions.entrySet() )
337                 {
338                     String projectId = fork.getKey();
339 
340                     int index = projectIndex.getIndices().get( projectId );
341 
342                     MavenProject forkedProject = projectIndex.getProjects().get( projectId );
343 
344                     forkedProjects.add( forkedProject );
345 
346                     MavenProject executedProject = forkedProject.clone();
347 
348                     forkedProject.setExecutionProject( executedProject );
349 
350                     List<MojoExecution> mojoExecutions = fork.getValue();
351 
352                     if ( mojoExecutions.isEmpty() )
353                     {
354                         continue;
355                     }
356 
357                     try
358                     {
359                         session.setCurrentProject( executedProject );
360                         session.getProjects().set( index, executedProject );
361                         projectIndex.getProjects().put( projectId, executedProject );
362 
363                         eventCatapult.fire( ExecutionEvent.Type.ForkedProjectStarted, session, mojoExecution );
364 
365                         execute( session, mojoExecutions, projectIndex );
366 
367                         eventCatapult.fire( ExecutionEvent.Type.ForkedProjectSucceeded, session, mojoExecution );
368                     }
369                     catch ( LifecycleExecutionException e )
370                     {
371                         eventCatapult.fire( ExecutionEvent.Type.ForkedProjectFailed, session, mojoExecution, e );
372 
373                         throw e;
374                     }
375                     finally
376                     {
377                         projectIndex.getProjects().put( projectId, forkedProject );
378                         session.getProjects().set( index, forkedProject );
379                         session.setCurrentProject( project );
380                     }
381                 }
382 
383                 eventCatapult.fire( ExecutionEvent.Type.ForkSucceeded, session, mojoExecution );
384             }
385             catch ( LifecycleExecutionException e )
386             {
387                 eventCatapult.fire( ExecutionEvent.Type.ForkFailed, session, mojoExecution, e );
388 
389                 throw e;
390             }
391         }
392 
393         return forkedProjects;
394     }
395 }