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.Future;
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   *
53   * @since 3.0
54   * @author Kristian Rosenvold
55   *         Builds one or more lifecycles for a full module
56   *         <p/>
57   *         NOTE: This class is not part of any public api and can be changed or deleted without prior notice.
58   */
59  @Component( role = Builder.class, hint = "multithreaded" )
60  public class MultiThreadedBuilder
61      implements Builder
62  {
63  
64      @Requirement
65      private Logger logger;
66  
67      @Requirement
68      private LifecycleModuleBuilder lifecycleModuleBuilder;
69  
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          ConcurrencyDependencyGraph analyzer =
91              new ConcurrencyDependencyGraph( projectBuilds, session.getProjectDependencyGraph() );
92  
93          // Currently disabled
94          ThreadOutputMuxer muxer = null; // new ThreadOutputMuxer( analyzer.getProjectBuilds(), System.out );
95  
96          for ( TaskSegment taskSegment : taskSegments )
97          {
98              Map<MavenProject, ProjectSegment> projectBuildMap = projectBuilds.selectSegment( taskSegment );
99              try
100             {
101                 multiThreadedProjectTaskSegmentBuild( analyzer, reactorContext, session, service, taskSegment,
102                                                       projectBuildMap, muxer );
103                 if ( reactorContext.getReactorBuildStatus().isHalted() )
104                 {
105                     break;
106                 }
107             }
108             catch ( Exception e )
109             {
110                 session.getResult().addException( e );
111                 break;
112             }
113 
114         }
115     }
116 
117     private void multiThreadedProjectTaskSegmentBuild( ConcurrencyDependencyGraph analyzer,
118                                                        ReactorContext reactorContext, MavenSession rootSession,
119                                                        CompletionService<ProjectSegment> service,
120                                                        TaskSegment taskSegment,
121                                                        Map<MavenProject, ProjectSegment> projectBuildList,
122                                                        ThreadOutputMuxer muxer )
123     {
124 
125         // schedule independent projects
126         for ( MavenProject mavenProject : analyzer.getRootSchedulableBuilds() )
127         {
128             ProjectSegment projectSegment = projectBuildList.get( mavenProject );
129             logger.debug( "Scheduling: " + projectSegment.getProject() );
130             Callable<ProjectSegment> cb =
131                 createBuildCallable( rootSession, projectSegment, reactorContext, taskSegment, muxer );
132             service.submit( cb );
133         }
134 
135         // for each finished project
136         for ( int i = 0; i < analyzer.getNumberOfBuilds(); i++ )
137         {
138             try
139             {
140                 ProjectSegment projectBuild = service.take().get();
141                 if ( reactorContext.getReactorBuildStatus().isHalted() )
142                 {
143                     break;
144                 }
145                 final List<MavenProject> newItemsThatCanBeBuilt =
146                     analyzer.markAsFinished( projectBuild.getProject() );
147                 for ( MavenProject mavenProject : newItemsThatCanBeBuilt )
148                 {
149                     ProjectSegment scheduledDependent = projectBuildList.get( mavenProject );
150                     logger.debug( "Scheduling: " + scheduledDependent );
151                     Callable<ProjectSegment> cb =
152                         createBuildCallable( rootSession, scheduledDependent, reactorContext, taskSegment, muxer );
153                     service.submit( cb );
154                 }
155             }
156             catch ( InterruptedException e )
157             {
158                 rootSession.getResult().addException( e );
159                 break;
160             }
161             catch ( ExecutionException e )
162             {
163                 // TODO MNG-5766 changes likely made this redundant 
164                 rootSession.getResult().addException( e );
165                 break;
166             }
167         }
168 
169         // cancel outstanding builds (if any)  - this can happen if an exception is thrown in above block
170 
171         Future<ProjectSegment> unprocessed;
172         while ( ( unprocessed = service.poll() ) != null )
173         {
174             try
175             {
176                 unprocessed.get();
177             }
178             catch ( InterruptedException | ExecutionException e )
179             {
180                 throw new RuntimeException( e );
181             }
182         }
183     }
184 
185     private Callable<ProjectSegment> createBuildCallable( final MavenSession rootSession,
186                                                           final ProjectSegment projectBuild,
187                                                           final ReactorContext reactorContext,
188                                                           final TaskSegment taskSegment, final ThreadOutputMuxer muxer )
189     {
190         return new Callable<ProjectSegment>()
191         {
192             public ProjectSegment call()
193             {
194                 // muxer.associateThreadWithProjectSegment( projectBuild );
195                 lifecycleModuleBuilder.buildProject( projectBuild.getSession(), rootSession, reactorContext,
196                                                      projectBuild.getProject(), taskSegment );
197                 // muxer.setThisModuleComplete( projectBuild );
198 
199                 return projectBuild;
200             }
201         };
202     }
203 }