I have a Java application that makes use of a Quartz Scheduler in the guise of a SchedulerFactoryBean. The main()
method gets the application context, retrieves the root bean, and commences scheduling jobs.
The issue is that the Scheduler runs in its own thread, so when the main thread is done submitting jobs, it returns and the Scheduler goes on without it. When the Scheduler is finally done (or even if you explicitly call shutdown()
on it), the application just hangs there for all eternity.
I have two solutions:
triggerFinalized()
, and set up a while
loop with a Thread.sleep()
inside it that constantly checks to see if the count has hit 0. When it does, it will return up to the main()
method and the application will exit normally.jobAdded()
, and decrement for every call to triggerFinalized()
. When the count hits 0, call shutdown()
on the Scheduler (or not, it doesn't actually matter) and then call System.exit(0)
.I have implemented both of these independently in turn, so I know they both actually function. The problem is that they are both terrible. An infinite while
loop polling a value? System.exit(0)
? Bleargh.
Does someone have a better way, or are these seriously my only options here?
Edit: While thinking about this on the way home, I came to the conclusion that this may be caused by the fact that I'm using SchedulerFactoryBean. This auto-starts when Spring initializes the application context - that seems to put it outside the scope of the main thread. If I went with a slightly different Scheduler that I manually initialized and called start()
on in the code, would this run the Scheduler in the main thread, thus blocking it until the Scheduler completes running all jobs? Or would I still have this problem?
Edit: Son of a...http://quartz-scheduler.org/documentation/quartz-2.x/examples/Example1
To let the program have an opportunity to run the job, we then sleep for 90 seconds. The scheduler is running in the background and should fire off the job during those 90 seconds.
Apparently, that will not work, because the scheduler seems to always run in the background.
In your SchedulerListener
add an object solely for synchronization and locking. Call it exitLock
or something. You main thread retrieves the scheduler, sets up the listener, submits all the jobs and then just before returning executes
Object exitLock = listener.getExitLock();
synchronized (exitLock) {
exitLock.wait(); // wait unless notified to terminate
}
On every triggerFinalized()
call your listener decrements the counter for pending jobs. Once all the jobs have finished executing your listener shuts the scheduler down.
if (--pendingJobs == 0)
scheduler.shutdown(); // notice, we don't notify exit from here
Once the scheduler shuts down it invokes one last callback on the listener where we notify the main thread to terminate and hence the program exits gracefully.
void schedulerShutdown() {
// scheduler has stopped
synchronized (exitLock) {
exitLock.notify(); // notify the main thread to terminate
}
}
The reason we didn't notify in triggerFinalized()
when all the pending jobs were finished is that in case the scheduler was shutdown prematurely and not all the jobs were finished we would have left our main thread hanging. By notifying in response to the shutdown event we make sure our program exits successfully.
I think here can be another solution.
Key points:
context.getNextFireTime()
returns null
.Scheduler.getCurrentlyExecutingJobs == 1
indicate that it is the last executed job.So when point 1 and 2 is true we can shutdown Scheduler and call System.exit(0)
.
Here is the code:
Listener
public class ShutDownListenet implements JobListener {
@Override
public String getName () { return "someName"; }
@Override
public void jobToBeExecuted (JobExecutionContext context) {}
@Override
public void jobExecutionVetoed (JobExecutionContext context) {}
@Override
public void jobWasExecuted (JobExecutionContext context, JobExecutionException jobException) {
try {
if (context.getNextFireTime() == null && context.getScheduler().getCurrentlyExecutingJobs().size() == 1) {
context.getScheduler().shutdown();
System.exit(0);
}
} catch (SchedulerException e) {
e.printStackTrace();
}
}
}
Code in the main function public static void main (String[] args) { Trigger trigger = ... Job job = ...
JobListener listener = new ShutDownListenet();
scheduler.getListenerManager().addJobListener(listener);
scheduler.scheduleJob(job, trigger);
}
NOTE
synchronized
blocks, but I tested this code with 100 concurent jobs, it works.Any comments are wellcome.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With