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 @Override
82 public void execute(MavenSession session) {
83 eventCatapult.fire(ExecutionEvent.Type.SessionStarted, session, null);
84
85 try {
86 if (requiresProject(session) && projectIsNotPresent(session)) {
87 throw new MissingProjectException("The goal you specified requires a project to execute"
88 + " but there is no POM in this directory (" + session.getTopDirectory() + ")."
89 + " Please verify you invoked Maven from the correct directory.");
90 }
91
92 List<TaskSegment> taskSegments = calculateTaskSegments(session);
93 if (taskSegments.isEmpty()) {
94 throw new NoGoalSpecifiedException("No goals have been specified for this build."
95 + " You must specify a valid lifecycle phase or a goal in the format <plugin-prefix>:<goal> or"
96 + " <plugin-group-id>:<plugin-artifact-id>[:<plugin-version>]:<goal>."
97 + " Available lifecycle phases are: " + defaultLifeCycles.getLifecyclePhaseList() + ".");
98 }
99
100 int degreeOfConcurrency = session.getRequest().getDegreeOfConcurrency();
101 if (degreeOfConcurrency > 1) {
102 logger.info("");
103 logger.info(String.format(
104 "Using the %s implementation with a thread count of %d",
105 executor.getClass().getSimpleName(), degreeOfConcurrency));
106 }
107
108 ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader();
109 ReactorBuildStatus reactorBuildStatus = new ReactorBuildStatus(session.getProjectDependencyGraph());
110 ReactorContext reactorContext =
111 new ReactorContext(session.getResult(), oldContextClassLoader, reactorBuildStatus);
112 executor.execute(session, reactorContext, taskSegments);
113
114 } catch (Exception e) {
115 session.getResult().addException(e);
116 } finally {
117 eventCatapult.fire(ExecutionEvent.Type.SessionEnded, session, null);
118 }
119 }
120
121 public List<TaskSegment> calculateTaskSegments(MavenSession session) throws Exception {
122
123 MavenProject rootProject = session.getTopLevelProject();
124
125 List<String> tasks = requireNonNull(session.getGoals());
126
127 if (tasks.isEmpty()
128 && (rootProject.getDefaultGoal() != null
129 && !rootProject.getDefaultGoal().isEmpty())) {
130 tasks = Stream.of(rootProject.getDefaultGoal().split("\\s+"))
131 .filter(g -> !g.isEmpty())
132 .collect(Collectors.toList());
133 }
134
135 return calculateTaskSegments(session, tasks);
136 }
137
138 public List<TaskSegment> calculateTaskSegments(MavenSession session, List<String> tasks) throws Exception {
139 List<TaskSegment> taskSegments = new ArrayList<>(tasks.size());
140
141 TaskSegment currentSegment = null;
142
143 for (String task : tasks) {
144 if (isBeforeOrAfterPhase(task)) {
145 String prevTask = task;
146 task = PhaseId.of(task).phase();
147 logger.warn("Illegal call to phase '{}'. The main phase '{}' will be used instead.", prevTask, task);
148 }
149 if (isGoalSpecification(task)) {
150
151
152 lifecyclePluginResolver.resolveMissingPluginVersions(session.getTopLevelProject(), session);
153
154 MojoDescriptor mojoDescriptor =
155 mojoDescriptorCreator.getMojoDescriptor(task, session, session.getTopLevelProject());
156
157 boolean aggregating = mojoDescriptor.isAggregator() || !mojoDescriptor.isProjectRequired();
158
159 if (currentSegment == null || currentSegment.isAggregating() != aggregating) {
160 currentSegment = new TaskSegment(aggregating);
161 taskSegments.add(currentSegment);
162 }
163
164 currentSegment.getTasks().add(new GoalTask(task));
165 } else {
166
167
168 if (currentSegment == null || currentSegment.isAggregating()) {
169 currentSegment = new TaskSegment(false);
170 taskSegments.add(currentSegment);
171 }
172
173 currentSegment.getTasks().add(new LifecycleTask(task));
174 }
175 }
176
177 return taskSegments;
178 }
179
180 private boolean projectIsNotPresent(MavenSession session) {
181 return !session.getRequest().isProjectPresent();
182 }
183
184 private boolean requiresProject(MavenSession session) {
185 List<String> goals = session.getGoals();
186 if (goals != null) {
187 for (String goal : goals) {
188 if (!isGoalSpecification(goal)) {
189 return true;
190 }
191 }
192 }
193 return false;
194 }
195
196 private boolean isBeforeOrAfterPhase(String task) {
197 return task.startsWith(Lifecycle.BEFORE) || task.startsWith(Lifecycle.AFTER);
198 }
199
200 private boolean isGoalSpecification(String task) {
201 return task.indexOf(':') >= 0;
202 }
203 }