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