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