All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.quartz.core.QuartzSchedulerThread Maven / Gradle / Ivy

Go to download

SDK for dev_appserver (local development) with some of the dependencies shaded (repackaged)

There is a newer version: 2.0.31
Show newest version

/* 
 * Copyright 2004-2005 OpenSymphony 
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 
 * use this file except in compliance with the License. You may obtain a copy 
 * of the License at 
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0 
 *   
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 
 * License for the specific language governing permissions and limitations 
 * under the License.
 * 
 */

/*
 * Previously Copyright (c) 2001-2004 James House
 */
package org.quartz.core;

import java.util.Random;

import com.google.appengine.repackaged.org.apache.commons.logging.Log;
import com.google.appengine.repackaged.org.apache.commons.logging.LogFactory;
import org.quartz.Job;
import org.quartz.JobPersistenceException;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.spi.TriggerFiredBundle;

/**
 * 

* The thread responsible for performing the work of firing {@link Trigger} * s that are registered with the {@link QuartzScheduler}. *

* * @see QuartzScheduler * @see Job * @see Trigger * * @author James House */ public class QuartzSchedulerThread extends Thread { /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * Data members. * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ private QuartzScheduler qs; private QuartzSchedulerResources qsRsrcs; private Object pauseLock = new Object(); private Object idleLock = new Object(); private boolean signaled; private boolean paused; private boolean halted; private SchedulingContext ctxt = null; private Random random = new Random(System.currentTimeMillis()); // When the scheduler finds there is no current trigger to fire, how long // it should wait until checking again... private static long DEFAULT_IDLE_WAIT_TIME = 30L * 1000L; private long idleWaitTime = DEFAULT_IDLE_WAIT_TIME; private int idleWaitVariablness = 7 * 1000; private long dbFailureRetryInterval = 15L * 1000L; /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * Constructors. * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ /** *

* Construct a new QuartzSchedulerThread for the given * QuartzScheduler as a non-daemon Thread * with normal priority. *

*/ QuartzSchedulerThread(QuartzScheduler qs, QuartzSchedulerResources qsRsrcs, SchedulingContext ctxt) { this(qs, qsRsrcs, ctxt, false, Thread.NORM_PRIORITY); } /** *

* Construct a new QuartzSchedulerThread for the given * QuartzScheduler as a Thread with the given * attributes. *

*/ QuartzSchedulerThread(QuartzScheduler qs, QuartzSchedulerResources qsRsrcs, SchedulingContext ctxt, boolean setDaemon, int threadPrio) { super(qs.getSchedulerThreadGroup(), qsRsrcs.getThreadName()); this.qs = qs; this.qsRsrcs = qsRsrcs; this.ctxt = ctxt; this.setDaemon(setDaemon); this.setPriority(threadPrio); // start the underlying thread, but put this object into the 'paused' // state // so processing doesn't start yet... paused = true; halted = false; this.start(); } /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * Interface. * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ void setIdleWaitTime(long waitTime) { idleWaitTime = waitTime; idleWaitVariablness = (int) (waitTime * 0.2); } private long getDbFailureRetryInterval() { return dbFailureRetryInterval; } public void setDbFailureRetryInterval(long dbFailureRetryInterval) { this.dbFailureRetryInterval = dbFailureRetryInterval; } private long getRandomizedIdleWaitTime() { return idleWaitTime - random.nextInt(idleWaitVariablness); } /** *

* Signals the main processing loop to pause at the next possible point. *

*/ void togglePause(boolean pause) { synchronized (pauseLock) { paused = pause; if (paused) { signalSchedulingChange(); } else { pauseLock.notify(); } } } /** *

* Signals the main processing loop to pause at the next possible point. *

*/ void halt() { synchronized (pauseLock) { halted = true; if (paused) { pauseLock.notify(); } else { signalSchedulingChange(); } } } boolean isPaused() { return paused; } /** *

* Signals the main processing loop that a change in scheduling has been * made - in order to interrupt any sleeping that may be occuring while * waiting for the fire time to arrive. *

*/ void signalSchedulingChange() { signaled = true; } /** *

* The main processing loop of the QuartzSchedulerThread. *

*/ public void run() { boolean lastAcquireFailed = false; while (!halted) { signaled = false; try { // check if we're supposed to pause... synchronized (pauseLock) { while (paused && !halted) { try { // wait until togglePause(false) is called... pauseLock.wait(100L); } catch (InterruptedException ignore) { } } if (halted) { break; } } Trigger trigger = null; long now = System.currentTimeMillis(); try { trigger = qsRsrcs.getJobStore().acquireNextTrigger( ctxt, now + idleWaitTime); lastAcquireFailed = false; } catch (JobPersistenceException jpe) { if(!lastAcquireFailed) qs.notifySchedulerListenersError( "An error occured while scanning for the next trigger to fire.", jpe); lastAcquireFailed = true; } catch (RuntimeException e) { if(!lastAcquireFailed) getLog().error("quartzSchedulerThreadLoop: RuntimeException " +e.getMessage(), e); lastAcquireFailed = true; } if (trigger != null) { now = System.currentTimeMillis(); long triggerTime = trigger.getNextFireTime().getTime(); long timeUntilTrigger = triggerTime - now; long spinInterval = 10; // this looping may seem a bit silly, but it's the // current work-around // for a dead-lock that can occur if the Thread.sleep() // is replaced with // a obj.wait() that gets notified when the signal is // set... // so to be able to detect the signal change without // sleeping the entire // timeUntilTrigger, we spin here... don't worry // though, this spinning // doesn't even register 0.2% cpu usage on a pentium 4. int numPauses = (int) (timeUntilTrigger / spinInterval); while (numPauses >= 0 && !signaled) { try { Thread.sleep(spinInterval); } catch (InterruptedException ignore) { } now = System.currentTimeMillis(); timeUntilTrigger = triggerTime - now; numPauses = (int) (timeUntilTrigger / spinInterval); } if (signaled) { try { qsRsrcs.getJobStore().releaseAcquiredTrigger( ctxt, trigger); } catch (JobPersistenceException jpe) { qs.notifySchedulerListenersError( "An error occured while releasing trigger '" + trigger.getFullName() + "'", jpe); // db connection must have failed... keep // retrying until it's up... releaseTriggerRetryLoop(trigger); } catch (RuntimeException e) { getLog().error( "releaseTriggerRetryLoop: RuntimeException " +e.getMessage(), e); // db connection must have failed... keep // retrying until it's up... releaseTriggerRetryLoop(trigger); } signaled = false; continue; } // set trigger to 'executing' TriggerFiredBundle bndle = null; try { bndle = qsRsrcs.getJobStore().triggerFired(ctxt, trigger); } catch (SchedulerException se) { qs.notifySchedulerListenersError( "An error occured while firing trigger '" + trigger.getFullName() + "'", se); } catch (RuntimeException e) { getLog().error( "RuntimeException while firing trigger " + trigger.getFullName(), e); // db connection must have failed... keep // retrying until it's up... releaseTriggerRetryLoop(trigger); } // it's possible to get 'null' if the trigger was paused, // blocked, or other similar occurances that prevent it being // fired at this time... if (bndle == null) { try { qsRsrcs.getJobStore().releaseAcquiredTrigger(ctxt, trigger); } catch (SchedulerException se) { qs.notifySchedulerListenersError( "An error occured while releasing trigger '" + trigger.getFullName() + "'", se); // db connection must have failed... keep retrying // until it's up... releaseTriggerRetryLoop(trigger); } continue; } // TODO: improvements: // // 1- get thread from pool before firing trigger. // 2- make sure we can get a job runshell first as well, or // don't let that throw an exception (right now it never does, // bugthe signature says it can). // 3- acquire more triggers at a time (based on num threads?) JobRunShell shell = null; try { shell = qsRsrcs.getJobRunShellFactory().borrowJobRunShell(); shell.initialize(qs, bndle); } catch (SchedulerException se) { try { qsRsrcs.getJobStore().triggeredJobComplete(ctxt, trigger, bndle.getJobDetail(), Trigger.INSTRUCTION_SET_ALL_JOB_TRIGGERS_ERROR); } catch (SchedulerException se2) { qs.notifySchedulerListenersError( "An error occured while releasing trigger '" + trigger.getFullName() + "'", se2); // db connection must have failed... keep retrying // until it's up... errorTriggerRetryLoop(bndle); } continue; } qsRsrcs.getThreadPool().runInThread(shell); continue; } // this looping may seem a bit silly, but it's the current // work-around // for a dead-lock that can occur if the Thread.sleep() is replaced // with // a obj.wait() that gets notified when the signal is set... // so to be able to detect the signal change without sleeping the // entier // getRandomizedIdleWaitTime(), we spin here... don't worry though, // the // CPU usage of this spinning can't even be measured on a pentium // 4. now = System.currentTimeMillis(); long waitTime = now + getRandomizedIdleWaitTime(); long timeUntilContinue = waitTime - now; long spinInterval = 10; int numPauses = (int) (timeUntilContinue / spinInterval); while (numPauses > 0 && !signaled) { try { Thread.sleep(10L); } catch (InterruptedException ignore) { } now = System.currentTimeMillis(); timeUntilContinue = waitTime - now; numPauses = (int) (timeUntilContinue / spinInterval); } } catch(RuntimeException re) { getLog().error("Runtime error occured in main trigger firing loop.", re); } } // loop... // drop references to scheduler stuff to aid garbage collection... qs = null; qsRsrcs = null; } public void errorTriggerRetryLoop(TriggerFiredBundle bndle) { int retryCount = 0; try { while (!halted) { try { Thread.sleep(getDbFailureRetryInterval()); // retry every N // seconds (the db // connection must // be failed) retryCount++; qsRsrcs.getJobStore().triggeredJobComplete(ctxt, bndle.getTrigger(), bndle.getJobDetail(), Trigger.INSTRUCTION_SET_ALL_JOB_TRIGGERS_ERROR); retryCount = 0; break; } catch (JobPersistenceException jpe) { if(retryCount % 4 == 0) qs.notifySchedulerListenersError( "An error occured while releasing trigger '" + bndle.getTrigger().getFullName() + "'", jpe); } catch (RuntimeException e) { getLog().error("releaseTriggerRetryLoop: RuntimeException "+e.getMessage(), e); } catch (InterruptedException e) { getLog().error("releaseTriggerRetryLoop: InterruptedException "+e.getMessage(), e); } } } finally { if(retryCount == 0) getLog().info("releaseTriggerRetryLoop: connection restored."); } } public void releaseTriggerRetryLoop(Trigger trigger) { int retryCount = 0; try { while (!halted) { try { Thread.sleep(getDbFailureRetryInterval()); // retry every N // seconds (the db // connection must // be failed) retryCount++; qsRsrcs.getJobStore().releaseAcquiredTrigger(ctxt, trigger); retryCount = 0; break; } catch (JobPersistenceException jpe) { if(retryCount % 4 == 0) qs.notifySchedulerListenersError( "An error occured while releasing trigger '" + trigger.getFullName() + "'", jpe); } catch (RuntimeException e) { getLog().error("releaseTriggerRetryLoop: RuntimeException "+e.getMessage(), e); } catch (InterruptedException e) { getLog().error("releaseTriggerRetryLoop: InterruptedException "+e.getMessage(), e); } } } finally { if(retryCount == 0) getLog().info("releaseTriggerRetryLoop: connection restored."); } } public static Log getLog() { return LogFactory.getLog(QuartzSchedulerThread.class); } } // end of QuartzSchedulerThread




© 2015 - 2024 Weber Informatics LLC | Privacy Policy