1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  package org.apache.maven.lifecycle.internal.concurrent;
20  
21  import javax.inject.Inject;
22  import javax.inject.Named;
23  import javax.inject.Singleton;
24  
25  import java.util.ArrayList;
26  import java.util.List;
27  import java.util.stream.Collectors;
28  import java.util.stream.Stream;
29  
30  import org.apache.maven.execution.ExecutionEvent;
31  import org.apache.maven.execution.MavenSession;
32  import org.apache.maven.lifecycle.DefaultLifecycles;
33  import org.apache.maven.lifecycle.MissingProjectException;
34  import org.apache.maven.lifecycle.NoGoalSpecifiedException;
35  import org.apache.maven.lifecycle.internal.ExecutionEventCatapult;
36  import org.apache.maven.lifecycle.internal.GoalTask;
37  import org.apache.maven.lifecycle.internal.LifecyclePluginResolver;
38  import org.apache.maven.lifecycle.internal.LifecycleStarter;
39  import org.apache.maven.lifecycle.internal.LifecycleTask;
40  import org.apache.maven.lifecycle.internal.MojoDescriptorCreator;
41  import org.apache.maven.lifecycle.internal.ReactorBuildStatus;
42  import org.apache.maven.lifecycle.internal.ReactorContext;
43  import org.apache.maven.lifecycle.internal.TaskSegment;
44  import org.apache.maven.plugin.descriptor.MojoDescriptor;
45  import org.apache.maven.project.MavenProject;
46  import org.slf4j.Logger;
47  import org.slf4j.LoggerFactory;
48  
49  import static java.util.Objects.requireNonNull;
50  
51  
52  
53  
54  @Named("concurrent")
55  @Singleton
56  public class ConcurrentLifecycleStarter implements LifecycleStarter {
57      private final Logger logger = LoggerFactory.getLogger(getClass());
58  
59      private final ExecutionEventCatapult eventCatapult;
60      private final DefaultLifecycles defaultLifeCycles;
61      private final BuildPlanExecutor executor;
62      private final LifecyclePluginResolver lifecyclePluginResolver;
63      private final MojoDescriptorCreator mojoDescriptorCreator;
64  
65      @Inject
66      public ConcurrentLifecycleStarter(
67              ExecutionEventCatapult eventCatapult,
68              DefaultLifecycles defaultLifeCycles,
69              BuildPlanExecutor executor,
70              LifecyclePluginResolver lifecyclePluginResolver,
71              MojoDescriptorCreator mojoDescriptorCreator) {
72          this.eventCatapult = eventCatapult;
73          this.defaultLifeCycles = defaultLifeCycles;
74          this.executor = executor;
75          this.lifecyclePluginResolver = lifecyclePluginResolver;
76          this.mojoDescriptorCreator = mojoDescriptorCreator;
77      }
78  
79      public void execute(MavenSession session) {
80          eventCatapult.fire(ExecutionEvent.Type.SessionStarted, session, null);
81  
82          try {
83              if (requiresProject(session) && projectIsNotPresent(session)) {
84                  throw new MissingProjectException("The goal you specified requires a project to execute"
85                          + " but there is no POM in this directory (" + session.getTopDirectory() + ")."
86                          + " Please verify you invoked Maven from the correct directory.");
87              }
88  
89              List<TaskSegment> taskSegments = calculateTaskSegments(session);
90              if (taskSegments.isEmpty()) {
91                  throw new NoGoalSpecifiedException("No goals have been specified for this build."
92                          + " You must specify a valid lifecycle phase or a goal in the format <plugin-prefix>:<goal> or"
93                          + " <plugin-group-id>:<plugin-artifact-id>[:<plugin-version>]:<goal>."
94                          + " Available lifecycle phases are: " + defaultLifeCycles.getLifecyclePhaseList() + ".");
95              }
96  
97              int degreeOfConcurrency = session.getRequest().getDegreeOfConcurrency();
98              if (degreeOfConcurrency > 1) {
99                  logger.info("");
100                 logger.info(String.format(
101                         "Using the %s implementation with a thread count of %d",
102                         executor.getClass().getSimpleName(), degreeOfConcurrency));
103             }
104 
105             ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader();
106             ReactorBuildStatus reactorBuildStatus = new ReactorBuildStatus(session.getProjectDependencyGraph());
107             ReactorContext reactorContext =
108                     new ReactorContext(session.getResult(), oldContextClassLoader, reactorBuildStatus);
109             executor.execute(session, reactorContext, taskSegments);
110 
111         } catch (Exception e) {
112             session.getResult().addException(e);
113         } finally {
114             eventCatapult.fire(ExecutionEvent.Type.SessionEnded, session, null);
115         }
116     }
117 
118     public List<TaskSegment> calculateTaskSegments(MavenSession session) throws Exception {
119 
120         MavenProject rootProject = session.getTopLevelProject();
121 
122         List<String> tasks = requireNonNull(session.getGoals()); 
123 
124         if (tasks.isEmpty()
125                 && (rootProject.getDefaultGoal() != null
126                         && !rootProject.getDefaultGoal().isEmpty())) {
127             tasks = Stream.of(rootProject.getDefaultGoal().split("\\s+"))
128                     .filter(g -> !g.isEmpty())
129                     .collect(Collectors.toList());
130         }
131 
132         return calculateTaskSegments(session, tasks);
133     }
134 
135     public List<TaskSegment> calculateTaskSegments(MavenSession session, List<String> tasks) throws Exception {
136         List<TaskSegment> taskSegments = new ArrayList<>(tasks.size());
137 
138         TaskSegment currentSegment = null;
139 
140         for (String task : tasks) {
141             if (isGoalSpecification(task)) {
142                 
143 
144                 lifecyclePluginResolver.resolveMissingPluginVersions(session.getTopLevelProject(), session);
145 
146                 MojoDescriptor mojoDescriptor =
147                         mojoDescriptorCreator.getMojoDescriptor(task, session, session.getTopLevelProject());
148 
149                 boolean aggregating = mojoDescriptor.isAggregator() || !mojoDescriptor.isProjectRequired();
150 
151                 if (currentSegment == null || currentSegment.isAggregating() != aggregating) {
152                     currentSegment = new TaskSegment(aggregating);
153                     taskSegments.add(currentSegment);
154                 }
155 
156                 currentSegment.getTasks().add(new GoalTask(task));
157             } else {
158                 
159 
160                 if (currentSegment == null || currentSegment.isAggregating()) {
161                     currentSegment = new TaskSegment(false);
162                     taskSegments.add(currentSegment);
163                 }
164 
165                 currentSegment.getTasks().add(new LifecycleTask(task));
166             }
167         }
168 
169         return taskSegments;
170     }
171 
172     private boolean projectIsNotPresent(MavenSession session) {
173         return !session.getRequest().isProjectPresent();
174     }
175 
176     private boolean requiresProject(MavenSession session) {
177         List<String> goals = session.getGoals();
178         if (goals != null) {
179             for (String goal : goals) {
180                 if (!isGoalSpecification(goal)) {
181                     return true;
182                 }
183             }
184         }
185         return false;
186     }
187 
188     private boolean isGoalSpecification(String task) {
189         return task.indexOf(':') >= 0;
190     }
191 }