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