package com.indeed.proctor.webapp.controllers;
import com.google.common.collect.Lists;
import com.indeed.proctor.webapp.extensions.AfterBackgroundJobExecute;
import com.indeed.proctor.webapp.extensions.BeforeBackgroundJobExecute;
import org.apache.log4j.Logger;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
/**
*/
public abstract class BackgroundJob<T> implements Callable<T> {
private static final Logger LOGGER = Logger.getLogger(BackgroundJob.class);
private Future<T> future;
private JobStatus status = JobStatus.PENDING;
protected final StringBuilder logBuilder = new StringBuilder();
private Long id;
private final long createdTime = System.currentTimeMillis();
// URL to direct users to upon completion
private final List<ResultUrl> urls = Lists.newArrayList();
private String endMessage = "";
private Throwable error = null;
private boolean executeFinished = false;
public void log(final String message) {
logBuilder.append(message).append("\n");
}
public String getLog() {
return logBuilder.toString();
}
public JobStatus getStatus() {
if (future != null && status == JobStatus.PENDING) {
if (future.isCancelled()) {
setStatus(JobStatus.CANCELLED);
} else if(error != null) {
setStatus(JobStatus.FAILED);
} else if (executeFinished){
setStatus(JobStatus.DONE);
}
}
return status;
}
public void setStatus(final JobStatus status) {
this.status = status;
}
public Future<T> getFuture() {
return future;
}
public void setFuture(final Future<T> future) {
this.future = future;
}
public void setId(final long id) {
this.id = id;
}
public Long getId() {
return id;
}
public List<ResultUrl> getUrls() {
return urls;
}
public void addUrl(final String url, final String text) {
this.addUrl(url, text, "");
}
public void addUrl(final String url, final String text, final String target) {
this.addUrl(new ResultUrl(url, text, target));
}
public void addUrl(final ResultUrl url) {
this.urls.add(url);
}
public String getEndMessage() {
return endMessage;
}
public void setEndMessage(final String endMessage) {
this.endMessage = endMessage;
}
public Throwable getError() {
return error;
}
public void setError(final Throwable error) {
this.error = error;
}
public long getCreatedTime() {
return createdTime;
}
public String toString() {
return id + ": " + status;
}
public boolean isRunning() {
return future == null || (!future.isDone() && !future.isCancelled());
}
public abstract String getTitle();
public JobType getJobType() {
return JobType.UNKNOWN;
}
public void logFailedJob(final Throwable t) {
log("Failed:");
Throwable cause = t;
final StringBuilder level = new StringBuilder(10);
while (cause != null) {
log(level.toString() + cause.getMessage());
cause = cause.getCause();
level.append("-- ");
}
if (!future.isCancelled() && !executeFinished) {
setError(t);
}
}
public static class ResultUrl {
private final String href;
private final String text;
private final String target;
public ResultUrl(final String href,
final String text,
final String target) {
this.href = href;
this.text = text;
this.target = target;
}
public String getHref() {
return href;
}
public String getTarget() {
return target;
}
public String getText() {
return text;
}
}
protected abstract List<BeforeBackgroundJobExecute> getBeforeBackgroundJobExecutes();
protected abstract List<AfterBackgroundJobExecute> getAfterBackgroundJobExecutes();
protected abstract T execute() throws Exception;
@Override
public T call() throws Exception {
T result = null;
try {
for (final BeforeBackgroundJobExecute beforeBackgroundJobExecute : getBeforeBackgroundJobExecutes()) {
beforeBackgroundJobExecute.beforeExecute(this);
}
} catch (final Exception e) {
LOGGER.error("BeforeBackgroundJobExecute Failed: " + getTitle(), e);
logFailedJob(e);
return null;
}
try {
result = execute();
} catch (final Exception e) {
LOGGER.error("Background Job Failed: " + getTitle(), e);
logFailedJob(e);
} finally {
executeFinished = true;
}
try {
for (final AfterBackgroundJobExecute afterBackgroundJobExecute : getAfterBackgroundJobExecutes()) {
afterBackgroundJobExecute.afterExecute(this, result);
}
} catch (final Exception e) {
LOGGER.error("AfterBackgroundJobExecute Failed: " + getTitle(), e);
logFailedJob(e);
}
return result;
}
public enum JobType {
TEST_CREATION("test-creation"),
TEST_EDIT("test-edit"),
TEST_DELETION("test-deletion"),
TEST_PROMOTION("test-promotion"),
WORKING_DIRECTORY_CLEANING("working-directory-cleaning"),
JOB_TEST("job-test"),
UNKNOWN("unknown");
private final String name;
JobType(final String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public enum JobStatus {
PENDING("PENDING"),
DONE("DONE"),
CANCELLED("CANCELLED"),
FAILED("FAILED");
private final String name;
JobStatus(final String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public String toString() {
return name;
}
}
}