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