Wait in the main topic for the quartz planner to finish

I have a Java application that uses a Quartz Scheduler as a SchedulerFactoryBean . The main() method gets the application context, retrieves the bean root, and runs the scheduling tasks.

The problem is that the scheduler works in its own thread, so when the main thread sends jobs, it returns and the Scheduler continues without it. When the Scheduler is finally complete (or even if you explicitly call shutdown() on it), the application simply hangs there for ages.

I have two solutions:

  • Keep track of the number of tasks / triggers, increasing it each time a task is added to the Scheduler. Attach a simple SchedulerListener to the Scheduler, which decreases this count each time triggerFinalized() called and sets up a while with Thread.sleep() inside it, which constantly checks to see if the count 0 has been deleted. When it does, it will return to the main() , and the application will exit normally.
  • Take a custom SchedulerListener from option 1 and track the number of jobs inside it. The increment for each call to jobAdded() and the decrease for each call to triggerFinalized() . When the counter reaches 0, call shutdown() in the Scheduler (or not, it doesn't really matter), and then call System.exit(0) .

In turn, I implemented both of these parameters myself, so I know that they both actually function. The problem is that they are both terrible. Infinite while by polling a value? System.exit(0) ? Bleargh.

Does anyone have a better way, or is this seriously my options?

Edit:. After thinking about this on my way home, I came to the conclusion that this could be due to the fact that I am using SchedulerFactoryBean. It automatically starts when Spring initializes an application context that seems to go beyond the main thread. If I went with a slightly different Scheduler, which I manually initialized and called start() in the code, would this launch the Scheduler in the main thread, blocking it until the Scheduler completed all the tasks? Or will I still have this problem?

Edit: Son ... http://quartz-scheduler.org/documentation/quartz-2.x/examples/Example1

For the program to be able to run the task, we then sleep for 90 seconds. The scheduler runs in the background and must fire the job in these 90 seconds.

Apparently, this will not work, because the scheduler always runs in the background.

+4
source share
5 answers

In SchedulerListener add an object exclusively for synchronization and blocking. Call it exitLock or something else. The main thread is retrieved by the scheduler, configures the listener, sends all jobs, and then executes before returning

 Object exitLock = listener.getExitLock(); synchronized (exitLock) { exitLock.wait(); // wait unless notified to terminate } 

Each time triggerFinalized() called, your listener decreases the counter for pending jobs. When all tasks are completed, your listener disables the scheduler.

 if (--pendingJobs == 0) scheduler.shutdown(); // notice, we don't notify exit from here 

As soon as the scheduler shuts down, it calls the last callback on the listener, where we notify the main thread of completion and, therefore, the program exits gracefully.

 void schedulerShutdown() { // scheduler has stopped synchronized (exitLock) { exitLock.notify(); // notify the main thread to terminate } } 

The reason we did not notify triggerFinalized() when all pending tasks were completed is that if the scheduler was shut down prematurely and not all tasks were completed, we will leave our main thread hanging. Using a notification in response to a shutdown event, ensure that our program has completed successfully.

+4
source

I think this may be a different solution.

Key points:

  • When the task was completed, the last time context.getNextFireTime() returns null .
  • Scheduler.getCurrentlyExecutingJobs == 1 indicate that this is the last completed task.

So, when points 1 and 2 are true, we can turn off the 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(); } } } 

The code in the main function is public static void main (String [] args) {Trigger trigger = ... Job job = ...

  JobListener listener = new ShutDownListenet(); scheduler.getListenerManager().addJobListener(listener); scheduler.scheduleJob(job, trigger); } 

Note

  • I do not write synchronized blocks, but I tested this code with 100 matching jobs, it works.
  • Not tested in a "complex" environment: clusters or RMI. (behavior may be different).

Any comments are welcome.

+2
source

If your quartz schedules / triggers are database based, then the program should be live until you want to stop it. This can be done as shown below. The idea is to hook SchedulerListener and wait in the main thread. You need to connect your own path in order to finish the program gracefully, which is completely different from the theme itself.

 public static void main(String[] args) { AnnotationConfigApplicationContext appContext = // initialize the your spring app Context // register the shutdown hook for JVM appContext.registerShutdownHook(); SchedulerFactoryBean schedulerFactory = appContext.getBean(SchedulerFactoryBean.class); scheduler = schedulerFactory.getScheduler(); final Lock lock = new ReentrantLock(); final Condition waitCond = lock.newCondition(); try { scheduler.getListenerManager().addSchedulerListener(new SchedulerListener() { @Override public void jobAdded(JobDetail arg0) { } @Override public void jobDeleted(JobKey arg0) { } @Override public void jobPaused(JobKey arg0) { } @Override public void jobResumed(JobKey arg0) { } @Override public void jobScheduled(Trigger arg0) { } @Override public void jobUnscheduled(TriggerKey arg0) { } @Override public void jobsPaused(String arg0) { } @Override public void jobsResumed(String arg0) { } @Override public void schedulerError(String arg0, SchedulerException arg1) { } @Override public void schedulerInStandbyMode() { } @Override public void schedulerShutdown() { lock.lock(); try { waitCond.signal(); } finally { lock.unlock(); } } @Override public void schedulerShuttingdown() { } @Override public void schedulerStarted() { } @Override public void schedulerStarting() { } @Override public void schedulingDataCleared() { } @Override public void triggerFinalized(Trigger arg0) { } @Override public void triggerPaused(TriggerKey arg0) { } @Override public void triggerResumed(TriggerKey arg0) { } @Override public void triggersPaused(String arg0) { } @Override public void triggersResumed(String arg0) { } }); // start the scheduler. I set the SchedulerFactoryBean.setAutoStartup(false) scheduler.start(); lock.lock(); try { waitCond.await(); } finally { lock.unlock(); } } finally { scheduler.shutdown(true); } } 
0
source

If it helps someone else. I solved this by adding a trailing hook that launches Ctrl-C or normal kill (15) from the script. A new thread is generated and polled getCurrentlyExecutingJobs().size() every 3 seconds and ends when the job counter has reached zero and all jobs are completed.

 Runtime.getRuntime().addShutdownHook(new Thread(() -> { try { while (jobScheduler.getScheduler().getCurrentlyExecutingJobs().size() > 0) { Thread.sleep(3000); } jobScheduler.getScheduler().clear(); } catch (Exception e) { e.printStackTrace(); } })); 
0
source
  while (!scheduler.isShutdown()) { Thread.sleep(2L * 1000L);//Choose reasonable sleep time } 
-1
source

Source: https://habr.com/ru/post/1482662/


All Articles