问题
I'm using Play! framwork of version 1.2.4
In my application, I have implemented a number of scheduled jobs (descendants of play.jobs.Job
).
Question
Can I change the schedule of these jobs at runtime?
My own findings
At first glance, the job scheduling in Play! is done via play.jobs.JobsPlugin
, which in turn uses JDK's java.util.concurrent.ScheduledThreadPoolExecutor
instance which is publicly exposed. So far I came up with following approach: when it's the moment to change schedule, I simply iterate over executor' scheduled jobs, cancel one which I need to reschedule and schedule the same job (more precisely, the job of the same class) with new settings. Something like this:
for (final Object o: JobsPlugin.executor.getQueue())
{
ScheduledFuture task = (ScheduledFuture) o;
Job job = (Job) Java.extractUnderlyingCallable((FutureTask)task);
if (MyJob.class == job.getClass())
{
task.cancel(true);
new MyJob().every("2h");
break;
}
}
Are there any better solutions? Thanks!
回答1:
I am not sure your solution works because when you look at JobsPlugin call, it recalculates Job next planned execution date at the end of each job execution.
Here is my solution, I recreate a JobScheduler class to be able to calculate job execution date based on a cron attribute of my job. I had to redeclare some jobs attributes because of visibility rules.
So my job class is
package jobs;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import play.jobs.Job;
public class MyJob extends Job<Object> {
public Date nextPlannedExecution = null;
public String cron = null;
@Override
public void _finally() {
// cron is null if we force job execution
if (cron != null) {
// As the job don't have a Cron annotation, super call does nothing
super._finally();
JobScheduler.scheduleForCRON(this, cron);
}
}
void setExecutor(ExecutorService executor) {
this.executor = executor;
}
}
And my scheduler class is package jobs;
import java.util.Date;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import models.Partner;
import play.Logger;
import play.jobs.Job;
import play.jobs.JobsPlugin;
import play.libs.Expression;
import play.libs.Time.CronExpression;
public class JobScheduler {
public static synchronized void scheduleCsvExportJob(String cron) {
MyJob myJob = null;
for (Job<?> job : JobsPlugin.scheduledJobs) {
if (job instanceof MyJob) {
myJob = (MyJob) job;
}
}
if (myJob == null) {
myJob = new MyJob();
JobsPlugin.scheduledJobs.add(myJob);
}
myJob.cron = cron;
scheduleForCRON(myJob, myJob.cron);
}
public static void scheduleForCRON(MyJob job, String cron) {
try {
Date now = new Date();
cron = Expression.evaluate(cron, cron).toString();
CronExpression cronExp = new CronExpression(cron);
Date nextDate = cronExp.getNextValidTimeAfter(now);
if (nextDate != null && !nextDate.equals(job.nextPlannedExecution)) {
job.nextPlannedExecution = nextDate;
JobsPlugin.executor.schedule((Callable<?>) job, nextDate.getTime() - now.getTime(),
TimeUnit.MILLISECONDS);
job.setExecutor(JobsPlugin.executor);
}
} catch (Exception ex) {
Logger.error(ex, "Cannot schedule job %s", job);
}
}
}
With this code, you can call JobScheduler.scheduleMyJob to change the scheduling of the job by passing the right scheduling expression : 0 0 2 * * ? for every 2 hours.
When the job terminates, the _finally method will recall the scheduleForCRON method which set the new scheduling time.
If you want to force execution of the job, for example through a gui button, you can create an instance without cron attribute define and this instance won't reschedule itselft at the end
来源:https://stackoverflow.com/questions/12538170/play-framework-can-i-reschedule-the-job