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 @SuppressWarnings( { "UnusedDeclaration" } )
91 public LifecycleWeaveBuilder()
92 {
93 }
94
95 public LifecycleWeaveBuilder( MojoExecutor mojoExecutor, BuilderCommon builderCommon, Logger logger,
96 ExecutionEventCatapult eventCatapult )
97 {
98 this.mojoExecutor = mojoExecutor;
99 this.builderCommon = builderCommon;
100 this.logger = logger;
101 this.eventCatapult = eventCatapult;
102 }
103
104 public void build( ProjectBuildList projectBuilds, ReactorContext buildContext, List<TaskSegment> taskSegments,
105 MavenSession session, ExecutorService executor, ReactorBuildStatus reactorBuildStatus )
106 throws ExecutionException, InterruptedException
107 {
108 ConcurrentBuildLogger concurrentBuildLogger = new ConcurrentBuildLogger();
109 CompletionService<ProjectSegment> service = new ExecutorCompletionService<ProjectSegment>( executor );
110
111 try
112 {
113 for ( MavenProject mavenProject : session.getProjects() )
114 {
115 Artifact mainArtifact = mavenProject.getArtifact();
116 if ( mainArtifact != null && !( mainArtifact instanceof ThreadLockedArtifact ) )
117 {
118 ThreadLockedArtifact threadLockedArtifact = new ThreadLockedArtifact( mainArtifact );
119 mavenProject.setArtifact( threadLockedArtifact );
120 }
121 }
122
123 final List<Future<ProjectSegment>> futures = new ArrayList<Future<ProjectSegment>>();
124 final Map<ProjectSegment, Future<MavenExecutionPlan>> plans =
125 new HashMap<ProjectSegment, Future<MavenExecutionPlan>>();
126
127 for ( TaskSegment taskSegment : taskSegments )
128 {
129 ProjectBuildList segmentChunks = projectBuilds.getByTaskSegment( taskSegment );
130 Set<Artifact> projectArtifacts = new HashSet<Artifact>();
131 for ( ProjectSegment segmentChunk : segmentChunks )
132 {
133 Artifact artifact = segmentChunk.getProject().getArtifact();
134 if ( artifact != null )
135 {
136 projectArtifacts.add( artifact );
137 }
138 }
139 for ( ProjectSegment projectBuild : segmentChunks )
140 {
141 plans.put( projectBuild, executor.submit( createEPFuture( projectBuild, projectArtifacts ) ) );
142 }
143
144 for ( ProjectSegment projectSegment : plans.keySet() )
145 {
146 executionPlans.put( projectSegment.getProject(), plans.get( projectSegment ).get() );
147
148 }
149 for ( ProjectSegment projectBuild : segmentChunks )
150 {
151 try
152 {
153 final MavenExecutionPlan executionPlan = plans.get( projectBuild ).get();
154
155 DependencyContext dependencyContext =
156 mojoExecutor.newDependencyContext( session, executionPlan.getMojoExecutions() );
157
158 final Callable<ProjectSegment> projectBuilder =
159 createCallableForBuildingOneFullModule( buildContext, session, reactorBuildStatus,
160 executionPlan, projectBuild, dependencyContext,
161 concurrentBuildLogger );
162
163 futures.add( service.submit( projectBuilder ) );
164 }
165 catch ( Exception e )
166 {
167 throw new ExecutionException( e );
168 }
169 }
170
171 for ( Future<ProjectSegment> buildFuture : futures )
172 {
173 buildFuture.get();
174
175 }
176 futures.clear();
177 }
178 }
179 finally
180 {
181 projectBuilds.closeAll();
182 }
183 logger.info( concurrentBuildLogger.toString() );
184 }
185
186 private Callable<MavenExecutionPlan> createEPFuture( final ProjectSegment projectSegment,
187 final Set<Artifact> projectArtifacts )
188 {
189 return new Callable<MavenExecutionPlan>()
190 {
191 public MavenExecutionPlan call()
192 throws Exception
193 {
194 return builderCommon.resolveBuildPlan( projectSegment.getSession(), projectSegment.getProject(),
195 projectSegment.getTaskSegment(), projectArtifacts );
196 }
197 };
198 }
199
200
201 private Callable<ProjectSegment> createCallableForBuildingOneFullModule( final ReactorContext reactorContext,
202 final MavenSession rootSession,
203 final ReactorBuildStatus reactorBuildStatus,
204 final MavenExecutionPlan executionPlan,
205 final ProjectSegment projectBuild,
206 final DependencyContext dependencyContext,
207 final ConcurrentBuildLogger concurrentBuildLogger )
208 {
209 return new Callable<ProjectSegment>()
210 {
211 public ProjectSegment call()
212 throws Exception
213 {
214 Iterator<ExecutionPlanItem> planItems = executionPlan.iterator();
215 ExecutionPlanItem current = planItems.hasNext() ? planItems.next() : null;
216 ThreadLockedArtifact threadLockedArtifact = (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.getProject(), e,
282 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 return artifact instanceof ThreadLockedArtifact && !((ThreadLockedArtifact) artifact).hasReal();
381 }
382
383 private static Artifact findDependency( MavenProject project, Artifact upStreamArtifact )
384 {
385 if ( upStreamArtifact == null || isThreadLockedAndEmpty(upStreamArtifact))
386 {
387 return null;
388 }
389
390 String key = ArtifactUtils.key( upStreamArtifact.getGroupId(), upStreamArtifact.getArtifactId(),
391 upStreamArtifact.getVersion() );
392 final Set<Artifact> deps = project.getDependencyArtifacts();
393 for ( Artifact dep : deps )
394 {
395 String depKey = ArtifactUtils.key( dep.getGroupId(), dep.getArtifactId(), dep.getVersion() );
396 if ( key.equals( depKey ) )
397 {
398 return dep;
399 }
400 }
401 return null;
402
403 }
404
405 private void buildExecutionPlanItem( ExecutionPlanItem current, PhaseRecorder phaseRecorder, Schedule schedule,
406 ReactorContext reactorContext, ProjectSegment projectBuild,
407 DependencyContext dependencyContext )
408 throws LifecycleExecutionException
409 {
410 if ( schedule != null && schedule.isMojoSynchronized() )
411 {
412 synchronized ( current.getPlugin() )
413 {
414 buildExecutionPlanItem( reactorContext, current, projectBuild, dependencyContext, phaseRecorder );
415 }
416 }
417 else
418 {
419 buildExecutionPlanItem( reactorContext, current, projectBuild, dependencyContext, phaseRecorder );
420 }
421 }
422
423
424 private void buildExecutionPlanItem( ReactorContext reactorContext, ExecutionPlanItem node,
425 ProjectSegment projectBuild, DependencyContext dependencyContext,
426 PhaseRecorder phaseRecorder )
427 throws LifecycleExecutionException
428 {
429
430 MavenProject currentProject = projectBuild.getProject();
431
432 long buildStartTime = System.currentTimeMillis();
433
434 CurrentPhaseForThread.setPhase( node.getLifecyclePhase() );
435
436 MavenSession sessionForThisModule = projectBuild.getSession();
437 try
438 {
439
440 if ( reactorContext.getReactorBuildStatus().isHaltedOrBlacklisted( currentProject ) )
441 {
442 return;
443 }
444
445 BuilderCommon.attachToThread( currentProject );
446
447 mojoExecutor.execute( sessionForThisModule, node.getMojoExecution(), reactorContext.getProjectIndex(),
448 dependencyContext, phaseRecorder );
449
450 final BuildSuccess summary =
451 new BuildSuccess( currentProject, System.currentTimeMillis() - buildStartTime );
452 reactorContext.getResult().addBuildSummary( summary );
453 }
454 finally
455 {
456 Thread.currentThread().setContextClassLoader( reactorContext.getOriginalContextClassLoader() );
457 }
458 }
459
460 public static boolean isWeaveMode( MavenExecutionRequest request )
461 {
462 return "true".equals( request.getUserProperties().getProperty( "maven3.weaveMode" ) );
463 }
464
465 public static void setWeaveMode( Properties properties )
466 {
467 properties.setProperty( "maven3.weaveMode", "true" );
468 }
469
470 static class ArtifactLink
471 {
472 private final Artifact artifactInThis;
473
474 private final Artifact upstream;
475
476 ArtifactLink( Artifact artifactInThis, Artifact upstream )
477 {
478 this.artifactInThis = artifactInThis;
479 this.upstream = upstream;
480 }
481
482 public void resolveFromUpstream()
483 {
484 artifactInThis.setFile( upstream.getFile() );
485 artifactInThis.setRepository( upstream.getRepository() );
486 artifactInThis.setResolved( true );
487
488 }
489 }
490
491 }