package play.jobs;
import java.util.Date;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import play.Invoker;
import play.Invoker.InvocationContext;
import play.Logger;
import play.Play;
import play.exceptions.JavaExecutionException;
import play.exceptions.PlayException;
import play.libs.F.Promise;
import play.libs.Time;
import com.jamonapi.Monitor;
import com.jamonapi.MonitorFactory;
/**
* A job is an asynchronously executed unit of work
* @param <V> The job result type (if any)
*/
public class Job<V> extends Invoker.Invocation implements Callable<V> {
public static final String invocationType = "Job";
protected ExecutorService executor;
protected long lastRun = 0;
protected boolean wasError = false;
protected Throwable lastException = null;
Date nextPlannedExecution = null;
@Override
public InvocationContext getInvocationContext() {
return new InvocationContext(invocationType, this.getClass().getAnnotations());
}
/**
* Here you do the job
*/
public void doJob() throws Exception {
}
/**
* Here you do the job and return a result
*/
public V doJobWithResult() throws Exception {
doJob();
return null;
}
@Override
public void execute() throws Exception {
}
/**
* Start this job now (well ASAP)
* @return the job completion
*/
public Promise<V> now() {
final Promise<V> smartFuture = new Promise<V>();
JobsPlugin.executor.submit(new Callable<V>() {
public V call() throws Exception {
V result = Job.this.call();
smartFuture.invoke(result);
return result;
}
});
return smartFuture;
}
/**
* Start this job in several seconds
* @return the job completion
*/
public Promise<V> in(String delay) {
return in(Time.parseDuration(delay));
}
/**
* Start this job in several seconds
* @return the job completion
*/
public Promise<V> in(int seconds) {
final Promise<V> smartFuture = new Promise<V>();
JobsPlugin.executor.schedule(new Callable<V>() {
public V call() throws Exception {
V result = Job.this.call();
smartFuture.invoke(result);
return result;
}
}, seconds, TimeUnit.SECONDS);
return smartFuture;
}
/**
* Run this job every n seconds
*/
public void every(String delay) {
every(Time.parseDuration(delay));
}
/**
* Run this job every n seconds
*/
public void every(int seconds) {
JobsPlugin.executor.scheduleWithFixedDelay(this, seconds, seconds, TimeUnit.SECONDS);
}
// Customize Invocation
@Override
public void onException(Throwable e) {
wasError = true;
lastException = e;
try {
super.onException(e);
} catch(Throwable ex) {
Logger.error(ex, "Error during job execution (%s)", this);
}
}
@Override
public void run() {
call();
}
public V call() {
Monitor monitor = null;
try {
if (init()) {
before();
V result = null;
try {
lastException = null;
lastRun = System.currentTimeMillis();
monitor = MonitorFactory.start(getClass().getName()+".doJob()");
result = doJobWithResult();
monitor.stop();
monitor = null;
wasError = false;
} catch (PlayException e) {
throw e;
} catch (Exception e) {
StackTraceElement element = PlayException.getInterestingStrackTraceElement(e);
if (element != null) {
throw new JavaExecutionException(Play.classes.getApplicationClass(element.getClassName()), element.getLineNumber(), e);
}
throw e;
}
after();
return result;
}
} catch (Throwable e) {
onException(e);
} finally {
if(monitor != null) {
monitor.stop();
}
_finally();
}
return null;
}
@Override
public void _finally() {
super._finally();
if (executor == JobsPlugin.executor) {
JobsPlugin.scheduleForCRON(this);
}
}
@Override
public String toString() {
return this.getClass().getName();
}
}