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 org.apache.maven.execution.ProjectDependencyGraph;
023import org.apache.maven.lifecycle.internal.ProjectBuildList;
024import org.apache.maven.lifecycle.internal.ProjectSegment;
025import org.apache.maven.project.MavenProject;
026
027import java.util.ArrayList;
028import java.util.HashSet;
029import java.util.List;
030import java.util.Set;
031
032/**
033 * Presents a view of the Dependency Graph that is suited for concurrent building.
034 *
035 * @since 3.0
036 * @author Kristian Rosenvold
037 *         <p/>
038 *         NOTE: This class is not part of any public api and can be changed or deleted without prior notice.
039 */
040public class ConcurrencyDependencyGraph
041{
042
043    private final ProjectBuildList projectBuilds;
044
045    private final ProjectDependencyGraph projectDependencyGraph;
046
047    private final HashSet<MavenProject> finishedProjects = new HashSet<>();
048
049    public ConcurrencyDependencyGraph( ProjectBuildList projectBuilds, ProjectDependencyGraph projectDependencyGraph )
050    {
051        this.projectDependencyGraph = projectDependencyGraph;
052        this.projectBuilds = projectBuilds;
053    }
054
055    public int getNumberOfBuilds()
056    {
057        return projectBuilds.size();
058    }
059
060    /**
061     * Gets all the builds that have no reactor-dependencies
062     *
063     * @return A list of all the initial builds
064     */
065
066    public List<MavenProject> getRootSchedulableBuilds()
067    {
068        List<MavenProject> result = new ArrayList<>();
069        for ( ProjectSegment projectBuild : projectBuilds )
070        {
071            if ( projectDependencyGraph.getUpstreamProjects( projectBuild.getProject(), false ).size() == 0 )
072            {
073                result.add( projectBuild.getProject() );
074            }
075        }
076        return result;
077    }
078
079    /**
080     * Marks the provided project as finished. Returns a list of
081     *
082     * @param mavenProject The project
083     * @return The list of builds that are eligible for starting now that the provided project is done
084     */
085    public List<MavenProject> markAsFinished( MavenProject mavenProject )
086    {
087        finishedProjects.add( mavenProject );
088        return getSchedulableNewProcesses( mavenProject );
089    }
090
091    private List<MavenProject> getSchedulableNewProcesses( MavenProject finishedProject )
092    {
093        List<MavenProject> result = new ArrayList<>();
094        // schedule dependent projects, if all of their requirements are met
095        for ( MavenProject dependentProject : projectDependencyGraph.getDownstreamProjects( finishedProject, false ) )
096        {
097            final List<MavenProject> upstreamProjects =
098                projectDependencyGraph.getUpstreamProjects( dependentProject, false );
099            if ( finishedProjects.containsAll( upstreamProjects ) )
100            {
101                result.add( dependentProject );
102            }
103        }
104        return result;
105    }
106
107    /**
108     * @return set of projects that have yet to be processed successfully by the build.
109     */
110    public Set<MavenProject> getUnfinishedProjects()
111    {
112        Set<MavenProject> unfinished = new HashSet<>( projectBuilds.getProjects() );
113        unfinished.remove( finishedProjects );
114        return unfinished;
115    }
116
117    /**
118     * @return set of projects that have been successfully processed by the build.
119     */
120    protected Set<MavenProject> getFinishedProjects()
121    {
122        return finishedProjects;
123    }
124
125    protected ProjectBuildList getProjectBuilds()
126    {
127        return projectBuilds;
128    }
129
130    /**
131     * For the given {@link MavenProject} {@code p}, return all of {@code p}'s dependencies.
132     *
133     * @param p
134     * @return List of prerequisite projects
135     */
136    protected List<MavenProject> getDependencies( MavenProject p )
137    {
138        return projectDependencyGraph.getUpstreamProjects( p, false );
139    }
140
141    /**
142     * For the given {@link MavenProject} {@code p} return {@code p}'s uncompleted dependencies.
143     *
144     * @param p
145     * @return List of uncompleted prerequisite projects
146     */
147    public List<MavenProject> getActiveDependencies( MavenProject p )
148    {
149        List<MavenProject> activeDependencies = projectDependencyGraph.getUpstreamProjects( p, false );
150        activeDependencies.removeAll( finishedProjects );
151        return activeDependencies;
152    }
153}