Splits BuildReportHandler from ExecutionResultHandler

This commit is contained in:
Spencer Gibb
2020-01-22 14:59:45 -05:00
parent 99dd179e13
commit 93598690ab
4 changed files with 205 additions and 171 deletions

View File

@@ -55,17 +55,17 @@ class BatchConfiguration {
}
@Bean
@ConditionalOnMissingBean
BuildReportHandler springBuildReportHandler(JobExplorer jobExplorer,
ConfigurableApplicationContext context) {
return new SpringBatchExecutionResultHandler(jobExplorer, context);
@ConditionalOnMissingBean(BuildReportHandler.class)
SpringBatchBuildReportHandler springBatchBuildReportHandler(JobExplorer jobExplorer) {
return new SpringBatchBuildReportHandler(jobExplorer);
}
@Bean
@ConditionalOnMissingBean
ExecutionResultHandler springBatchExecutionResultHandler(JobExplorer jobExplorer,
@ConditionalOnMissingBean(ExecutionResultHandler.class)
SpringBatchExecutionResultHandler springBatchExecutionResultHandler(
BuildReportHandler buildReportHandler,
ConfigurableApplicationContext context) {
return new SpringBatchExecutionResultHandler(jobExplorer, context);
return new SpringBatchExecutionResultHandler(buildReportHandler, context);
}
@Bean

View File

@@ -0,0 +1,187 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package releaser.internal.spring;
import java.text.SimpleDateFormat;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import com.jakewharton.fliptables.FlipTableConverters;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import releaser.internal.tasks.TrainPostReleaseReleaserTask;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.explore.JobExplorer;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.core.NestedExceptionUtils;
import org.springframework.util.StringUtils;
class SpringBatchBuildReportHandler implements BuildReportHandler {
private static final Logger log = LoggerFactory
.getLogger(SpringBatchExecutionResultHandler.class);
private final JobExplorer jobExplorer;
SpringBatchBuildReportHandler(JobExplorer jobExplorer) {
this.jobExplorer = jobExplorer;
}
@Override
public void reportBuildSummary() {
List<String> jobNames = this.jobExplorer.getJobNames();
List<JobExecution> sortedJobExecutions = jobNames.stream()
.flatMap(name -> this.jobExplorer.findJobInstancesByJobName(name, 0, 100)
.stream())
.flatMap(instance -> this.jobExplorer.getJobExecutions(instance).stream())
.filter(j -> !j.isRunning())
.sorted(Comparator.comparing(JobExecution::getCreateTime))
.collect(Collectors.toList());
List<StepExecution> stepContexts = sortedJobExecutions.stream()
.flatMap(j -> j.getStepExecutions().stream())
.collect(Collectors.toCollection(LinkedList::new));
printTable(buildTable(stepContexts));
}
private List<Table> buildTable(List<StepExecution> stepContexts) {
return stepContexts.stream().map(step -> {
String date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS")
.format(step.getStartTime());
long millis = ChronoUnit.MILLIS.between(step.getStartTime().toInstant(),
step.getEndTime().toInstant());
ExecutionContext context = step.getExecutionContext();
ExecutionResultReport entity = (ExecutionResultReport) context.get("entity");
if (entity == null) {
return null;
}
String projectName = TrainPostReleaseReleaserTask.class
.isAssignableFrom(entity.getReleaserTaskType()) ? "postRelease"
: entity.getProjectName();
return new Table(date, time(millis), projectName, entity.getShortName(),
entity.getDescription(), entity.getState(), entity.getExceptions());
}).filter(Objects::nonNull).collect(Collectors.toCollection(LinkedList::new));
}
private String time(long millis) {
long minutes = (millis / 1000) / 60;
long seconds = (millis / 1000) % 60;
if (minutes == 0 && seconds == 0) {
return millis + " ms";
}
else if (minutes == 0) {
return seconds + " s";
}
return minutes + " min " + seconds + " s";
}
private void printTable(List<Table> table) {
String string = "\n\n***** BUILD REPORT *****\n\n"
+ FlipTableConverters.fromIterable(table, Table.class)
+ "\n\n***** BUILD REPORT *****\n\n";
List<Table> brokenTasks = table.stream()
.filter(table1 -> StringUtils.hasText(table1.thrownException))
.collect(Collectors.toList());
if (!brokenTasks.isEmpty()) {
String brokenBuilds = "\n\n[BUILD UNSTABLE] The following release tasks are failing!\n\n"
+ brokenTasks.stream().map(table1 -> String.format(
"***** Project / Task : <%s/%s> ***** \nTask Description <%s>\nException Stacktrace \n\n%s",
table1.projectName, table1.taskCaption,
table1.taskDescription,
table1.exceptions + "\n" + table1.exceptions.stream()
.map(Throwable::getStackTrace).flatMap(Arrays::stream)
.map(StackTraceElement::toString)
.collect(Collectors.joining("\n"))))
.collect(Collectors.joining("\n\n"));
log.warn(string + brokenBuilds);
}
else {
log.info(string);
}
}
class Table {
final String creationTime;
final String executionTime;
final String projectName;
final String taskCaption;
final String taskDescription;
final String taskState;
final String thrownException;
List<Throwable> exceptions;
Table(String creationTime, String executionTime, String projectName,
String taskCaption, String taskDescription, String taskState,
List<Throwable> exceptions) {
this.creationTime = creationTime;
this.executionTime = executionTime;
this.projectName = projectName;
this.taskCaption = taskCaption;
this.taskDescription = taskDescription;
this.taskState = taskState;
this.thrownException = exceptions == null ? "" : exceptions.stream()
// TODO: Last but most specific
.map(t -> NestedExceptionUtils.getMostSpecificCause(t).toString())
.collect(Collectors.joining("\n"));
this.exceptions = exceptions;
}
public String getExecutionTime() {
return this.executionTime;
}
public String getCreationTime() {
return this.creationTime;
}
public String getProjectName() {
return this.projectName;
}
public String getTaskCaption() {
return this.taskCaption;
}
public String getTaskDescription() {
return this.taskDescription;
}
public String getTaskState() {
return this.taskState;
}
public String getThrownException() {
return this.thrownException;
}
}
}

View File

@@ -19,49 +19,32 @@ package releaser.internal.spring;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.text.SimpleDateFormat;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import com.jakewharton.fliptables.FlipTableConverters;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import releaser.internal.tasks.TrainPostReleaseReleaserTask;
import releaser.internal.tech.ExecutionResult;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.explore.JobExplorer;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.NestedExceptionUtils;
import org.springframework.util.StringUtils;
class SpringBatchExecutionResultHandler
implements ExecutionResultHandler, BuildReportHandler {
class SpringBatchExecutionResultHandler implements ExecutionResultHandler {
private static final Logger log = LoggerFactory
.getLogger(SpringBatchExecutionResultHandler.class);
private final JobExplorer jobExplorer;
private final ConfigurableApplicationContext context;
SpringBatchExecutionResultHandler(JobExplorer jobExplorer,
private final BuildReportHandler buildReportHandler;
SpringBatchExecutionResultHandler(BuildReportHandler buildReportHandler,
ConfigurableApplicationContext context) {
this.jobExplorer = jobExplorer;
this.buildReportHandler = buildReportHandler;
this.context = context;
}
@Override
public void accept(ExecutionResult executionResult) {
reportBuildSummary();
this.buildReportHandler.reportBuildSummary();
if (executionResult.isFailure()) {
log.error("At least one failure occurred while running the release process",
executionResult.foundExceptions());
@@ -87,78 +70,6 @@ class SpringBatchExecutionResultHandler
System.exit(SpringApplication.exit(this.context, () -> 1));
}
@Override
public void reportBuildSummary() {
List<String> jobNames = this.jobExplorer.getJobNames();
List<JobExecution> sortedJobExecutions = jobNames.stream()
.flatMap(name -> this.jobExplorer.findJobInstancesByJobName(name, 0, 100)
.stream())
.flatMap(instance -> this.jobExplorer.getJobExecutions(instance).stream())
.filter(j -> !j.isRunning())
.sorted(Comparator.comparing(JobExecution::getCreateTime))
.collect(Collectors.toList());
List<StepExecution> stepContexts = sortedJobExecutions.stream()
.flatMap(j -> j.getStepExecutions().stream())
.collect(Collectors.toCollection(LinkedList::new));
printTable(buildTable(stepContexts));
}
private List<Table> buildTable(List<StepExecution> stepContexts) {
return stepContexts.stream().map(step -> {
String date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS")
.format(step.getStartTime());
long millis = ChronoUnit.MILLIS.between(step.getStartTime().toInstant(),
step.getEndTime().toInstant());
ExecutionContext context = step.getExecutionContext();
ExecutionResultReport entity = (ExecutionResultReport) context.get("entity");
if (entity == null) {
return null;
}
String projectName = TrainPostReleaseReleaserTask.class
.isAssignableFrom(entity.getReleaserTaskType()) ? "postRelease"
: entity.getProjectName();
return new Table(date, time(millis), projectName, entity.getShortName(),
entity.getDescription(), entity.getState(), entity.getExceptions());
}).filter(Objects::nonNull).collect(Collectors.toCollection(LinkedList::new));
}
private String time(long millis) {
long minutes = (millis / 1000) / 60;
long seconds = (millis / 1000) % 60;
if (minutes == 0 && seconds == 0) {
return millis + " ms";
}
else if (minutes == 0) {
return seconds + " s";
}
return minutes + " min " + seconds + " s";
}
private void printTable(List<Table> table) {
String string = "\n\n***** BUILD REPORT *****\n\n"
+ FlipTableConverters.fromIterable(table, Table.class)
+ "\n\n***** BUILD REPORT *****\n\n";
List<Table> brokenTasks = table.stream()
.filter(table1 -> StringUtils.hasText(table1.thrownException))
.collect(Collectors.toList());
if (!brokenTasks.isEmpty()) {
String brokenBuilds = "\n\n[BUILD UNSTABLE] The following release tasks are failing!\n\n"
+ brokenTasks.stream().map(table1 -> String.format(
"***** Project / Task : <%s/%s> ***** \nTask Description <%s>\nException Stacktrace \n\n%s",
table1.projectName, table1.taskCaption,
table1.taskDescription,
table1.exceptions + "\n" + table1.exceptions.stream()
.map(Throwable::getStackTrace).flatMap(Arrays::stream)
.map(StackTraceElement::toString)
.collect(Collectors.joining("\n"))))
.collect(Collectors.joining("\n\n"));
log.warn(string + brokenBuilds);
}
else {
log.info(string);
}
}
// File creation required by Jenkins
private void handleUnstableException() {
File buildStatus = new File("build_status");
@@ -226,67 +137,3 @@ class SpringBatchExecutionResultHandler
}
}
class Table {
final String creationTime;
final String executionTime;
final String projectName;
final String taskCaption;
final String taskDescription;
final String taskState;
final String thrownException;
List<Throwable> exceptions;
Table(String creationTime, String executionTime, String projectName,
String taskCaption, String taskDescription, String taskState,
List<Throwable> exceptions) {
this.creationTime = creationTime;
this.executionTime = executionTime;
this.projectName = projectName;
this.taskCaption = taskCaption;
this.taskDescription = taskDescription;
this.taskState = taskState;
this.thrownException = exceptions == null ? "" : exceptions.stream()
// TODO: Last but most specific
.map(t -> NestedExceptionUtils.getMostSpecificCause(t).toString())
.collect(Collectors.joining("\n"));
this.exceptions = exceptions;
}
public String getExecutionTime() {
return this.executionTime;
}
public String getCreationTime() {
return this.creationTime;
}
public String getProjectName() {
return this.projectName;
}
public String getTaskCaption() {
return this.taskCaption;
}
public String getTaskDescription() {
return this.taskDescription;
}
public String getTaskState() {
return this.taskState;
}
public String getThrownException() {
return this.thrownException;
}
}

View File

@@ -57,7 +57,6 @@ import releaser.internal.template.TemplateGenerator;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.explore.JobExplorer;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.builder.SpringApplicationBuilder;
@@ -352,9 +351,9 @@ public abstract class AbstractSpringAcceptanceTests {
public boolean exitedWithException;
public TestExecutionResultHandler(JobExplorer jobExplorer,
public TestExecutionResultHandler(BuildReportHandler buildReportHandler,
ConfigurableApplicationContext context) {
super(jobExplorer, context);
super(buildReportHandler, context);
}
@Override
@@ -401,9 +400,10 @@ public abstract class AbstractSpringAcceptanceTests {
}
@Bean
TestExecutionResultHandler testExecutionResultHandler(JobExplorer explorer,
TestExecutionResultHandler testExecutionResultHandler(
BuildReportHandler buildReportHandler,
ConfigurableApplicationContext context) {
return new TestExecutionResultHandler(explorer, context);
return new TestExecutionResultHandler(buildReportHandler, context);
}
}