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