Skip to content

Commit a2f97e5

Browse files
committed
[MNG-8052] Concurrently lifecycle executor
1 parent d0c9a6b commit a2f97e5

File tree

17 files changed

+2198
-47
lines changed

17 files changed

+2198
-47
lines changed

maven-core/pom.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,14 @@ under the License.
9191
<groupId>org.apache.maven</groupId>
9292
<artifactId>maven-api-impl</artifactId>
9393
</dependency>
94+
<dependency>
95+
<groupId>org.apache.maven</groupId>
96+
<artifactId>maven-jline</artifactId>
97+
</dependency>
98+
<dependency>
99+
<groupId>org.apache.maven</groupId>
100+
<artifactId>maven-slf4j-provider</artifactId>
101+
</dependency>
94102
<dependency>
95103
<groupId>org.apache.maven.resolver</groupId>
96104
<artifactId>maven-resolver-api</artifactId>

maven-core/src/main/java/org/apache/maven/execution/BuildFailure.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,19 @@ public class BuildFailure extends BuildSummary {
3939
* @param cause The cause of the build failure, may be {@code null}.
4040
*/
4141
public BuildFailure(MavenProject project, long time, Throwable cause) {
42-
super(project, time);
42+
this(project, time, time, cause);
43+
}
44+
45+
/**
46+
* Creates a new build summary for the specified project.
47+
*
48+
* @param project The project being summarized, must not be {@code null}.
49+
* @param execTime The exec time of the project in milliseconds.
50+
* @param wallTime The wall time of the project in milliseconds.
51+
* @param cause The cause of the build failure, may be {@code null}.
52+
*/
53+
public BuildFailure(MavenProject project, long execTime, long wallTime, Throwable cause) {
54+
super(project, execTime, wallTime);
4355
this.cause = cause;
4456
}
4557

maven-core/src/main/java/org/apache/maven/execution/BuildSuccess.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,17 @@ public class BuildSuccess extends BuildSummary {
3333
* @param time The build time of the project in milliseconds.
3434
*/
3535
public BuildSuccess(MavenProject project, long time) {
36-
super(project, time);
36+
super(project, time, time);
37+
}
38+
39+
/**
40+
* Creates a new build summary for the specified project.
41+
*
42+
* @param project The project being summarized, must not be {@code null}.
43+
* @param wallTime The wall time of the project in milliseconds.
44+
* @param execTime The exec time of the project in milliseconds.
45+
*/
46+
public BuildSuccess(MavenProject project, long wallTime, long execTime) {
47+
super(project, wallTime, execTime);
3748
}
3849
}

maven-core/src/main/java/org/apache/maven/execution/BuildSummary.java

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,12 @@ public abstract class BuildSummary {
3636
/**
3737
* The build time of the project in milliseconds.
3838
*/
39-
private final long time;
39+
private final long wallTime;
40+
41+
/**
42+
* The total amount of time spent for to run mojos in milliseconds.
43+
*/
44+
private final long execTime;
4045

4146
/**
4247
* Creates a new build summary for the specified project.
@@ -45,9 +50,21 @@ public abstract class BuildSummary {
4550
* @param time The build time of the project in milliseconds.
4651
*/
4752
protected BuildSummary(MavenProject project, long time) {
53+
this(project, time, time);
54+
}
55+
56+
/**
57+
* Creates a new build summary for the specified project.
58+
*
59+
* @param project The project being summarized, must not be {@code null}.
60+
* @param execTime The exec time of the project in milliseconds.
61+
* @param wallTime The wall time of the project in milliseconds.
62+
*/
63+
protected BuildSummary(MavenProject project, long execTime, long wallTime) {
4864
this.project = Objects.requireNonNull(project, "project cannot be null");
4965
// TODO Validate for < 0?
50-
this.time = time;
66+
this.execTime = execTime;
67+
this.wallTime = wallTime;
5168
}
5269

5370
/**
@@ -60,11 +77,20 @@ public MavenProject getProject() {
6077
}
6178

6279
/**
63-
* Gets the build time of the project in milliseconds.
80+
* Gets the wall time of the project in milliseconds.
6481
*
65-
* @return The build time of the project in milliseconds.
82+
* @return The wall time of the project in milliseconds.
6683
*/
6784
public long getTime() {
68-
return time;
85+
return execTime;
86+
}
87+
88+
/**
89+
* Gets the exec time of the project in milliseconds.
90+
*
91+
* @return The exec time of the project in milliseconds.
92+
*/
93+
public long getWallTime() {
94+
return wallTime;
6995
}
7096
}

maven-core/src/main/java/org/apache/maven/lifecycle/internal/CompoundProjectExecutionListener.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@
2424
import org.apache.maven.execution.ProjectExecutionListener;
2525
import org.apache.maven.lifecycle.LifecycleExecutionException;
2626

27-
class CompoundProjectExecutionListener implements ProjectExecutionListener {
27+
public class CompoundProjectExecutionListener implements ProjectExecutionListener {
2828
private final Collection<ProjectExecutionListener> listeners;
2929

30-
CompoundProjectExecutionListener(Collection<ProjectExecutionListener> listeners) {
30+
public CompoundProjectExecutionListener(Collection<ProjectExecutionListener> listeners) {
3131
this.listeners = listeners; // NB this is live injected collection
3232
}
3333

maven-core/src/main/java/org/apache/maven/lifecycle/internal/MojoExecutor.java

Lines changed: 53 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,13 @@ private void execute(MavenSession session, MojoExecution mojoExecution, Dependen
213213
doExecute(session, mojoExecution, dependencyContext);
214214
}
215215

216+
protected static class NoLock implements NoExceptionCloseable {
217+
public NoLock() {}
218+
219+
@Override
220+
public void close() {}
221+
}
222+
216223
/**
217224
* Aggregating mojo executions (possibly) modify all MavenProjects, including those that are currently in use
218225
* by concurrently running mojo executions. To prevent race conditions, an aggregating execution will block
@@ -221,54 +228,45 @@ private void execute(MavenSession session, MojoExecution mojoExecution, Dependen
221228
* TODO: ideally, the builder should take care of the ordering in a smarter way
222229
* TODO: and concurrency issues fixed with MNG-7157
223230
*/
224-
private class ProjectLock implements AutoCloseable {
231+
protected class ProjectLock implements NoExceptionCloseable {
225232
final Lock acquiredAggregatorLock;
226233
final OwnerReentrantLock acquiredProjectLock;
227234

228235
ProjectLock(MavenSession session, MojoDescriptor mojoDescriptor) {
229236
mojos.put(Thread.currentThread(), mojoDescriptor);
230-
if (session.getRequest().getDegreeOfConcurrency() > 1) {
231-
boolean aggregator = mojoDescriptor.isAggregator();
232-
acquiredAggregatorLock = aggregator ? aggregatorLock.writeLock() : aggregatorLock.readLock();
233-
acquiredProjectLock = getProjectLock(session);
234-
if (!acquiredAggregatorLock.tryLock()) {
235-
Thread owner = aggregatorLock.getOwner();
236-
MojoDescriptor ownerMojo = owner != null ? mojos.get(owner) : null;
237-
String str = ownerMojo != null ? " The " + ownerMojo.getId() : "An";
238-
String msg = str + " aggregator mojo is already being executed "
239-
+ "in this parallel build, those kind of mojos require exclusive access to "
240-
+ "reactor to prevent race conditions. This mojo execution will be blocked "
241-
+ "until the aggregator mojo is done.";
242-
warn(msg);
243-
acquiredAggregatorLock.lock();
244-
}
245-
if (!acquiredProjectLock.tryLock()) {
246-
Thread owner = acquiredProjectLock.getOwner();
247-
MojoDescriptor ownerMojo = owner != null ? mojos.get(owner) : null;
248-
String str = ownerMojo != null ? " The " + ownerMojo.getId() : "A";
249-
String msg = str + " mojo is already being executed "
250-
+ "on the project " + session.getCurrentProject().getGroupId()
251-
+ ":" + session.getCurrentProject().getArtifactId() + ". "
252-
+ "This mojo execution will be blocked "
253-
+ "until the mojo is done.";
254-
warn(msg);
255-
acquiredProjectLock.lock();
256-
}
257-
} else {
258-
acquiredAggregatorLock = null;
259-
acquiredProjectLock = null;
237+
boolean aggregator = mojoDescriptor.isAggregator();
238+
acquiredAggregatorLock = aggregator ? aggregatorLock.writeLock() : aggregatorLock.readLock();
239+
acquiredProjectLock = getProjectLock(session);
240+
if (!acquiredAggregatorLock.tryLock()) {
241+
Thread owner = aggregatorLock.getOwner();
242+
MojoDescriptor ownerMojo = owner != null ? mojos.get(owner) : null;
243+
String str = ownerMojo != null ? " The " + ownerMojo.getId() : "An";
244+
String msg = str + " aggregator mojo is already being executed "
245+
+ "in this parallel build, those kind of mojos require exclusive access to "
246+
+ "reactor to prevent race conditions. This mojo execution will be blocked "
247+
+ "until the aggregator mojo is done.";
248+
warn(msg);
249+
acquiredAggregatorLock.lock();
250+
}
251+
if (!acquiredProjectLock.tryLock()) {
252+
Thread owner = acquiredProjectLock.getOwner();
253+
MojoDescriptor ownerMojo = owner != null ? mojos.get(owner) : null;
254+
String str = ownerMojo != null ? " The " + ownerMojo.getId() : "A";
255+
String msg = str + " mojo is already being executed "
256+
+ "on the project " + session.getCurrentProject().getGroupId()
257+
+ ":" + session.getCurrentProject().getArtifactId() + ". "
258+
+ "This mojo execution will be blocked "
259+
+ "until the mojo is done.";
260+
warn(msg);
261+
acquiredProjectLock.lock();
260262
}
261263
}
262264

263265
@Override
264266
public void close() {
265267
// release the lock in the reverse order of the acquisition
266-
if (acquiredProjectLock != null) {
267-
acquiredProjectLock.unlock();
268-
}
269-
if (acquiredAggregatorLock != null) {
270-
acquiredAggregatorLock.unlock();
271-
}
268+
acquiredProjectLock.unlock();
269+
acquiredAggregatorLock.unlock();
272270
mojos.remove(Thread.currentThread());
273271
}
274272

@@ -307,7 +305,7 @@ private void doExecute(MavenSession session, MojoExecution mojoExecution, Depend
307305

308306
ensureDependenciesAreResolved(mojoDescriptor, session, dependencyContext);
309307

310-
try (ProjectLock lock = new ProjectLock(session, mojoDescriptor)) {
308+
try (NoExceptionCloseable lock = getProjectLock(session, mojoDescriptor)) {
311309
doExecute2(session, mojoExecution);
312310
} finally {
313311
for (MavenProject forkedProject : forkedProjects) {
@@ -316,6 +314,23 @@ private void doExecute(MavenSession session, MojoExecution mojoExecution, Depend
316314
}
317315
}
318316

317+
protected interface NoExceptionCloseable extends AutoCloseable {
318+
@Override
319+
void close();
320+
}
321+
322+
protected NoExceptionCloseable getProjectLock(MavenSession session, MojoDescriptor mojoDescriptor) {
323+
if (useProjectLock(session)) {
324+
return new ProjectLock(session, mojoDescriptor);
325+
} else {
326+
return new NoLock();
327+
}
328+
}
329+
330+
protected boolean useProjectLock(MavenSession session) {
331+
return session.getRequest().getDegreeOfConcurrency() > 1;
332+
}
333+
319334
private void doExecute2(MavenSession session, MojoExecution mojoExecution) throws LifecycleExecutionException {
320335
eventCatapult.fire(ExecutionEvent.Type.MojoStarted, session, mojoExecution);
321336
try {

0 commit comments

Comments
 (0)