1 package org.apache.maven.lifecycle.internal.builder.multithreaded;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.util.HashSet;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.Set;
26 import java.util.concurrent.Callable;
27 import java.util.concurrent.CompletionService;
28 import java.util.concurrent.ExecutionException;
29 import java.util.concurrent.ExecutorCompletionService;
30 import java.util.concurrent.ExecutorService;
31 import java.util.concurrent.Executors;
32 import java.util.concurrent.TimeUnit;
33
34 import org.apache.maven.execution.MavenSession;
35 import org.apache.maven.lifecycle.internal.BuildThreadFactory;
36 import org.apache.maven.lifecycle.internal.LifecycleModuleBuilder;
37 import org.apache.maven.lifecycle.internal.ProjectBuildList;
38 import org.apache.maven.lifecycle.internal.ProjectSegment;
39 import org.apache.maven.lifecycle.internal.ReactorBuildStatus;
40 import org.apache.maven.lifecycle.internal.ReactorContext;
41 import org.apache.maven.lifecycle.internal.TaskSegment;
42 import org.apache.maven.lifecycle.internal.builder.Builder;
43 import org.apache.maven.project.MavenProject;
44 import org.codehaus.plexus.component.annotations.Component;
45 import org.codehaus.plexus.component.annotations.Requirement;
46 import org.codehaus.plexus.logging.Logger;
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62 @Component( role = Builder.class, hint = "multithreaded" )
63 public class MultiThreadedBuilder
64 implements Builder
65 {
66
67 @Requirement
68 private Logger logger;
69
70 @Requirement
71 private LifecycleModuleBuilder lifecycleModuleBuilder;
72
73 public MultiThreadedBuilder()
74 {
75 }
76
77 @Override
78 public void build( MavenSession session, ReactorContext reactorContext, ProjectBuildList projectBuilds,
79 List<TaskSegment> taskSegments, ReactorBuildStatus reactorBuildStatus )
80 throws ExecutionException, InterruptedException
81 {
82 int nThreads = Math.min( session.getRequest().getDegreeOfConcurrency(), session.getProjects().size() );
83 boolean parallel = nThreads >= 2;
84
85 session.setParallel( parallel );
86 for ( ProjectSegment segment : projectBuilds )
87 {
88 segment.getSession().setParallel( parallel );
89 }
90 ExecutorService executor = Executors.newFixedThreadPool( nThreads, new BuildThreadFactory() );
91 CompletionService<ProjectSegment> service = new ExecutorCompletionService<>( executor );
92
93
94 ThreadOutputMuxer muxer = null;
95
96 for ( TaskSegment taskSegment : taskSegments )
97 {
98 ProjectBuildList segmentProjectBuilds = projectBuilds.getByTaskSegment( taskSegment );
99 Map<MavenProject, ProjectSegment> projectBuildMap = projectBuilds.selectSegment( taskSegment );
100 try
101 {
102 ConcurrencyDependencyGraph analyzer =
103 new ConcurrencyDependencyGraph( segmentProjectBuilds,
104 session.getProjectDependencyGraph() );
105 multiThreadedProjectTaskSegmentBuild( analyzer, reactorContext, session, service, taskSegment,
106 projectBuildMap, muxer );
107 if ( reactorContext.getReactorBuildStatus().isHalted() )
108 {
109 break;
110 }
111 }
112 catch ( Exception e )
113 {
114 session.getResult().addException( e );
115 break;
116 }
117
118 }
119
120 executor.shutdown();
121 executor.awaitTermination( Long.MAX_VALUE, TimeUnit.MILLISECONDS );
122 }
123
124 private void multiThreadedProjectTaskSegmentBuild( ConcurrencyDependencyGraph analyzer,
125 ReactorContext reactorContext, MavenSession rootSession,
126 CompletionService<ProjectSegment> service,
127 TaskSegment taskSegment,
128 Map<MavenProject, ProjectSegment> projectBuildList,
129 ThreadOutputMuxer muxer )
130 {
131
132
133 Set<String> duplicateArtifactIds = gatherDuplicateArtifactIds( projectBuildList.keySet() );
134
135
136 for ( MavenProject mavenProject : analyzer.getRootSchedulableBuilds() )
137 {
138 ProjectSegment projectSegment = projectBuildList.get( mavenProject );
139 logger.debug( "Scheduling: " + projectSegment.getProject() );
140 Callable<ProjectSegment> cb =
141 createBuildCallable( rootSession, projectSegment, reactorContext, taskSegment, muxer,
142 duplicateArtifactIds );
143 service.submit( cb );
144 }
145
146
147 for ( int i = 0; i < analyzer.getNumberOfBuilds(); i++ )
148 {
149 try
150 {
151 ProjectSegment projectBuild = service.take().get();
152 if ( reactorContext.getReactorBuildStatus().isHalted() )
153 {
154 break;
155 }
156
157
158 if ( analyzer.getNumberOfBuilds() > 1 )
159 {
160 final List<MavenProject> newItemsThatCanBeBuilt =
161 analyzer.markAsFinished( projectBuild.getProject() );
162 for ( MavenProject mavenProject : newItemsThatCanBeBuilt )
163 {
164 ProjectSegment scheduledDependent = projectBuildList.get( mavenProject );
165 logger.debug( "Scheduling: " + scheduledDependent );
166 Callable<ProjectSegment> cb =
167 createBuildCallable( rootSession, scheduledDependent, reactorContext, taskSegment, muxer,
168 duplicateArtifactIds );
169 service.submit( cb );
170 }
171 }
172 }
173 catch ( InterruptedException e )
174 {
175 rootSession.getResult().addException( e );
176 break;
177 }
178 catch ( ExecutionException e )
179 {
180
181 rootSession.getResult().addException( e );
182 break;
183 }
184 }
185 }
186
187 private Callable<ProjectSegment> createBuildCallable( final MavenSession rootSession,
188 final ProjectSegment projectBuild,
189 final ReactorContext reactorContext,
190 final TaskSegment taskSegment,
191 final ThreadOutputMuxer muxer,
192 final Set<String> duplicateArtifactIds )
193 {
194 return new Callable<ProjectSegment>()
195 {
196 public ProjectSegment call()
197 {
198 final Thread currentThread = Thread.currentThread();
199 final String originalThreadName = currentThread.getName();
200 final MavenProject project = projectBuild.getProject();
201
202 final String threadNameSuffix = duplicateArtifactIds.contains( project.getArtifactId() )
203 ? project.getGroupId() + ":" + project.getArtifactId()
204 : project.getArtifactId();
205 currentThread.setName( "mvn-builder-" + threadNameSuffix );
206
207 try
208 {
209
210 lifecycleModuleBuilder.buildProject( projectBuild.getSession(), rootSession, reactorContext,
211 project, taskSegment );
212
213
214 return projectBuild;
215 }
216 finally
217 {
218 currentThread.setName( originalThreadName );
219 }
220 }
221 };
222 }
223
224 private Set<String> gatherDuplicateArtifactIds( Set<MavenProject> projects )
225 {
226 Set<String> artifactIds = new HashSet<>( projects.size() );
227 Set<String> duplicateArtifactIds = new HashSet<>();
228 for ( MavenProject project : projects )
229 {
230 if ( !artifactIds.add( project.getArtifactId() ) )
231 {
232 duplicateArtifactIds.add( project.getArtifactId() );
233 }
234 }
235 return duplicateArtifactIds;
236 }
237 }