1 package org.apache.maven.lifecycle.internal;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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 import org.eclipse.aether.SessionData;
43
44 import java.util.ArrayList;
45 import java.util.Arrays;
46 import java.util.Collection;
47 import java.util.Collections;
48 import java.util.List;
49 import java.util.Map;
50 import java.util.Set;
51 import java.util.TreeSet;
52 import java.util.concurrent.ConcurrentHashMap;
53 import java.util.concurrent.ConcurrentMap;
54 import java.util.concurrent.locks.Lock;
55 import java.util.concurrent.locks.ReadWriteLock;
56 import java.util.concurrent.locks.ReentrantLock;
57 import java.util.concurrent.locks.ReentrantReadWriteLock;
58
59
60
61
62
63
64
65
66
67
68
69
70 @Component( role = MojoExecutor.class )
71 public class MojoExecutor
72 {
73
74 @Requirement
75 private BuildPluginManager pluginManager;
76
77 @Requirement
78 private MavenPluginManager mavenPluginManager;
79
80 @Requirement
81 private LifecycleDependencyResolver lifeCycleDependencyResolver;
82
83 @Requirement
84 private ExecutionEventCatapult eventCatapult;
85
86 private final ReadWriteLock aggregatorLock = new ReentrantReadWriteLock();
87
88 public MojoExecutor()
89 {
90 }
91
92 public DependencyContext newDependencyContext( MavenSession session, List<MojoExecution> mojoExecutions )
93 {
94 Set<String> scopesToCollect = new TreeSet<>();
95 Set<String> scopesToResolve = new TreeSet<>();
96
97 collectDependencyRequirements( scopesToResolve, scopesToCollect, mojoExecutions );
98
99 return new DependencyContext( session.getCurrentProject(), scopesToCollect, scopesToResolve );
100 }
101
102 private void collectDependencyRequirements( Set<String> scopesToResolve, Set<String> scopesToCollect,
103 Collection<MojoExecution> mojoExecutions )
104 {
105 for ( MojoExecution mojoExecution : mojoExecutions )
106 {
107 MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor();
108
109 scopesToResolve.addAll( toScopes( mojoDescriptor.getDependencyResolutionRequired() ) );
110
111 scopesToCollect.addAll( toScopes( mojoDescriptor.getDependencyCollectionRequired() ) );
112 }
113 }
114
115 private Collection<String> toScopes( String classpath )
116 {
117 Collection<String> scopes = Collections.emptyList();
118
119 if ( StringUtils.isNotEmpty( classpath ) )
120 {
121 if ( Artifact.SCOPE_COMPILE.equals( classpath ) )
122 {
123 scopes = Arrays.asList( Artifact.SCOPE_COMPILE, Artifact.SCOPE_SYSTEM, Artifact.SCOPE_PROVIDED );
124 }
125 else if ( Artifact.SCOPE_RUNTIME.equals( classpath ) )
126 {
127 scopes = Arrays.asList( Artifact.SCOPE_COMPILE, Artifact.SCOPE_RUNTIME );
128 }
129 else if ( Artifact.SCOPE_COMPILE_PLUS_RUNTIME.equals( classpath ) )
130 {
131 scopes = Arrays.asList( Artifact.SCOPE_COMPILE, Artifact.SCOPE_SYSTEM, Artifact.SCOPE_PROVIDED,
132 Artifact.SCOPE_RUNTIME );
133 }
134 else if ( Artifact.SCOPE_RUNTIME_PLUS_SYSTEM.equals( classpath ) )
135 {
136 scopes = Arrays.asList( Artifact.SCOPE_COMPILE, Artifact.SCOPE_SYSTEM, Artifact.SCOPE_RUNTIME );
137 }
138 else if ( Artifact.SCOPE_TEST.equals( classpath ) )
139 {
140 scopes = Arrays.asList( Artifact.SCOPE_COMPILE, Artifact.SCOPE_SYSTEM, Artifact.SCOPE_PROVIDED,
141 Artifact.SCOPE_RUNTIME, Artifact.SCOPE_TEST );
142 }
143 }
144 return Collections.unmodifiableCollection( scopes );
145 }
146
147 public void execute( MavenSession session, List<MojoExecution> mojoExecutions, ProjectIndex projectIndex )
148 throws LifecycleExecutionException
149
150 {
151 DependencyContext dependencyContext = newDependencyContext( session, mojoExecutions );
152
153 PhaseRecorder phaseRecorder = new PhaseRecorder( session.getCurrentProject() );
154
155 for ( MojoExecution mojoExecution : mojoExecutions )
156 {
157 execute( session, mojoExecution, projectIndex, dependencyContext, phaseRecorder );
158 }
159 }
160
161 public void execute( MavenSession session, MojoExecution mojoExecution, ProjectIndex projectIndex,
162 DependencyContext dependencyContext, PhaseRecorder phaseRecorder )
163 throws LifecycleExecutionException
164 {
165 execute( session, mojoExecution, projectIndex, dependencyContext );
166 phaseRecorder.observeExecution( mojoExecution );
167 }
168
169 private void execute( MavenSession session, MojoExecution mojoExecution, ProjectIndex projectIndex,
170 DependencyContext dependencyContext )
171 throws LifecycleExecutionException
172 {
173 MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor();
174
175 try
176 {
177 mavenPluginManager.checkRequiredMavenVersion( mojoDescriptor.getPluginDescriptor() );
178 }
179 catch ( PluginIncompatibleException e )
180 {
181 throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), e );
182 }
183
184 if ( mojoDescriptor.isProjectRequired() && !session.getRequest().isProjectPresent() )
185 {
186 Throwable cause = new MissingProjectException(
187 "Goal requires a project to execute" + " but there is no POM in this directory ("
188 + session.getExecutionRootDirectory() + ")."
189 + " Please verify you invoked Maven from the correct directory." );
190 throw new LifecycleExecutionException( mojoExecution, null, cause );
191 }
192
193 if ( mojoDescriptor.isOnlineRequired() && session.isOffline() )
194 {
195 if ( MojoExecution.Source.CLI.equals( mojoExecution.getSource() ) )
196 {
197 Throwable cause = new IllegalStateException(
198 "Goal requires online mode for execution" + " but Maven is currently offline." );
199 throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), cause );
200 }
201 else
202 {
203 eventCatapult.fire( ExecutionEvent.Type.MojoSkipped, session, mojoExecution );
204
205 return;
206 }
207 }
208
209 try ( ProjectLock lock = new ProjectLock( session, mojoDescriptor, aggregatorLock ) )
210 {
211 doExecute( session, mojoExecution, projectIndex, dependencyContext );
212 }
213 }
214
215
216
217
218
219
220
221
222
223 private static class ProjectLock implements AutoCloseable
224 {
225 final Lock acquiredAggregatorLock;
226 final Lock acquiredProjectLock;
227
228 ProjectLock( MavenSession session, MojoDescriptor mojoDescriptor, ReadWriteLock aggregatorLock )
229 {
230 if ( session.getRequest().getDegreeOfConcurrency() > 1 )
231 {
232 boolean aggregator = mojoDescriptor.isAggregator();
233 acquiredAggregatorLock = aggregator ? aggregatorLock.writeLock() : aggregatorLock.readLock();
234 acquiredProjectLock = getProjectLock( session );
235 acquiredAggregatorLock.lock();
236 acquiredProjectLock.lock();
237 }
238 else
239 {
240 acquiredAggregatorLock = null;
241 acquiredProjectLock = null;
242 }
243 }
244
245 @Override
246 public void close()
247 {
248
249 if ( acquiredProjectLock != null )
250 {
251 acquiredProjectLock.unlock();
252 }
253 if ( acquiredAggregatorLock != null )
254 {
255 acquiredAggregatorLock.unlock();
256 }
257 }
258
259 @SuppressWarnings( { "unchecked", "rawtypes" } )
260 private Lock getProjectLock( MavenSession session )
261 {
262 SessionData data = session.getRepositorySession().getData();
263 ConcurrentMap<MavenProject, Lock> locks = ( ConcurrentMap ) data.get( ProjectLock.class );
264
265 if ( locks == null )
266 {
267
268 data.set( ProjectLock.class, null, new ConcurrentHashMap<>() );
269 locks = ( ConcurrentMap ) data.get( ProjectLock.class );
270 }
271 Lock acquiredProjectLock = locks.get( session.getCurrentProject() );
272 if ( acquiredProjectLock == null )
273 {
274 acquiredProjectLock = new ReentrantLock();
275 Lock prev = locks.putIfAbsent( session.getCurrentProject(), acquiredProjectLock );
276 if ( prev != null )
277 {
278 acquiredProjectLock = prev;
279 }
280 }
281 return acquiredProjectLock;
282 }
283 }
284
285 private void doExecute( MavenSession session, MojoExecution mojoExecution, ProjectIndex projectIndex,
286 DependencyContext dependencyContext )
287 throws LifecycleExecutionException
288 {
289 MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor();
290
291 List<MavenProject> forkedProjects = executeForkedExecutions( mojoExecution, session, projectIndex );
292
293 ensureDependenciesAreResolved( mojoDescriptor, session, dependencyContext );
294
295 eventCatapult.fire( ExecutionEvent.Type.MojoStarted, session, mojoExecution );
296
297 try
298 {
299 try
300 {
301 pluginManager.executeMojo( session, mojoExecution );
302 }
303 catch ( MojoFailureException | PluginManagerException | PluginConfigurationException
304 | MojoExecutionException e )
305 {
306 throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), e );
307 }
308
309 eventCatapult.fire( ExecutionEvent.Type.MojoSucceeded, session, mojoExecution );
310 }
311 catch ( LifecycleExecutionException e )
312 {
313 eventCatapult.fire( ExecutionEvent.Type.MojoFailed, session, mojoExecution, e );
314
315 throw e;
316 }
317 finally
318 {
319 for ( MavenProject forkedProject : forkedProjects )
320 {
321 forkedProject.setExecutionProject( null );
322 }
323 }
324 }
325
326 public void ensureDependenciesAreResolved( MojoDescriptor mojoDescriptor, MavenSession session,
327 DependencyContext dependencyContext )
328 throws LifecycleExecutionException
329
330 {
331 MavenProject project = dependencyContext.getProject();
332 boolean aggregating = mojoDescriptor.isAggregator();
333
334 if ( dependencyContext.isResolutionRequiredForCurrentProject() )
335 {
336 Collection<String> scopesToCollect = dependencyContext.getScopesToCollectForCurrentProject();
337 Collection<String> scopesToResolve = dependencyContext.getScopesToResolveForCurrentProject();
338
339 lifeCycleDependencyResolver.resolveProjectDependencies( project, scopesToCollect, scopesToResolve, session,
340 aggregating, Collections.<Artifact>emptySet() );
341
342 dependencyContext.synchronizeWithProjectState();
343 }
344
345 if ( aggregating )
346 {
347 Collection<String> scopesToCollect = toScopes( mojoDescriptor.getDependencyCollectionRequired() );
348 Collection<String> scopesToResolve = toScopes( mojoDescriptor.getDependencyResolutionRequired() );
349
350 if ( dependencyContext.isResolutionRequiredForAggregatedProjects( scopesToCollect, scopesToResolve ) )
351 {
352 for ( MavenProject aggregatedProject : session.getProjects() )
353 {
354 if ( aggregatedProject != project )
355 {
356 lifeCycleDependencyResolver.resolveProjectDependencies( aggregatedProject, scopesToCollect,
357 scopesToResolve, session, aggregating,
358 Collections.<Artifact>emptySet() );
359 }
360 }
361 }
362 }
363
364 ArtifactFilter artifactFilter = getArtifactFilter( mojoDescriptor );
365 List<MavenProject> projectsToResolve =
366 LifecycleDependencyResolver.getProjects( session.getCurrentProject(), session,
367 mojoDescriptor.isAggregator() );
368 for ( MavenProject projectToResolve : projectsToResolve )
369 {
370 projectToResolve.setArtifactFilter( artifactFilter );
371 }
372 }
373
374 private ArtifactFilter getArtifactFilter( MojoDescriptor mojoDescriptor )
375 {
376 String scopeToResolve = mojoDescriptor.getDependencyResolutionRequired();
377 String scopeToCollect = mojoDescriptor.getDependencyCollectionRequired();
378
379 List<String> scopes = new ArrayList<>( 2 );
380 if ( StringUtils.isNotEmpty( scopeToCollect ) )
381 {
382 scopes.add( scopeToCollect );
383 }
384 if ( StringUtils.isNotEmpty( scopeToResolve ) )
385 {
386 scopes.add( scopeToResolve );
387 }
388
389 if ( scopes.isEmpty() )
390 {
391 return null;
392 }
393 else
394 {
395 return new CumulativeScopeArtifactFilter( scopes );
396 }
397 }
398
399 public List<MavenProject> executeForkedExecutions( MojoExecution mojoExecution, MavenSession session,
400 ProjectIndex projectIndex )
401 throws LifecycleExecutionException
402 {
403 List<MavenProject> forkedProjects = Collections.emptyList();
404
405 Map<String, List<MojoExecution>> forkedExecutions = mojoExecution.getForkedExecutions();
406
407 if ( !forkedExecutions.isEmpty() )
408 {
409 eventCatapult.fire( ExecutionEvent.Type.ForkStarted, session, mojoExecution );
410
411 MavenProject project = session.getCurrentProject();
412
413 forkedProjects = new ArrayList<>( forkedExecutions.size() );
414
415 try
416 {
417 for ( Map.Entry<String, List<MojoExecution>> fork : forkedExecutions.entrySet() )
418 {
419 String projectId = fork.getKey();
420
421 int index = projectIndex.getIndices().get( projectId );
422
423 MavenProject forkedProject = projectIndex.getProjects().get( projectId );
424
425 forkedProjects.add( forkedProject );
426
427 MavenProject executedProject = forkedProject.clone();
428
429 forkedProject.setExecutionProject( executedProject );
430
431 List<MojoExecution> mojoExecutions = fork.getValue();
432
433 if ( mojoExecutions.isEmpty() )
434 {
435 continue;
436 }
437
438 try
439 {
440 session.setCurrentProject( executedProject );
441 session.getProjects().set( index, executedProject );
442 projectIndex.getProjects().put( projectId, executedProject );
443
444 eventCatapult.fire( ExecutionEvent.Type.ForkedProjectStarted, session, mojoExecution );
445
446 execute( session, mojoExecutions, projectIndex );
447
448 eventCatapult.fire( ExecutionEvent.Type.ForkedProjectSucceeded, session, mojoExecution );
449 }
450 catch ( LifecycleExecutionException e )
451 {
452 eventCatapult.fire( ExecutionEvent.Type.ForkedProjectFailed, session, mojoExecution, e );
453
454 throw e;
455 }
456 finally
457 {
458 projectIndex.getProjects().put( projectId, forkedProject );
459 session.getProjects().set( index, forkedProject );
460 session.setCurrentProject( project );
461 }
462 }
463
464 eventCatapult.fire( ExecutionEvent.Type.ForkSucceeded, session, mojoExecution );
465 }
466 catch ( LifecycleExecutionException e )
467 {
468 eventCatapult.fire( ExecutionEvent.Type.ForkFailed, session, mojoExecution, e );
469
470 throw e;
471 }
472 }
473
474 return forkedProjects;
475 }
476 }