Skip to content

Commit 67198a2

Browse files
JENKINS-56448 Introduce a report of the time spent in each phase of the Maven build (compile, test...)
1 parent 638895d commit 67198a2

File tree

20 files changed

+2385
-522
lines changed

20 files changed

+2385
-522
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package org.jenkinsci.plugins.pipeline.maven.model;
2+
3+
import edu.umd.cs.findbugs.annotations.NonNull;
4+
5+
import java.time.Duration;
6+
import java.time.ZonedDateTime;
7+
import java.time.temporal.ChronoUnit;
8+
import java.time.temporal.TemporalUnit;
9+
import java.util.Arrays;
10+
import java.util.HashSet;
11+
import java.util.Set;
12+
import java.util.SortedSet;
13+
import java.util.TreeSet;
14+
import java.util.concurrent.TimeUnit;
15+
16+
import javax.annotation.Nonnull;
17+
18+
/**
19+
* @author <a href="mailto:[email protected]">Cyrille Le Clerc</a>
20+
*/
21+
public class MavenExecutionAggregatedPhasesDetails implements Comparable<MavenExecutionAggregatedPhasesDetails> {
22+
@NonNull
23+
private Set<String> phases;
24+
@NonNull
25+
private SortedSet<MavenMojoExecutionDetails> mojoExecutionDetails = new TreeSet<>();
26+
27+
public MavenExecutionAggregatedPhasesDetails(@NonNull String... phases) {
28+
this.phases = new HashSet<>(Arrays.asList(phases));
29+
}
30+
31+
/**
32+
* @param mojoExecutionDetails
33+
* @return {@code true} if given {@link MavenMojoExecutionDetails} was added to this aggregate.
34+
*/
35+
public boolean offer(@NonNull MavenMojoExecutionDetails mojoExecutionDetails) {
36+
if (phases.contains(mojoExecutionDetails.getLifecyclePhase())) {
37+
this.mojoExecutionDetails.add(mojoExecutionDetails);
38+
return true;
39+
} else {
40+
return false;
41+
}
42+
}
43+
44+
@Nonnull
45+
public ZonedDateTime getStart() {
46+
return mojoExecutionDetails.first().getStart();
47+
}
48+
49+
@Nonnull
50+
public ZonedDateTime getStop() {
51+
return mojoExecutionDetails.last().getStop();
52+
}
53+
54+
@Nonnull
55+
public String getDuration() {
56+
int durationInSecs = 0;
57+
for (MavenMojoExecutionDetails mojoExecutionDetails:getMojoExecutionDetails()) {
58+
durationInSecs += mojoExecutionDetails.getDurationMillis();
59+
}
60+
return TimeUnit.SECONDS.convert(durationInSecs, TimeUnit.MILLISECONDS) + "s";
61+
}
62+
63+
public Set<String> getPhases() {
64+
return phases;
65+
}
66+
67+
public SortedSet<MavenMojoExecutionDetails> getMojoExecutionDetails() {
68+
return mojoExecutionDetails;
69+
}
70+
71+
@Override
72+
public int compareTo(MavenExecutionAggregatedPhasesDetails other) {
73+
int comparison = this.getStart().compareTo(other.getStart());
74+
75+
if (comparison == 0) {
76+
comparison = this.getStop().compareTo(other.getStop());
77+
}
78+
return comparison;
79+
}
80+
81+
@Override
82+
public String toString() {
83+
return "MavenExecutionAggregatedPhasesDetails{" +
84+
"phases=" + phases +
85+
", mojoExecutionDetails=" + mojoExecutionDetails +
86+
'}';
87+
}
88+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
package org.jenkinsci.plugins.pipeline.maven.model;
2+
3+
import java.io.Serializable;
4+
import java.time.ZonedDateTime;
5+
import java.util.ArrayList;
6+
import java.util.Arrays;
7+
import java.util.List;
8+
import java.util.SortedSet;
9+
import java.util.TreeSet;
10+
import java.util.stream.Collectors;
11+
12+
import javax.annotation.Nonnull;
13+
14+
/**
15+
* @author <a href="mailto:[email protected]">Cyrille Le Clerc</a>
16+
*/
17+
public class MavenExecutionDetails implements Comparable<MavenExecutionDetails>, Serializable {
18+
19+
private static final long serialVersionUID = 1L;
20+
21+
private SortedSet<MavenProjectExecutionDetails> mavenProjectExecutionDetails = new TreeSet<>();
22+
23+
@Nonnull
24+
private final ZonedDateTime start;
25+
@Nonnull
26+
private ZonedDateTime stop;
27+
28+
public MavenExecutionDetails(@Nonnull ZonedDateTime start) {
29+
this.start = start;
30+
this.stop = stop;
31+
}
32+
33+
public SortedSet<MavenProjectExecutionDetails> getMavenProjectExecutionDetails() {
34+
return mavenProjectExecutionDetails;
35+
}
36+
37+
@Nonnull
38+
public ZonedDateTime getStart() {
39+
return start;
40+
}
41+
42+
@Nonnull
43+
public ZonedDateTime getStop() {
44+
return stop;
45+
}
46+
47+
public void setStop(@Nonnull ZonedDateTime stop) {
48+
this.stop = stop;
49+
}
50+
51+
@Override
52+
public int compareTo(MavenExecutionDetails o) {
53+
return this.start.compareTo(o.start);
54+
}
55+
56+
/**
57+
* it's a poor comparison but there is no risk of having 2 builds starting at the same time
58+
*
59+
* @param o
60+
* @return
61+
*/
62+
@Override
63+
public boolean equals(Object o) {
64+
if (this == o) return true;
65+
if (o == null || getClass() != o.getClass()) return false;
66+
67+
MavenExecutionDetails that = (MavenExecutionDetails) o;
68+
69+
return start.equals(that.start);
70+
}
71+
72+
@Override
73+
public int hashCode() {
74+
return start.hashCode();
75+
}
76+
77+
public String getExecutionDurationDetails() {
78+
List<MavenExecutionAggregatedPhasesDetails> aggregates = new ArrayList(Arrays.asList(
79+
new MavenExecutionAggregatedPhasesDetails("validate", "initialize"),
80+
new MavenExecutionAggregatedPhasesDetails("generate-sources", "process-sources", "generate-resources", "process-resources", "compile", "process-classes"),
81+
new MavenExecutionAggregatedPhasesDetails("generate-test-sources", "process-test-sources", "generate-test-resources", "process-test-resources", "test-compile", "process-test-classes", "test"),
82+
new MavenExecutionAggregatedPhasesDetails("prepare-package", "package"),
83+
new MavenExecutionAggregatedPhasesDetails("pre-integration-test", "integration-test", "post-integration-test"),
84+
new MavenExecutionAggregatedPhasesDetails("install"),
85+
new MavenExecutionAggregatedPhasesDetails("deploy"),
86+
new MavenExecutionAggregatedPhasesDetails("pre-clean", "clean", "post-clean"),
87+
new MavenExecutionAggregatedPhasesDetails("pre-site", "site", "post-site", "site-deploy")
88+
89+
));
90+
91+
for (MavenProjectExecutionDetails projectExecutionDetails :
92+
getMavenProjectExecutionDetails()) {
93+
for (MavenMojoExecutionDetails mojoExecutionDetails : projectExecutionDetails.getMojoExecutionDetails()) {
94+
boolean added = false;
95+
for (MavenExecutionAggregatedPhasesDetails aggregate : aggregates) {
96+
if (aggregate.offer(mojoExecutionDetails)) {
97+
added = true;
98+
break;
99+
}
100+
}
101+
if (!added) {
102+
MavenExecutionAggregatedPhasesDetails aggregate = new MavenExecutionAggregatedPhasesDetails(mojoExecutionDetails.getLifecyclePhase());
103+
aggregates.add(aggregate);
104+
boolean offer = aggregate.offer(mojoExecutionDetails);
105+
if (offer == false) {
106+
throw new IllegalStateException("Failure to add " + mojoExecutionDetails + " to " + aggregate);
107+
}
108+
}
109+
}
110+
}
111+
StringBuilder sb = new StringBuilder();
112+
113+
for (MavenExecutionAggregatedPhasesDetails aggregate : aggregates) {
114+
if (aggregate.getMojoExecutionDetails().isEmpty()) {
115+
116+
} else {
117+
sb.append(" * " + aggregate.getPhases().stream().collect(Collectors.joining(", ")) + " --- " + aggregate.getDuration() + "\n");
118+
for (MavenMojoExecutionDetails mojoExecutionDetails : aggregate.getMojoExecutionDetails().stream().filter(m -> m.getDurationMillis() > 1000).collect(Collectors.toList())) {
119+
sb.append(" * " + mojoExecutionDetails.getPlugin().getArtifactId() + ":" + mojoExecutionDetails.getGoal() + " (" + mojoExecutionDetails.getExecutionId() + ")" + " @ " + mojoExecutionDetails.getProject().getArtifactId() + " --- " + mojoExecutionDetails.getDuration() + "\n");
120+
}
121+
}
122+
}
123+
124+
return sb.toString();
125+
}
126+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package org.jenkinsci.plugins.pipeline.maven.model;
2+
3+
/**
4+
* See {@code org.apache.maven.execution.ExecutionEvent.Type}
5+
*/
6+
public enum MavenExecutionEventType {
7+
ProjectDiscoveryStarted,
8+
SessionStarted,
9+
SessionEnded,
10+
ProjectSkipped,
11+
ProjectStarted,
12+
ProjectSucceeded,
13+
ProjectFailed,
14+
MojoSkipped,
15+
MojoStarted,
16+
MojoSucceeded,
17+
MojoFailed,
18+
ForkStarted,
19+
ForkSucceeded,
20+
ForkFailed,
21+
ForkedProjectStarted,
22+
ForkedProjectSucceeded,
23+
ForkedProjectFailed
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package org.jenkinsci.plugins.pipeline.maven.model;
2+
3+
/**
4+
* See {@code org.apache.maven.execution.BuildSuccess} and {@code org.apache.maven.execution.BuildFailure}
5+
*/
6+
public enum MavenExecutionStatus {Success, Failure}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
package org.jenkinsci.plugins.pipeline.maven.model;
2+
3+
import org.jenkinsci.plugins.pipeline.maven.MavenArtifact;
4+
5+
import java.io.Serializable;
6+
import java.time.Duration;
7+
import java.time.ZonedDateTime;
8+
import java.time.temporal.ChronoUnit;
9+
10+
import javax.annotation.Nonnull;
11+
12+
/**
13+
* @author <a href="mailto:[email protected]">Cyrille Le Clerc</a>
14+
*/
15+
public class MavenMojoExecutionDetails implements Comparable<MavenMojoExecutionDetails>, Serializable {
16+
17+
private static final long serialVersionUID = 1L;
18+
19+
@Nonnull
20+
private final MavenArtifact project;
21+
@Nonnull
22+
private final MavenArtifact plugin;
23+
@Nonnull
24+
private final String executionId;
25+
@Nonnull
26+
private final String goal;
27+
@Nonnull
28+
private final String lifecyclePhase;
29+
@Nonnull
30+
private final ZonedDateTime start;
31+
@Nonnull
32+
private ZonedDateTime stop;
33+
@Nonnull
34+
private MavenExecutionEventType type;
35+
36+
public MavenMojoExecutionDetails(@Nonnull MavenArtifact project, @Nonnull MavenArtifact plugin, @Nonnull String executionId, @Nonnull String lifecyclePhase, @Nonnull String goal, @Nonnull ZonedDateTime start, @Nonnull MavenExecutionEventType type) {
37+
this.project = project;
38+
this.plugin = plugin;
39+
this.executionId = executionId;
40+
this.lifecyclePhase = lifecyclePhase;
41+
this.goal = goal;
42+
this.start = start;
43+
this.stop = start;
44+
this.type = type;
45+
}
46+
47+
/**
48+
* See {@code org.apache.maven.execution.ExecutionEvent.Type#MojoStarted}
49+
*/
50+
@Nonnull
51+
public ZonedDateTime getStart() {
52+
return start;
53+
}
54+
55+
/**
56+
* See {@code org.apache.maven.execution.ExecutionEvent.Type#MojoSucceeded} and {@code org.apache.maven.execution.ExecutionEvent.Type#MojoFailed}
57+
*/
58+
@Nonnull
59+
public ZonedDateTime getStop() {
60+
return stop;
61+
}
62+
63+
public void stop(@Nonnull ZonedDateTime stop, MavenExecutionEventType type) {
64+
this.stop = stop;
65+
this.type = type;
66+
}
67+
68+
@Nonnull
69+
public MavenArtifact getProject() {
70+
return project;
71+
}
72+
73+
@Nonnull
74+
public MavenArtifact getPlugin() {
75+
return plugin;
76+
}
77+
78+
@Nonnull
79+
public String getExecutionId() {
80+
return executionId;
81+
}
82+
83+
@Nonnull
84+
public String getLifecyclePhase() {
85+
return lifecyclePhase;
86+
}
87+
88+
@Nonnull
89+
public String getGoal() {
90+
return goal;
91+
}
92+
93+
@Override
94+
public String toString() {
95+
return "MavenMojoExecutionDetails{" +
96+
"project=" + project.getId() +
97+
", plugin=" + plugin.getId() +
98+
", executionId='" + executionId + '\'' +
99+
", lifecyclePhase='" + lifecyclePhase + '\'' +
100+
", goal='" + goal + '\'' +
101+
", start=" + start +
102+
", stop=" + stop +
103+
", type=" + type +
104+
'}';
105+
}
106+
107+
@Override
108+
public int compareTo(MavenMojoExecutionDetails other) {
109+
int comparison = this.getStart().compareTo(other.getStart());
110+
111+
if (comparison == 0) {
112+
comparison = this.getStop().compareTo(other.getStop());
113+
}
114+
return comparison;
115+
}
116+
117+
@Nonnull
118+
public String getDuration() {
119+
return Duration.between(start, stop).getSeconds() + "s";
120+
}
121+
122+
@Nonnull
123+
public long getDurationMillis() {
124+
return start.until(stop, ChronoUnit.MILLIS);
125+
}
126+
127+
@Override
128+
public boolean equals(Object o) {
129+
if (this == o) return true;
130+
if (o == null || getClass() != o.getClass()) return false;
131+
132+
MavenMojoExecutionDetails that = (MavenMojoExecutionDetails) o;
133+
134+
if (!project.equals(that.project)) return false;
135+
if (!plugin.equals(that.plugin)) return false;
136+
if (!executionId.equals(that.executionId)) return false;
137+
return goal.equals(that.goal);
138+
}
139+
140+
@Override
141+
public int hashCode() {
142+
int result = project.hashCode();
143+
result = 31 * result + plugin.hashCode();
144+
result = 31 * result + executionId.hashCode();
145+
result = 31 * result + goal.hashCode();
146+
return result;
147+
}
148+
}

0 commit comments

Comments
 (0)