View Javadoc
1   package org.apache.maven.lifecycle.internal.builder.multithreaded;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.util.List;
23  import java.util.Map;
24  import java.util.concurrent.Callable;
25  import java.util.concurrent.CompletionService;
26  import java.util.concurrent.ExecutionException;
27  import java.util.concurrent.ExecutorCompletionService;
28  import java.util.concurrent.ExecutorService;
29  import java.util.concurrent.Executors;
30  import java.util.concurrent.TimeUnit;
31  
32  import org.apache.maven.execution.MavenSession;
33  import org.apache.maven.lifecycle.internal.BuildThreadFactory;
34  import org.apache.maven.lifecycle.internal.LifecycleModuleBuilder;
35  import org.apache.maven.lifecycle.internal.ProjectBuildList;
36  import org.apache.maven.lifecycle.internal.ProjectSegment;
37  import org.apache.maven.lifecycle.internal.ReactorBuildStatus;
38  import org.apache.maven.lifecycle.internal.ReactorContext;
39  import org.apache.maven.lifecycle.internal.TaskSegment;
40  import org.apache.maven.lifecycle.internal.builder.Builder;
41  import org.apache.maven.project.MavenProject;
42  import org.codehaus.plexus.component.annotations.Component;
43  import org.codehaus.plexus.component.annotations.Requirement;
44  import org.codehaus.plexus.logging.Logger;
45  
46  /**
47   * Builds the full lifecycle in weave-mode (phase by phase as opposed to project-by-project).
48   * <p>
49   * This builder uses a number of threads equal to the minimum of the degree of concurrency (which is the thread count
50   * set with <code>-T</code> on the command-line) and the number of projects to build. As such, building a single project
51   * will always result in a sequential build, regardless of the thread count.
52   * </p>
53   * <strong>NOTE:</strong> This class is not part of any public api and can be changed or deleted without prior notice.
54   *
55   * @since 3.0
56   * @author Kristian Rosenvold
57   *         Builds one or more lifecycles for a full module
58   *         NOTE: This class is not part of any public api and can be changed or deleted without prior notice.
59   */
60  @Component( role = Builder.class, hint = "multithreaded" )
61  public class MultiThreadedBuilder
62      implements Builder
63  {
64  
65      @Requirement
66      private Logger logger;
67  
68      @Requirement
69      private LifecycleModuleBuilder lifecycleModuleBuilder;
70  
71      public MultiThreadedBuilder()
72      {
73      }
74  
75      @Override
76      public void build( MavenSession session, ReactorContext reactorContext, ProjectBuildList projectBuilds,
77                         List<TaskSegment> taskSegments, ReactorBuildStatus reactorBuildStatus )
78          throws ExecutionException, InterruptedException
79      {
80          int nThreads = Math.min( session.getRequest().getDegreeOfConcurrency(), session.getProjects().size() );
81          boolean parallel = nThreads >= 2;
82          // Propagate the parallel flag to the root session and all of the cloned sessions in each project segment
83          session.setParallel( parallel );
84          for ( ProjectSegment segment : projectBuilds )
85          {
86              segment.getSession().setParallel( parallel );
87          }
88          ExecutorService executor = Executors.newFixedThreadPool( nThreads, new BuildThreadFactory() );
89          CompletionService<ProjectSegment> service = new ExecutorCompletionService<>( executor );
90  
91          // Currently disabled
92          ThreadOutputMuxer muxer = null; // new ThreadOutputMuxer( analyzer.getProjectBuilds(), System.out );
93  
94          for ( TaskSegment taskSegment : taskSegments )
95          {
96              ProjectBuildList segmentProjectBuilds = projectBuilds.getByTaskSegment( taskSegment );
97              Map<MavenProject, ProjectSegment> projectBuildMap = projectBuilds.selectSegment( taskSegment );
98              try
99              {
100                 ConcurrencyDependencyGraph analyzer =
101                     new ConcurrencyDependencyGraph( segmentProjectBuilds,
102                                                     session.getProjectDependencyGraph() );
103                 multiThreadedProjectTaskSegmentBuild( analyzer, reactorContext, session, service, taskSegment,
104                                                       projectBuildMap, muxer );
105                 if ( reactorContext.getReactorBuildStatus().isHalted() )
106                 {
107                     break;
108                 }
109             }
110             catch ( Exception e )
111             {
112                 session.getResult().addException( e );
113                 break;
114             }
115 
116         }
117 
118         executor.shutdown();
119         executor.awaitTermination( Long.MAX_VALUE, TimeUnit.MILLISECONDS );
120     }
121 
122     private void multiThreadedProjectTaskSegmentBuild( ConcurrencyDependencyGraph analyzer,
123                                                        ReactorContext reactorContext, MavenSession rootSession,
124                                                        CompletionService<ProjectSegment> service,
125                                                        TaskSegment taskSegment,
126                                                        Map<MavenProject, ProjectSegment> projectBuildList,
127                                                        ThreadOutputMuxer muxer )
128     {
129 
130         // schedule independent projects
131         for ( MavenProject mavenProject : analyzer.getRootSchedulableBuilds() )
132         {
133             ProjectSegment projectSegment = projectBuildList.get( mavenProject );
134             logger.debug( "Scheduling: " + projectSegment.getProject() );
135             Callable<ProjectSegment> cb =
136                 createBuildCallable( rootSession, projectSegment, reactorContext, taskSegment, muxer );
137             service.submit( cb );
138         }
139 
140         // for each finished project
141         for ( int i = 0; i < analyzer.getNumberOfBuilds(); i++ )
142         {
143             try
144             {
145                 ProjectSegment projectBuild = service.take().get();
146                 if ( reactorContext.getReactorBuildStatus().isHalted() )
147                 {
148                     break;
149                 }
150 
151                 // MNG-6170: Only schedule other modules from reactor if we have more modules to build than one.
152                 if ( analyzer.getNumberOfBuilds() > 1 )
153                 {
154                     final List<MavenProject> newItemsThatCanBeBuilt =
155                         analyzer.markAsFinished( projectBuild.getProject() );
156                     for ( MavenProject mavenProject : newItemsThatCanBeBuilt )
157                     {
158                         ProjectSegment scheduledDependent = projectBuildList.get( mavenProject );
159                         logger.debug( "Scheduling: " + scheduledDependent );
160                         Callable<ProjectSegment> cb =
161                             createBuildCallable( rootSession, scheduledDependent, reactorContext, taskSegment, muxer );
162                         service.submit( cb );
163                     }
164                 }
165             }
166             catch ( InterruptedException e )
167             {
168                 rootSession.getResult().addException( e );
169                 break;
170             }
171             catch ( ExecutionException e )
172             {
173                 // TODO MNG-5766 changes likely made this redundant
174                 rootSession.getResult().addException( e );
175                 break;
176             }
177         }
178     }
179 
180     private Callable<ProjectSegment> createBuildCallable( final MavenSession rootSession,
181                                                           final ProjectSegment projectBuild,
182                                                           final ReactorContext reactorContext,
183                                                           final TaskSegment taskSegment, final ThreadOutputMuxer muxer )
184     {
185         return new Callable<ProjectSegment>()
186         {
187             public ProjectSegment call()
188             {
189                 final Thread currentThread = Thread.currentThread();
190                 final String originalThreadName = currentThread.getName();
191                 currentThread.setName( "mvn-builder-" + projectBuild.getProject().getId() );
192 
193                 try
194                 {
195                     // muxer.associateThreadWithProjectSegment( projectBuild );
196                     lifecycleModuleBuilder.buildProject( projectBuild.getSession(), rootSession, reactorContext,
197                                                          projectBuild.getProject(), taskSegment );
198                     // muxer.setThisModuleComplete( projectBuild );
199 
200                     return projectBuild;
201                 }
202                 finally
203                 {
204                      currentThread.setName( originalThreadName );
205                 }
206             }
207         };
208     }
209 }