Skip to content

Commit

Permalink
JENKINS-56448 Introduce a report of the time spent in each phase of t…
Browse files Browse the repository at this point in the history
…he Maven build (compile, test...)
  • Loading branch information
cyrille-leclerc committed Jun 27, 2019
1 parent 638895d commit 67198a2
Show file tree
Hide file tree
Showing 20 changed files with 2,385 additions and 522 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package org.jenkinsci.plugins.pipeline.maven.model;

import edu.umd.cs.findbugs.annotations.NonNull;

import java.time.Duration;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalUnit;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.TimeUnit;

import javax.annotation.Nonnull;

/**
* @author <a href="mailto:[email protected]">Cyrille Le Clerc</a>
*/
public class MavenExecutionAggregatedPhasesDetails implements Comparable<MavenExecutionAggregatedPhasesDetails> {
@NonNull
private Set<String> phases;
@NonNull
private SortedSet<MavenMojoExecutionDetails> mojoExecutionDetails = new TreeSet<>();

public MavenExecutionAggregatedPhasesDetails(@NonNull String... phases) {
this.phases = new HashSet<>(Arrays.asList(phases));
}

/**
* @param mojoExecutionDetails
* @return {@code true} if given {@link MavenMojoExecutionDetails} was added to this aggregate.
*/
public boolean offer(@NonNull MavenMojoExecutionDetails mojoExecutionDetails) {
if (phases.contains(mojoExecutionDetails.getLifecyclePhase())) {
this.mojoExecutionDetails.add(mojoExecutionDetails);
return true;
} else {
return false;
}
}

@Nonnull
public ZonedDateTime getStart() {
return mojoExecutionDetails.first().getStart();
}

@Nonnull
public ZonedDateTime getStop() {
return mojoExecutionDetails.last().getStop();
}

@Nonnull
public String getDuration() {
int durationInSecs = 0;
for (MavenMojoExecutionDetails mojoExecutionDetails:getMojoExecutionDetails()) {
durationInSecs += mojoExecutionDetails.getDurationMillis();
}
return TimeUnit.SECONDS.convert(durationInSecs, TimeUnit.MILLISECONDS) + "s";
}

public Set<String> getPhases() {
return phases;
}

public SortedSet<MavenMojoExecutionDetails> getMojoExecutionDetails() {
return mojoExecutionDetails;
}

@Override
public int compareTo(MavenExecutionAggregatedPhasesDetails other) {
int comparison = this.getStart().compareTo(other.getStart());

if (comparison == 0) {
comparison = this.getStop().compareTo(other.getStop());
}
return comparison;
}

@Override
public String toString() {
return "MavenExecutionAggregatedPhasesDetails{" +
"phases=" + phases +
", mojoExecutionDetails=" + mojoExecutionDetails +
'}';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package org.jenkinsci.plugins.pipeline.maven.model;

import java.io.Serializable;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.stream.Collectors;

import javax.annotation.Nonnull;

/**
* @author <a href="mailto:[email protected]">Cyrille Le Clerc</a>
*/
public class MavenExecutionDetails implements Comparable<MavenExecutionDetails>, Serializable {

private static final long serialVersionUID = 1L;

private SortedSet<MavenProjectExecutionDetails> mavenProjectExecutionDetails = new TreeSet<>();

@Nonnull
private final ZonedDateTime start;
@Nonnull
private ZonedDateTime stop;

public MavenExecutionDetails(@Nonnull ZonedDateTime start) {
this.start = start;
this.stop = stop;
}

public SortedSet<MavenProjectExecutionDetails> getMavenProjectExecutionDetails() {
return mavenProjectExecutionDetails;
}

@Nonnull
public ZonedDateTime getStart() {
return start;
}

@Nonnull
public ZonedDateTime getStop() {
return stop;
}

public void setStop(@Nonnull ZonedDateTime stop) {
this.stop = stop;
}

@Override
public int compareTo(MavenExecutionDetails o) {
return this.start.compareTo(o.start);
}

/**
* it's a poor comparison but there is no risk of having 2 builds starting at the same time
*
* @param o
* @return
*/
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;

MavenExecutionDetails that = (MavenExecutionDetails) o;

return start.equals(that.start);
}

@Override
public int hashCode() {
return start.hashCode();
}

public String getExecutionDurationDetails() {
List<MavenExecutionAggregatedPhasesDetails> aggregates = new ArrayList(Arrays.asList(
new MavenExecutionAggregatedPhasesDetails("validate", "initialize"),
new MavenExecutionAggregatedPhasesDetails("generate-sources", "process-sources", "generate-resources", "process-resources", "compile", "process-classes"),
new MavenExecutionAggregatedPhasesDetails("generate-test-sources", "process-test-sources", "generate-test-resources", "process-test-resources", "test-compile", "process-test-classes", "test"),
new MavenExecutionAggregatedPhasesDetails("prepare-package", "package"),
new MavenExecutionAggregatedPhasesDetails("pre-integration-test", "integration-test", "post-integration-test"),
new MavenExecutionAggregatedPhasesDetails("install"),
new MavenExecutionAggregatedPhasesDetails("deploy"),
new MavenExecutionAggregatedPhasesDetails("pre-clean", "clean", "post-clean"),
new MavenExecutionAggregatedPhasesDetails("pre-site", "site", "post-site", "site-deploy")

));

for (MavenProjectExecutionDetails projectExecutionDetails :
getMavenProjectExecutionDetails()) {
for (MavenMojoExecutionDetails mojoExecutionDetails : projectExecutionDetails.getMojoExecutionDetails()) {
boolean added = false;
for (MavenExecutionAggregatedPhasesDetails aggregate : aggregates) {
if (aggregate.offer(mojoExecutionDetails)) {
added = true;
break;
}
}
if (!added) {
MavenExecutionAggregatedPhasesDetails aggregate = new MavenExecutionAggregatedPhasesDetails(mojoExecutionDetails.getLifecyclePhase());
aggregates.add(aggregate);
boolean offer = aggregate.offer(mojoExecutionDetails);
if (offer == false) {
throw new IllegalStateException("Failure to add " + mojoExecutionDetails + " to " + aggregate);
}
}
}
}
StringBuilder sb = new StringBuilder();

for (MavenExecutionAggregatedPhasesDetails aggregate : aggregates) {
if (aggregate.getMojoExecutionDetails().isEmpty()) {

} else {
sb.append(" * " + aggregate.getPhases().stream().collect(Collectors.joining(", ")) + " --- " + aggregate.getDuration() + "\n");
for (MavenMojoExecutionDetails mojoExecutionDetails : aggregate.getMojoExecutionDetails().stream().filter(m -> m.getDurationMillis() > 1000).collect(Collectors.toList())) {
sb.append(" * " + mojoExecutionDetails.getPlugin().getArtifactId() + ":" + mojoExecutionDetails.getGoal() + " (" + mojoExecutionDetails.getExecutionId() + ")" + " @ " + mojoExecutionDetails.getProject().getArtifactId() + " --- " + mojoExecutionDetails.getDuration() + "\n");
}
}
}

return sb.toString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.jenkinsci.plugins.pipeline.maven.model;

/**
* See {@code org.apache.maven.execution.ExecutionEvent.Type}
*/
public enum MavenExecutionEventType {
ProjectDiscoveryStarted,
SessionStarted,
SessionEnded,
ProjectSkipped,
ProjectStarted,
ProjectSucceeded,
ProjectFailed,
MojoSkipped,
MojoStarted,
MojoSucceeded,
MojoFailed,
ForkStarted,
ForkSucceeded,
ForkFailed,
ForkedProjectStarted,
ForkedProjectSucceeded,
ForkedProjectFailed
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.jenkinsci.plugins.pipeline.maven.model;

/**
* See {@code org.apache.maven.execution.BuildSuccess} and {@code org.apache.maven.execution.BuildFailure}
*/
public enum MavenExecutionStatus {Success, Failure}
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package org.jenkinsci.plugins.pipeline.maven.model;

import org.jenkinsci.plugins.pipeline.maven.MavenArtifact;

import java.io.Serializable;
import java.time.Duration;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;

import javax.annotation.Nonnull;

/**
* @author <a href="mailto:[email protected]">Cyrille Le Clerc</a>
*/
public class MavenMojoExecutionDetails implements Comparable<MavenMojoExecutionDetails>, Serializable {

private static final long serialVersionUID = 1L;

@Nonnull
private final MavenArtifact project;
@Nonnull
private final MavenArtifact plugin;
@Nonnull
private final String executionId;
@Nonnull
private final String goal;
@Nonnull
private final String lifecyclePhase;
@Nonnull
private final ZonedDateTime start;
@Nonnull
private ZonedDateTime stop;
@Nonnull
private MavenExecutionEventType type;

public MavenMojoExecutionDetails(@Nonnull MavenArtifact project, @Nonnull MavenArtifact plugin, @Nonnull String executionId, @Nonnull String lifecyclePhase, @Nonnull String goal, @Nonnull ZonedDateTime start, @Nonnull MavenExecutionEventType type) {
this.project = project;
this.plugin = plugin;
this.executionId = executionId;
this.lifecyclePhase = lifecyclePhase;
this.goal = goal;
this.start = start;
this.stop = start;
this.type = type;
}

/**
* See {@code org.apache.maven.execution.ExecutionEvent.Type#MojoStarted}
*/
@Nonnull
public ZonedDateTime getStart() {
return start;
}

/**
* See {@code org.apache.maven.execution.ExecutionEvent.Type#MojoSucceeded} and {@code org.apache.maven.execution.ExecutionEvent.Type#MojoFailed}
*/
@Nonnull
public ZonedDateTime getStop() {
return stop;
}

public void stop(@Nonnull ZonedDateTime stop, MavenExecutionEventType type) {
this.stop = stop;
this.type = type;
}

@Nonnull
public MavenArtifact getProject() {
return project;
}

@Nonnull
public MavenArtifact getPlugin() {
return plugin;
}

@Nonnull
public String getExecutionId() {
return executionId;
}

@Nonnull
public String getLifecyclePhase() {
return lifecyclePhase;
}

@Nonnull
public String getGoal() {
return goal;
}

@Override
public String toString() {
return "MavenMojoExecutionDetails{" +
"project=" + project.getId() +
", plugin=" + plugin.getId() +
", executionId='" + executionId + '\'' +
", lifecyclePhase='" + lifecyclePhase + '\'' +
", goal='" + goal + '\'' +
", start=" + start +
", stop=" + stop +
", type=" + type +
'}';
}

@Override
public int compareTo(MavenMojoExecutionDetails other) {
int comparison = this.getStart().compareTo(other.getStart());

if (comparison == 0) {
comparison = this.getStop().compareTo(other.getStop());
}
return comparison;
}

@Nonnull
public String getDuration() {
return Duration.between(start, stop).getSeconds() + "s";
}

@Nonnull
public long getDurationMillis() {
return start.until(stop, ChronoUnit.MILLIS);
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;

MavenMojoExecutionDetails that = (MavenMojoExecutionDetails) o;

if (!project.equals(that.project)) return false;
if (!plugin.equals(that.plugin)) return false;
if (!executionId.equals(that.executionId)) return false;
return goal.equals(that.goal);
}

@Override
public int hashCode() {
int result = project.hashCode();
result = 31 * result + plugin.hashCode();
result = 31 * result + executionId.hashCode();
result = 31 * result + goal.hashCode();
return result;
}
}
Loading

0 comments on commit 67198a2

Please sign in to comment.