001package org.apache.maven.lifecycle.internal.builder.multithreaded;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *  http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import java.util.List;
023import java.util.Map;
024import java.util.concurrent.Callable;
025import java.util.concurrent.CompletionService;
026import java.util.concurrent.ExecutionException;
027import java.util.concurrent.ExecutorCompletionService;
028import java.util.concurrent.ExecutorService;
029import java.util.concurrent.Executors;
030import java.util.concurrent.Future;
031
032import org.apache.maven.execution.MavenSession;
033import org.apache.maven.lifecycle.internal.BuildThreadFactory;
034import org.apache.maven.lifecycle.internal.LifecycleModuleBuilder;
035import org.apache.maven.lifecycle.internal.ProjectBuildList;
036import org.apache.maven.lifecycle.internal.ProjectSegment;
037import org.apache.maven.lifecycle.internal.ReactorBuildStatus;
038import org.apache.maven.lifecycle.internal.ReactorContext;
039import org.apache.maven.lifecycle.internal.TaskSegment;
040import org.apache.maven.lifecycle.internal.builder.Builder;
041import org.apache.maven.project.MavenProject;
042import org.codehaus.plexus.component.annotations.Component;
043import org.codehaus.plexus.component.annotations.Requirement;
044import org.codehaus.plexus.logging.Logger;
045
046/**
047 * Builds the full lifecycle in weave-mode (phase by phase as opposed to project-by-project)
048 *
049 * @since 3.0
050 * @author Kristian Rosenvold
051 *         Builds one or more lifecycles for a full module
052 *         <p/>
053 *         NOTE: This class is not part of any public api and can be changed or deleted without prior notice.
054 */
055@Component( role = Builder.class, hint = "multithreaded" )
056public class MultiThreadedBuilder
057    implements Builder
058{
059
060    @Requirement
061    private Logger logger;
062
063    @Requirement
064    private LifecycleModuleBuilder lifecycleModuleBuilder;
065
066
067    public MultiThreadedBuilder()
068    {
069    }
070
071    @Override
072    public void build( MavenSession session, ReactorContext reactorContext, ProjectBuildList projectBuilds,
073                       List<TaskSegment> taskSegments, ReactorBuildStatus reactorBuildStatus )
074        throws ExecutionException, InterruptedException
075    {
076        ExecutorService executor =
077            Executors.newFixedThreadPool( Math.min( session.getRequest().getDegreeOfConcurrency(),
078                                                    session.getProjects().size() ), new BuildThreadFactory() );
079        CompletionService<ProjectSegment> service = new ExecutorCompletionService<>( executor );
080        ConcurrencyDependencyGraph analyzer =
081            new ConcurrencyDependencyGraph( projectBuilds, session.getProjectDependencyGraph() );
082
083        // Currently disabled
084        ThreadOutputMuxer muxer = null; // new ThreadOutputMuxer( analyzer.getProjectBuilds(), System.out );
085
086        for ( TaskSegment taskSegment : taskSegments )
087        {
088            Map<MavenProject, ProjectSegment> projectBuildMap = projectBuilds.selectSegment( taskSegment );
089            try
090            {
091                multiThreadedProjectTaskSegmentBuild( analyzer, reactorContext, session, service, taskSegment,
092                                                      projectBuildMap, muxer );
093                if ( reactorContext.getReactorBuildStatus().isHalted() )
094                {
095                    break;
096                }
097            }
098            catch ( Exception e )
099            {
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 | ExecutionException e )
169            {
170                throw new RuntimeException( e );
171            }
172        }
173    }
174
175    private Callable<ProjectSegment> createBuildCallable( final MavenSession rootSession,
176                                                          final ProjectSegment projectBuild,
177                                                          final ReactorContext reactorContext,
178                                                          final TaskSegment taskSegment, final ThreadOutputMuxer muxer )
179    {
180        return new Callable<ProjectSegment>()
181        {
182            public ProjectSegment call()
183            {
184                // muxer.associateThreadWithProjectSegment( projectBuild );
185                lifecycleModuleBuilder.buildProject( projectBuild.getSession(), rootSession, reactorContext,
186                                                     projectBuild.getProject(), taskSegment );
187                // muxer.setThisModuleComplete( projectBuild );
188
189                return projectBuild;
190            }
191        };
192    }
193}