
fi.evolver.basics.spring.job.JobStatusService Maven / Gradle / Ivy
package fi.evolver.basics.spring.job;
import java.time.Instant;
import java.time.LocalDateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import fi.evolver.basics.spring.job.entity.JobStatus;
import fi.evolver.basics.spring.job.entity.TaskStatus.TaskState;
@Service
public class JobStatusService {
private static final Logger LOG = LoggerFactory.getLogger(JobStatusService.class);
private static final int TOTAL_WEIGHT = 10;
private final JobStatusRepository jobStatusRepository;
@Autowired
public JobStatusService(JobStatusRepository jobStatusRepository) {
this.jobStatusRepository = jobStatusRepository;
}
public Job start(Class> clazz) {
return start(clazz.getSimpleName());
}
public Job start(String jobName) {
return new Job(this, jobName);
}
public void recordRun(Job job) {
synchronized (job.getName()) {
try {
LocalDateTime now = LocalDateTime.now();
JobStatus status = getStatus(job.getName());
ResultState state = job.getResultState();
long durationMs = job.getDurationMs();
if (state.getState().isSuccess()) {
status.setLastSuccessStart(now);
status.setLastSuccessDurationMs(durationMs);
if (status.getSuccessDurationHt() == 0.0)
status.setSuccessDurationHt(durationMs);
else
status.setSuccessDurationHt(calculateWeightedAverage(status.getSuccessDurationHt(), durationMs));
}
else {
status.setLastFailureStart(now);
status.setLastFailureDurationMs(durationMs);
if (status.getFailureDurationHt() == 0.0)
status.setFailureDurationHt(durationMs);
else
status.setFailureDurationHt(calculateWeightedAverage(status.getFailureDurationHt(), durationMs));
}
status.setSuccessRate(calculateWeightedAverage(status.getSuccessRate(), state.getState().isSuccess() ? 1.0 : 0.0));
if (state.getState() == TaskState.DONE)
status.setLastActualActivity(now);
if (state.getState().isSuccess())
status.setSuccessMessage(state.getMessage());
else
status.setErrorMessage(state.getMessage());
jobStatusRepository.save(status);
}
catch (RuntimeException e) {
LOG.error("JOB STATUS update FAILED", e);
}
}
}
private static double calculateWeightedAverage(double oldValue, double newValue) {
return (oldValue * (TOTAL_WEIGHT - 1) + newValue) / TOTAL_WEIGHT;
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void updateStatus(JobStatus jobStatus) {
jobStatusRepository.save(jobStatus);
}
private JobStatus getStatus(String jobName) {
JobStatus status = jobStatusRepository.findByName(jobName);
if (status == null) {
status = new JobStatus(jobName);
jobStatusRepository.save(status);
}
return status;
}
public static class Job implements AutoCloseable {
private static final ResultState DEFAULT_STATE = new ResultState(TaskState.FAILED, "Result state not set");
private final JobStatusService jobStatusService;
private final String name;
private final Instant startTime;
private ResultState resultState = DEFAULT_STATE;
public Job(JobStatusService jobStatusUpdater, String jobName) {
this.jobStatusService = jobStatusUpdater;
this.name = jobName.intern();
this.startTime = Instant.now();
}
public String getName() {
return name;
}
public int getDurationMs() {
return (int)(Instant.now().toEpochMilli() - startTime.toEpochMilli());
}
public void setResultState(ResultState resultState) {
if (resultState != null && resultState.getState().isFinished())
this.resultState = resultState;
}
public ResultState getResultState() {
return resultState;
}
@Override
public void close() {
jobStatusService.recordRun(this);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy