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