package de.otto.edison.jobs.domain;
import net.jcip.annotations.ThreadSafe;
import java.time.Clock;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import static de.otto.edison.jobs.domain.JobInfo.JobStatus.OK;
import static java.time.OffsetDateTime.now;
import static java.util.Collections.emptyList;
import static java.util.Collections.unmodifiableList;
import static java.util.Optional.empty;
import static java.util.Optional.ofNullable;
/**
* Information about a single job execution.
* <p>
* A JobInfo instance is created for every job execution. It is constantly updated by the background job and
* persisted in the JobRepository.
*/
@ThreadSafe
public class JobInfo {
private final String jobId;
private final String jobType;
private final OffsetDateTime started;
private final List<JobMessage> messages;
private final Optional<OffsetDateTime> stopped;
private final JobStatus status;
private final OffsetDateTime lastUpdated;
private final String hostname;
private final Clock clock;
public Clock getClock() {
return clock;
}
public enum JobStatus {OK, SKIPPED, ERROR, DEAD}
public static JobInfo newJobInfo(final String jobId, final String jobType,
final Clock clock, final String hostname) {
return new JobInfo(jobType, jobId, clock, hostname);
}
public static JobInfo newJobInfo(final String jobId,
final String jobType,
final OffsetDateTime started,
final OffsetDateTime lastUpdated,
final Optional<OffsetDateTime> stopped,
final JobStatus status,
final List<JobMessage> messages,
final Clock clock,
final String hostname) {
return new JobInfo(jobId, jobType, started, lastUpdated, stopped, status, messages, clock, hostname);
}
private JobInfo(final String jobType, final String jobId, final Clock clock, final String hostname) {
this.jobId = jobId;
this.jobType = jobType;
this.started = now(clock);
this.clock = clock;
this.stopped = empty();
this.status = OK;
this.lastUpdated = started;
this.hostname = hostname;
this.messages = emptyList();
}
private JobInfo(final String jobId,
final String jobType,
final OffsetDateTime started,
final OffsetDateTime lastUpdated,
final Optional<OffsetDateTime> stopped,
final JobStatus status,
final List<JobMessage> messages,
Clock clock, final String hostname) {
this.jobId = jobId;
this.jobType = jobType;
this.started = started;
this.lastUpdated = lastUpdated;
this.stopped = stopped;
this.status = status;
this.messages = unmodifiableList(messages);
this.hostname = hostname;
this.clock = clock;
}
/**
* @return true if the job is finished, false, if it is still in execution.
*/
public synchronized boolean isStopped() {
return stopped.isPresent();
}
/**
* @return the id of the job
*/
public String getJobId() {
return jobId;
}
/**
* @return the job type
*/
public String getJobType() {
return jobType;
}
/**
* @return the name of the server this job is executed on
*/
public String getHostname() {
return hostname;
}
/**
* @return timestamp when the job was started
*/
public OffsetDateTime getStarted() {
return started;
}
/**
* @return the current status of the job: OK, ERROR or DEAD
*/
public JobStatus getStatus() {
return status;
}
/**
* @return the timestamp when the job was stopped, of empty, if the job is still running.
*/
public Optional<OffsetDateTime> getStopped() {
return stopped;
}
/**
* @return list of job messages, containing human-readable information about what happened during execution.
*/
public List<JobMessage> getMessages() {
return messages;
}
/**
* @return last updated timestamp
*/
public OffsetDateTime getLastUpdated() {
return lastUpdated;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
JobInfo jobInfo = (JobInfo) o;
if (jobType != null ? !jobType.equals(jobInfo.jobType) : jobInfo.jobType != null) return false;
if (jobId != null ? !jobId.equals(jobInfo.jobId) : jobInfo.jobId != null) return false;
if (lastUpdated != null ? !lastUpdated.equals(jobInfo.lastUpdated) : jobInfo.lastUpdated != null) return false;
if (messages != null ? !messages.equals(jobInfo.messages) : jobInfo.messages != null) return false;
if (started != null ? !started.equals(jobInfo.started) : jobInfo.started != null) return false;
if (status != jobInfo.status) return false;
if (stopped != null ? !stopped.equals(jobInfo.stopped) : jobInfo.stopped != null) return false;
if (hostname != null ? !hostname.equals(jobInfo.hostname) : jobInfo.hostname != null) return false;
return true;
}
@Override
public int hashCode() {
int result = jobId != null ? jobId.hashCode() : 0;
result = 31 * result + (jobType != null ? jobType.hashCode() : 0);
result = 31 * result + (started != null ? started.hashCode() : 0);
result = 31 * result + (stopped != null ? stopped.hashCode() : 0);
result = 31 * result + (messages != null ? messages.hashCode() : 0);
result = 31 * result + (status != null ? status.hashCode() : 0);
result = 31 * result + (lastUpdated != null ? lastUpdated.hashCode() : 0);
result = 31 * result + (hostname != null ? hostname.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "JobInfo{" +
"jobId=" + jobId +
", jobType=" + jobType +
", started=" + started +
", hostname=" + hostname +
", stopped=" + stopped +
", messages=" + messages +
", status=" + status +
", lastUpdated=" + lastUpdated +
'}';
}
public static Builder builder() {
return new Builder();
}
public Builder copy() {
return new Builder(jobId, jobType, started, new ArrayList<>(messages), stopped, status, lastUpdated, hostname, clock);
}
public static final class Builder {
private String jobId;
private String jobType;
private OffsetDateTime started;
private List<JobMessage> messages = new ArrayList<>();
private Clock clock;
private OffsetDateTime stopped;
private JobStatus status;
private OffsetDateTime lastUpdated;
private String hostname;
public Builder() {
}
public Builder(String jobId, String jobType, OffsetDateTime started, List<JobMessage> messages,
Optional<OffsetDateTime> stopped, JobStatus status, OffsetDateTime lastUpdated,
String hostname, Clock clock) {
this.jobId = jobId;
this.jobType = jobType;
this.started = started;
this.messages = messages;
this.clock = clock;
this.stopped = stopped.orElse(null);
this.status = status;
this.lastUpdated = lastUpdated;
this.hostname = hostname;
}
public Builder setJobId(String jobId) {
this.jobId = jobId;
return this;
}
public Builder setClock(Clock clock) {
this.clock = clock;
return this;
}
public Builder setJobType(String jobType) {
this.jobType = jobType;
return this;
}
public Builder setStarted(OffsetDateTime started) {
this.started = started;
return this;
}
public Builder setMessages(List<JobMessage> messages) {
this.messages = messages;
return this;
}
public Builder setStopped(OffsetDateTime stopped) {
this.stopped = stopped;
return this;
}
public Builder setStatus(JobStatus status) {
this.status = status;
return this;
}
public Builder setLastUpdated(OffsetDateTime lastUpdated) {
this.lastUpdated = lastUpdated;
return this;
}
public Builder setHostname(String hostname) {
this.hostname = hostname;
return this;
}
public JobInfo build() {
return new JobInfo(jobId, jobType, started, lastUpdated, ofNullable(stopped), status, messages, clock, hostname);
}
public Builder addMessage(JobMessage jobMessage) {
this.messages.add(jobMessage);
return this;
}
}
}