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