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.ArtifactUtils;
24 import org.apache.maven.execution.BuildSuccess;
25 import org.apache.maven.execution.ExecutionEvent;
26 import org.apache.maven.execution.MavenExecutionRequest;
27 import org.apache.maven.execution.MavenSession;
28 import org.apache.maven.lifecycle.LifecycleExecutionException;
29 import org.apache.maven.lifecycle.MavenExecutionPlan;
30 import org.apache.maven.lifecycle.Schedule;
31 import org.apache.maven.project.MavenProject;
32 import org.codehaus.plexus.component.annotations.Component;
33 import org.codehaus.plexus.component.annotations.Requirement;
34 import org.codehaus.plexus.logging.Logger;
35
36 import java.util.ArrayList;
37 import java.util.Collection;
38 import java.util.HashMap;
39 import java.util.HashSet;
40 import java.util.Iterator;
41 import java.util.List;
42 import java.util.Map;
43 import java.util.Properties;
44 import java.util.Set;
45 import java.util.concurrent.Callable;
46 import java.util.concurrent.CompletionService;
47 import java.util.concurrent.ExecutionException;
48 import java.util.concurrent.ExecutorCompletionService;
49 import java.util.concurrent.ExecutorService;
50 import java.util.concurrent.Future;
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71 @Component( role = LifecycleWeaveBuilder.class )
72 public class LifecycleWeaveBuilder
73 {
74
75 @Requirement
76 private MojoExecutor mojoExecutor;
77
78 @Requirement
79 private BuilderCommon builderCommon;
80
81 @Requirement
82 private Logger logger;
83
84 @Requirement
85 private ExecutionEventCatapult eventCatapult;
86
87 private Map<MavenProject, MavenExecutionPlan> executionPlans = new HashMap<MavenProject, MavenExecutionPlan>();
88
89
90 public LifecycleWeaveBuilder()
91 {
92 }
93
94 public LifecycleWeaveBuilder( MojoExecutor mojoExecutor, BuilderCommon builderCommon, Logger logger,
95 ExecutionEventCatapult eventCatapult )
96 {
97 this.mojoExecutor = mojoExecutor;
98 this.builderCommon = builderCommon;
99 this.logger = logger;
100 this.eventCatapult = eventCatapult;
101 }
102
103 public void build( ProjectBuildList projectBuilds, ReactorContext buildContext, List<TaskSegment> taskSegments,
104 MavenSession session, ExecutorService executor, ReactorBuildStatus reactorBuildStatus )
105 throws ExecutionException, InterruptedException
106 {
107 ConcurrentBuildLogger concurrentBuildLogger = new ConcurrentBuildLogger();
108 CompletionService<ProjectSegment> service = new ExecutorCompletionService<ProjectSegment>( executor );
109
110 try
111 {
112 for ( MavenProject mavenProject : session.getProjects() )
113 {
114 Artifact mainArtifact = mavenProject.getArtifact();
115 if ( mainArtifact != null && !( mainArtifact instanceof ThreadLockedArtifact ) )
116 {
117 ThreadLockedArtifact threadLockedArtifact = new ThreadLockedArtifact( mainArtifact );
118 mavenProject.setArtifact( threadLockedArtifact );
119 }
120 }
121
122 final List<Future<ProjectSegment>> futures = new ArrayList<Future<ProjectSegment>>();
123 final Map<ProjectSegment, Future<MavenExecutionPlan>> plans =
124 new HashMap<ProjectSegment, Future<MavenExecutionPlan>>();
125
126 for ( TaskSegment taskSegment : taskSegments )
127 {
128 ProjectBuildList segmentChunks = projectBuilds.getByTaskSegment( taskSegment );
129 Set<Artifact> projectArtifacts = new HashSet<Artifact>();
130 for ( ProjectSegment segmentChunk : segmentChunks )
131 {
132 Artifact artifact = segmentChunk.getProject().getArtifact();
133 if ( artifact != null )
134 {
135 projectArtifacts.add( artifact );
136 }
137 }
138 for ( ProjectSegment projectBuild : segmentChunks )
139 {
140 plans.put( projectBuild, executor.submit( createEPFuture( projectBuild, projectArtifacts ) ) );
141 }
142
143 for ( ProjectSegment projectSegment : plans.keySet() )
144 {
145 executionPlans.put( projectSegment.getProject(), plans.get( projectSegment ).get() );
146
147 }
148 for ( ProjectSegment projectBuild : segmentChunks )
149 {
150 try
151 {
152 final MavenExecutionPlan executionPlan = plans.get( projectBuild ).get();
153
154 DependencyContext dependencyContext =
155 mojoExecutor.newDependencyContext( session, executionPlan.getMojoExecutions() );
156
157 final Callable<ProjectSegment> projectBuilder =
158 createCallableForBuildingOneFullModule( buildContext, session, reactorBuildStatus,
159 executionPlan, projectBuild, dependencyContext,
160 concurrentBuildLogger );
161
162 futures.add( service.submit( projectBuilder ) );
163 }
164 catch ( Exception e )
165 {
166 throw new ExecutionException( e );
167 }
168 }
169
170 for ( Future<ProjectSegment> buildFuture : futures )
171 {
172 buildFuture.get();
173
174 }
175 futures.clear();
176 }
177 }
178 finally
179 {
180 projectBuilds.closeAll();
181 }
182 logger.info( concurrentBuildLogger.toString() );
183 }
184
185 private Callable<MavenExecutionPlan> createEPFuture( final ProjectSegment projectSegment,
186 final Set<Artifact> projectArtifacts )
187 {
188 return new Callable<MavenExecutionPlan>()
189 {
190 public MavenExecutionPlan call()
191 throws Exception
192 {
193 return builderCommon.resolveBuildPlan( projectSegment.getSession(), projectSegment.getProject(),
194 projectSegment.getTaskSegment(), projectArtifacts );
195 }
196 };
197 }
198
199
200 private Callable<ProjectSegment> createCallableForBuildingOneFullModule( final ReactorContext reactorContext,
201 final MavenSession rootSession,
202 final ReactorBuildStatus reactorBuildStatus,
203 final MavenExecutionPlan executionPlan,
204 final ProjectSegment projectBuild,
205 final DependencyContext dependencyContext,
206 final ConcurrentBuildLogger concurrentBuildLogger )
207 {
208 return new Callable<ProjectSegment>()
209 {
210 public ProjectSegment call()
211 throws Exception
212 {
213 Iterator<ExecutionPlanItem> planItems = executionPlan.iterator();
214 ExecutionPlanItem current = planItems.hasNext() ? planItems.next() : null;
215 ThreadLockedArtifact threadLockedArtifact =
216 (ThreadLockedArtifact) projectBuild.getProject().getArtifact();
217 if ( threadLockedArtifact != null )
218 {
219 threadLockedArtifact.attachToThread();
220 }
221 long buildStartTime = System.currentTimeMillis();
222
223
224
225 if ( reactorBuildStatus.isHaltedOrBlacklisted( projectBuild.getProject() ) )
226 {
227 eventCatapult.fire( ExecutionEvent.Type.ProjectSkipped, projectBuild.getSession(), null );
228 return null;
229 }
230
231 eventCatapult.fire( ExecutionEvent.Type.ProjectStarted, projectBuild.getSession(), null );
232
233 Collection<ArtifactLink> dependencyLinks = getUpstreamReactorDependencies( projectBuild );
234
235 try
236 {
237 PhaseRecorder phaseRecorder = new PhaseRecorder( projectBuild.getProject() );
238 long totalMojoTime = 0;
239 long mojoStart;
240 while ( current != null && !reactorBuildStatus.isHaltedOrBlacklisted( projectBuild.getProject() ) )
241 {
242
243 BuildLogItem builtLogItem =
244 concurrentBuildLogger.createBuildLogItem( projectBuild.getProject(), current );
245 final Schedule schedule = current.getSchedule();
246
247 mojoStart = System.currentTimeMillis();
248 buildExecutionPlanItem( current, phaseRecorder, schedule, reactorContext, projectBuild,
249 dependencyContext );
250 totalMojoTime += ( System.currentTimeMillis() - mojoStart );
251
252 current.setComplete();
253 builtLogItem.setComplete();
254
255 ExecutionPlanItem nextPlanItem = planItems.hasNext() ? planItems.next() : null;
256 if ( nextPlanItem != null && phaseRecorder.isDifferentPhase( nextPlanItem.getMojoExecution() ) )
257 {
258
259 final Schedule scheduleOfNext = nextPlanItem.getSchedule();
260 if ( scheduleOfNext == null || !scheduleOfNext.isParallel() )
261 {
262 waitForAppropriateUpstreamExecutionsToFinish( builtLogItem, nextPlanItem, projectBuild,
263 scheduleOfNext );
264 }
265
266 for ( ArtifactLink dependencyLink : dependencyLinks )
267 {
268 dependencyLink.resolveFromUpstream();
269 }
270 }
271 current = nextPlanItem;
272 }
273
274 final BuildSuccess summary =
275 new BuildSuccess( projectBuild.getProject(), totalMojoTime );
276 reactorContext.getResult().addBuildSummary( summary );
277 eventCatapult.fire( ExecutionEvent.Type.ProjectSucceeded, projectBuild.getSession(), null );
278 }
279 catch ( Exception e )
280 {
281 builderCommon.handleBuildError( reactorContext, rootSession, projectBuild.getSession(),
282 projectBuild.getProject(), e, buildStartTime );
283 }
284 finally
285 {
286 if ( current != null )
287 {
288 executionPlan.forceAllComplete();
289 }
290
291 }
292 return null;
293 }
294
295 };
296 }
297
298 private void waitForAppropriateUpstreamExecutionsToFinish( BuildLogItem builtLogItem,
299 ExecutionPlanItem nextPlanItem,
300 ProjectSegment projectBuild, Schedule scheduleOfNext )
301 throws InterruptedException
302 {
303 for ( MavenProject upstreamProject : projectBuild.getImmediateUpstreamProjects() )
304 {
305 final MavenExecutionPlan upstreamPlan = executionPlans.get( upstreamProject );
306 final String nextPhase = scheduleOfNext != null && scheduleOfNext.hasUpstreamPhaseDefined()
307 ? scheduleOfNext.getUpstreamPhase()
308 : nextPlanItem.getLifecyclePhase();
309 final ExecutionPlanItem upstream = upstreamPlan.findLastInPhase( nextPhase );
310
311 if ( upstream != null )
312 {
313 long startWait = System.currentTimeMillis();
314 upstream.waitUntilDone();
315 builtLogItem.addWait( upstreamProject, upstream, startWait );
316 }
317 else if ( !upstreamPlan.containsPhase( nextPhase ) )
318 {
319
320
321
322 builtLogItem.addDependency( upstreamProject, "No phase tracking possible " );
323 upstreamPlan.waitUntilAllDone();
324 }
325 else
326 {
327 builtLogItem.addDependency( upstreamProject, "No schedule" );
328 }
329 }
330 }
331
332 private Collection<ArtifactLink> getUpstreamReactorDependencies( ProjectSegment projectBuild )
333 {
334 Collection<ArtifactLink> result = new ArrayList<ArtifactLink>();
335 for ( MavenProject upstreamProject : projectBuild.getTransitiveUpstreamProjects() )
336 {
337 Artifact upStreamArtifact = upstreamProject.getArtifact();
338 if ( upStreamArtifact != null )
339 {
340 Artifact dependencyArtifact = findDependency( projectBuild.getProject(), upStreamArtifact );
341 if ( dependencyArtifact != null )
342 {
343 result.add( new ArtifactLink( dependencyArtifact, upStreamArtifact ) );
344 }
345 }
346
347 Artifact upStreamTestScopedArtifact = findTestScopedArtifact( upstreamProject );
348 if ( upStreamTestScopedArtifact != null )
349 {
350 Artifact dependencyArtifact = findDependency( projectBuild.getProject(), upStreamArtifact );
351 if ( dependencyArtifact != null )
352 {
353 result.add( new ArtifactLink( dependencyArtifact, upStreamTestScopedArtifact ) );
354 }
355 }
356 }
357 return result;
358 }
359
360
361 private Artifact findTestScopedArtifact( MavenProject upstreamProject )
362 {
363 if ( upstreamProject == null )
364 {
365 return null;
366 }
367
368 List<Artifact> artifactList = upstreamProject.getAttachedArtifacts();
369 for ( Artifact artifact : artifactList )
370 {
371 if ( Artifact.SCOPE_TEST.equals( artifact.getScope() ) )
372 {
373 return artifact;
374 }
375 }
376 return null;
377 }
378
379 private static boolean isThreadLockedAndEmpty( Artifact artifact )
380 {
381 return artifact instanceof ThreadLockedArtifact && !( (ThreadLockedArtifact) artifact ).hasReal();
382 }
383
384 private static Artifact findDependency( MavenProject project, Artifact upStreamArtifact )
385 {
386 if ( upStreamArtifact == null || isThreadLockedAndEmpty( upStreamArtifact ) )
387 {
388 return null;
389 }
390
391 String key = ArtifactUtils.key( upStreamArtifact.getGroupId(), upStreamArtifact.getArtifactId(),
392 upStreamArtifact.getVersion() );
393 final Set<Artifact> deps = project.getDependencyArtifacts();
394 for ( Artifact dep : deps )
395 {
396 String depKey = ArtifactUtils.key( dep.getGroupId(), dep.getArtifactId(), dep.getVersion() );
397 if ( key.equals( depKey ) )
398 {
399 return dep;
400 }
401 }
402 return null;
403
404 }
405
406 private void buildExecutionPlanItem( ExecutionPlanItem current, PhaseRecorder phaseRecorder, Schedule schedule,
407 ReactorContext reactorContext, ProjectSegment projectBuild,
408 DependencyContext dependencyContext )
409 throws LifecycleExecutionException
410 {
411 if ( schedule != null && schedule.isMojoSynchronized() )
412 {
413 synchronized ( current.getPlugin() )
414 {
415 buildExecutionPlanItem( reactorContext, current, projectBuild, dependencyContext, phaseRecorder );
416 }
417 }
418 else
419 {
420 buildExecutionPlanItem( reactorContext, current, projectBuild, dependencyContext, phaseRecorder );
421 }
422 }
423
424
425 private void buildExecutionPlanItem( ReactorContext reactorContext, ExecutionPlanItem node,
426 ProjectSegment projectBuild, DependencyContext dependencyContext,
427 PhaseRecorder phaseRecorder )
428 throws LifecycleExecutionException
429 {
430
431 MavenProject currentProject = projectBuild.getProject();
432
433 long buildStartTime = System.currentTimeMillis();
434
435 CurrentPhaseForThread.setPhase( node.getLifecyclePhase() );
436
437 MavenSession sessionForThisModule = projectBuild.getSession();
438 try
439 {
440
441 if ( reactorContext.getReactorBuildStatus().isHaltedOrBlacklisted( currentProject ) )
442 {
443 return;
444 }
445
446 BuilderCommon.attachToThread( currentProject );
447
448 mojoExecutor.execute( sessionForThisModule, node.getMojoExecution(), reactorContext.getProjectIndex(),
449 dependencyContext, phaseRecorder );
450
451 final BuildSuccess summary =
452 new BuildSuccess( currentProject, System.currentTimeMillis() - buildStartTime );
453 reactorContext.getResult().addBuildSummary( summary );
454 }
455 finally
456 {
457 Thread.currentThread().setContextClassLoader( reactorContext.getOriginalContextClassLoader() );
458 }
459 }
460
461 public static boolean isWeaveMode( MavenExecutionRequest request )
462 {
463 return "true".equals( request.getUserProperties().getProperty( "maven3.weaveMode" ) );
464 }
465
466 public static void setWeaveMode( Properties properties )
467 {
468 properties.setProperty( "maven3.weaveMode", "true" );
469 }
470
471 static class ArtifactLink
472 {
473 private final Artifact artifactInThis;
474
475 private final Artifact upstream;
476
477 ArtifactLink( Artifact artifactInThis, Artifact upstream )
478 {
479 this.artifactInThis = artifactInThis;
480 this.upstream = upstream;
481 }
482
483 public void resolveFromUpstream()
484 {
485 artifactInThis.setFile( upstream.getFile() );
486 artifactInThis.setRepository( upstream.getRepository() );
487 artifactInThis.setResolved( true );
488 }
489 }
490 }