/* * Copyright 2001-2009 Terracotta, Inc. * * 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. * */ package org.quartz.core; import java.io.InputStream; import java.lang.management.ManagementFactory; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.rmi.server.UnicastRemoteObject; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Properties; import java.util.Random; import java.util.Set; import java.util.Timer; import javax.management.MBeanServer; import javax.management.ObjectName; import org.quartz.Calendar; import org.quartz.InterruptableJob; import org.quartz.Job; import org.quartz.JobDataMap; import org.quartz.JobDetail; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.quartz.JobListener; import org.quartz.JobPersistenceException; import org.quartz.ObjectAlreadyExistsException; import org.quartz.Scheduler; import org.quartz.SchedulerContext; import org.quartz.SchedulerException; import org.quartz.SchedulerListener; import org.quartz.SchedulerMetaData; import org.quartz.Trigger; import org.quartz.TriggerListener; import org.quartz.UnableToInterruptJobException; import org.quartz.spi.ThreadExecutor; import org.quartz.core.jmx.QuartzSchedulerMBean; import org.quartz.impl.SchedulerRepository; import org.quartz.impl.StdSchedulerFactory; import org.quartz.listeners.SchedulerListenerSupport; import org.quartz.simpl.SimpleJobFactory; import org.quartz.spi.JobFactory; import org.quartz.spi.SchedulerPlugin; import org.quartz.spi.SchedulerSignaler; import org.quartz.utils.UpdateChecker; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** *

* This is the heart of Quartz, an indirect implementation of the {@link org.quartz.Scheduler} * interface, containing methods to schedule {@link org.quartz.Job}s, * register {@link org.quartz.JobListener} instances, etc. *

// TODO: more docs... * * @see org.quartz.Scheduler * @see org.quartz.core.QuartzSchedulerThread * @see org.quartz.spi.JobStore * @see org.quartz.spi.ThreadPool * * @author James House */ public class QuartzScheduler implements RemotableQuartzScheduler { /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * Constants. * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ private static String VERSION_MAJOR = "UNKNOWN"; private static String VERSION_MINOR = "UNKNOWN"; private static String VERSION_ITERATION = "UNKNOWN"; static { Properties props = new Properties(); InputStream is = null; try { is = QuartzScheduler.class.getResourceAsStream("quartz-build.properties"); if(is != null) { props.load(is); String version = props.getProperty("version"); if (version != null) { String[] versionComponents = version.split("\\."); VERSION_MAJOR = versionComponents[0]; VERSION_MINOR = versionComponents[1]; VERSION_ITERATION = versionComponents[2]; } else { (LoggerFactory.getLogger(QuartzScheduler.class)).error( "Can't parse Quartz version from quartz-build.properties"); } } } catch (Exception e) { (LoggerFactory.getLogger(QuartzScheduler.class)).error( "Error loading version info from quartz-build.properties.", e); } finally { if(is != null) { try { is.close(); } catch(Exception ignore) {} } } } /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * Data members. * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ private QuartzSchedulerResources resources; private QuartzSchedulerThread schedThread; private ThreadGroup threadGroup; private SchedulerContext context = new SchedulerContext(); private HashMap jobListeners = new HashMap(10); private HashMap globalJobListeners = new HashMap(10); private HashMap triggerListeners = new HashMap(10); private HashMap globalTriggerListeners = new HashMap(10); private ArrayList schedulerListeners = new ArrayList(10); private JobFactory jobFactory = new SimpleJobFactory(); ExecutingJobsManager jobMgr = null; ErrorLogger errLogger = null; private SchedulerSignaler signaler; private Random random = new Random(); private ArrayList holdToPreventGC = new ArrayList(5); private boolean signalOnSchedulingChange = true; private boolean closed = false; private boolean shuttingDown = false; private boolean boundRemotely = false; private QuartzSchedulerMBean jmxBean = null; private Date initialStart = null; /** Update timer that must be cancelled upon shutdown. */ private final Timer updateTimer; private final Logger log = LoggerFactory.getLogger(getClass()); private long dbRetryInterval; /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * Constructors. * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ /** *

* Create a QuartzScheduler with the given configuration * properties. *

* * @see QuartzSchedulerResources */ public QuartzScheduler(QuartzSchedulerResources resources, SchedulingContext ctxt, long idleWaitTime, long dbRetryInterval) throws SchedulerException { this.resources = resources; this.schedThread = new QuartzSchedulerThread(this, resources, ctxt); ThreadExecutor schedThreadExecutor = resources.getThreadExecutor(); schedThreadExecutor.execute(this.schedThread); if (idleWaitTime > 0) { this.schedThread.setIdleWaitTime(idleWaitTime); } if (dbRetryInterval > 0) { this.schedThread.setDbFailureRetryInterval(dbRetryInterval); } jobMgr = new ExecutingJobsManager(); addGlobalJobListener(jobMgr); errLogger = new ErrorLogger(); addSchedulerListener(errLogger); signaler = new SchedulerSignalerImpl(this, this.schedThread); if(shouldRunUpdateCheck()) updateTimer = scheduleUpdateCheck(); else updateTimer = null; this.dbRetryInterval = dbRetryInterval; getLog().info("Quartz Scheduler v." + getVersion() + " created."); } public long getDbRetryInterval() { return dbRetryInterval; } public void initialize() throws SchedulerException { try { bind(); } catch (Exception re) { throw new SchedulerException( "Unable to bind scheduler to RMI Registry.", re); } if (resources.getJMXExport()) { try { registerJMX(); } catch (Exception e) { throw new SchedulerException( "Unable to register scheduler with MBeanServer.", e); } } getLog().info("Scheduler meta-data: " + (new SchedulerMetaData(getSchedulerName(), getSchedulerInstanceId(), getClass(), boundRemotely, runningSince() != null, isInStandbyMode(), isShutdown(), runningSince(), numJobsExecuted(), getJobStoreClass(), supportsPersistence(), isClustered(), getThreadPoolClass(), getThreadPoolSize(), getVersion())).toString()); } /* * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * Interface. * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ public String getVersion() { return getVersionMajor() + "." + getVersionMinor() + "." + getVersionIteration(); } public static String getVersionMajor() { return VERSION_MAJOR; } private boolean shouldRunUpdateCheck() { if(resources.isRunUpdateCheck() && !Boolean.getBoolean(StdSchedulerFactory.PROP_SCHED_SKIP_UPDATE_CHECK) && !Boolean.getBoolean("org.terracotta.quartz.skipUpdateCheck")) { return true; } return false; } public static String getVersionMinor() { return VERSION_MINOR; } public static String getVersionIteration() { return VERSION_ITERATION; } public SchedulerSignaler getSchedulerSignaler() { return signaler; } public Logger getLog() { return log; } /** * Update checker scheduler - fires every week */ private Timer scheduleUpdateCheck() { Timer rval = new Timer(true); rval.scheduleAtFixedRate(new UpdateChecker(), 1000, 7 * 24 * 60 * 60 * 1000L); return rval; } /** * Register the scheduler in the local MBeanServer. */ private void registerJMX() throws Exception { String jmxObjectName = resources.getJMXObjectName(); MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); jmxBean = new QuartzSchedulerMBeanImpl(this); mbs.registerMBean(jmxBean, new ObjectName(jmxObjectName)); } /** * Unregister the scheduler from the local MBeanServer. */ private void unregisterJMX() throws Exception { String jmxObjectName = resources.getJMXObjectName(); MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); mbs.unregisterMBean(new ObjectName(jmxObjectName)); jmxBean.setSampledStatisticsEnabled(false); getLog().info("Scheduler unregistered from name '" + jmxObjectName + "' in the local MBeanServer."); } /** *

* Bind the scheduler to an RMI registry. *

*/ private void bind() throws RemoteException { String host = resources.getRMIRegistryHost(); // don't export if we're not configured to do so... if (host == null || host.length() == 0) { return; } RemotableQuartzScheduler exportable = null; if(resources.getRMIServerPort() > 0) { exportable = (RemotableQuartzScheduler) UnicastRemoteObject .exportObject(this, resources.getRMIServerPort()); } else { exportable = (RemotableQuartzScheduler) UnicastRemoteObject .exportObject(this); } Registry registry = null; if (resources.getRMICreateRegistryStrategy().equals( QuartzSchedulerResources.CREATE_REGISTRY_AS_NEEDED)) { try { // First try to get an existing one, instead of creating it, // since if // we're in a web-app being 'hot' re-depoloyed, then the JVM // still // has the registry that we created above the first time... registry = LocateRegistry.getRegistry(resources .getRMIRegistryPort()); registry.list(); } catch (Exception e) { registry = LocateRegistry.createRegistry(resources .getRMIRegistryPort()); } } else if (resources.getRMICreateRegistryStrategy().equals( QuartzSchedulerResources.CREATE_REGISTRY_ALWAYS)) { try { registry = LocateRegistry.createRegistry(resources .getRMIRegistryPort()); } catch (Exception e) { // Fall back to an existing one, instead of creating it, since // if // we're in a web-app being 'hot' re-depoloyed, then the JVM // still // has the registry that we created above the first time... registry = LocateRegistry.getRegistry(resources .getRMIRegistryPort()); } } else { registry = LocateRegistry.getRegistry(resources .getRMIRegistryHost(), resources.getRMIRegistryPort()); } String bindName = resources.getRMIBindName(); registry.rebind(bindName, exportable); boundRemotely = true; getLog().info("Scheduler bound to RMI registry under name '" + bindName + "'"); } /** *

* Un-bind the scheduler from an RMI registry. *

*/ private void unBind() throws RemoteException { String host = resources.getRMIRegistryHost(); // don't un-export if we're not configured to do so... if (host == null || host.length() == 0) { return; } Registry registry = LocateRegistry.getRegistry(resources .getRMIRegistryHost(), resources.getRMIRegistryPort()); String bindName = resources.getRMIBindName(); try { registry.unbind(bindName); UnicastRemoteObject.unexportObject(this, true); } catch (java.rmi.NotBoundException nbe) { } getLog().info("Scheduler un-bound from name '" + bindName + "' in RMI registry"); } /** *

* Returns the name of the QuartzScheduler. *

*/ public String getSchedulerName() { return resources.getName(); } /** *

* Returns the instance Id of the QuartzScheduler. *

*/ public String getSchedulerInstanceId() { return resources.getInstanceId(); } /** *

* Returns the name of the QuartzScheduler. *

*/ public ThreadGroup getSchedulerThreadGroup() { if (threadGroup == null) { threadGroup = new ThreadGroup("QuartzScheduler:" + getSchedulerName()); if (resources.getMakeSchedulerThreadDaemon()) { threadGroup.setDaemon(true); } } return threadGroup; } public void addNoGCObject(Object obj) { holdToPreventGC.add(obj); } public boolean removeNoGCObject(Object obj) { return holdToPreventGC.remove(obj); } /** *

* Returns the SchedulerContext of the Scheduler. *

*/ public SchedulerContext getSchedulerContext() throws SchedulerException { return context; } public boolean isSignalOnSchedulingChange() { return signalOnSchedulingChange; } public void setSignalOnSchedulingChange(boolean signalOnSchedulingChange) { this.signalOnSchedulingChange = signalOnSchedulingChange; } /////////////////////////////////////////////////////////////////////////// /// /// Scheduler State Management Methods /// /////////////////////////////////////////////////////////////////////////// /** *

* Starts the QuartzScheduler's threads that fire {@link org.quartz.Trigger}s. *

* *

* All {@link org.quartz.Trigger}s that have misfired will * be passed to the appropriate TriggerListener(s). *

*/ public void start() throws SchedulerException { if (shuttingDown|| closed) { throw new SchedulerException( "The Scheduler cannot be restarted after shutdown() has been called."); } if (initialStart == null) { initialStart = new Date(); this.resources.getJobStore().schedulerStarted(); startPlugins(); } schedThread.togglePause(false); getLog().info( "Scheduler " + resources.getUniqueIdentifier() + " started."); notifySchedulerListenersStarted(); } public void startDelayed(final int seconds) throws SchedulerException { if (shuttingDown || closed) { throw new SchedulerException( "The Scheduler cannot be restarted after shutdown() has been called."); } Thread t = new Thread(new Runnable() { public void run() { try { Thread.sleep(seconds * 1000L); } catch(InterruptedException ignore) {} try { start(); } catch(SchedulerException se) { getLog().error("Unable to start secheduler after startup delay.", se); } } }); t.start(); } /** *

* Temporarily halts the QuartzScheduler's firing of {@link org.quartz.Trigger}s. *

* *

* The scheduler is not destroyed, and can be re-started at any time. *

*/ public void standby() { schedThread.togglePause(true); getLog().info( "Scheduler " + resources.getUniqueIdentifier() + " paused."); notifySchedulerListenersInStandbyMode(); } /** *

* Reports whether the Scheduler is paused. *

*/ public boolean isInStandbyMode() { return schedThread.isPaused(); } public Date runningSince() { return initialStart; } public int numJobsExecuted() { return jobMgr.getNumJobsFired(); } public Class getJobStoreClass() { return resources.getJobStore().getClass(); } public boolean supportsPersistence() { return resources.getJobStore().supportsPersistence(); } public boolean isClustered() { return resources.getJobStore().isClustered(); } public Class getThreadPoolClass() { return resources.getThreadPool().getClass(); } public int getThreadPoolSize() { return resources.getThreadPool().getPoolSize(); } /** *

* Halts the QuartzScheduler's firing of {@link org.quartz.Trigger}s, * and cleans up all resources associated with the QuartzScheduler. * Equivalent to shutdown(false). *

* *

* The scheduler cannot be re-started. *

*/ public void shutdown() { shutdown(false); } /** *

* Halts the QuartzScheduler's firing of {@link org.quartz.Trigger}s, * and cleans up all resources associated with the QuartzScheduler. *

* *

* The scheduler cannot be re-started. *

* * @param waitForJobsToComplete * if true the scheduler will not allow this method * to return until all currently executing jobs have completed. */ public void shutdown(boolean waitForJobsToComplete) { if(shuttingDown || closed) { return; } shuttingDown = true; getLog().info( "Scheduler " + resources.getUniqueIdentifier() + " shutting down."); notifySchedulerListenersShuttingdown(); standby(); schedThread.halt(); if( (resources.isInterruptJobsOnShutdown() && !waitForJobsToComplete) || (resources.isInterruptJobsOnShutdownWithWait() && waitForJobsToComplete)) { List jobs = getCurrentlyExecutingJobs(); for(JobExecutionContext job: jobs) { if(job.getJobInstance() instanceof InterruptableJob) try { ((InterruptableJob)job.getJobInstance()).interrupt(); } catch (Throwable e) { // do nothing, this was just a courtesy effort getLog().warn("Encountered error when interrupting job {} during shutdown: {}", job.getJobDetail().getFullName(), e); } } } resources.getThreadPool().shutdown(waitForJobsToComplete); if (waitForJobsToComplete) { while (jobMgr.getNumJobsCurrentlyExecuting() > 0) { try { Thread.sleep(100); } catch (Exception ignore) { } } } // Scheduler thread may have be waiting for the fire time of an acquired // trigger and need time to release the trigger once halted, so make sure // the thread is dead before continuing to shutdown the job store. try { schedThread.join(); } catch (InterruptedException ignore) { } closed = true; if (resources.getJMXExport()) { try { unregisterJMX(); } catch (Exception e) { } } if(boundRemotely) { try { unBind(); } catch (RemoteException re) { } } shutdownPlugins(); resources.getJobStore().shutdown(); notifySchedulerListenersShutdown(); SchedulerRepository.getInstance().remove(resources.getName()); holdToPreventGC.clear(); if(updateTimer != null) updateTimer.cancel(); getLog().info( "Scheduler " + resources.getUniqueIdentifier() + " shutdown complete."); } /** *

* Reports whether the Scheduler has been shutdown. *

*/ public boolean isShutdown() { return closed; } public boolean isStarted() { return !shuttingDown && !closed && !isInStandbyMode() && initialStart != null; } public void validateState() throws SchedulerException { if (isShutdown()) { throw new SchedulerException("The Scheduler has been shutdown."); } // other conditions to check (?) } /** *

* Return a list of JobExecutionContext objects that * represent all currently executing Jobs in this Scheduler instance. *

* *

* This method is not cluster aware. That is, it will only return Jobs * currently executing in this Scheduler instance, not across the entire * cluster. *

* *

* Note that the list returned is an 'instantaneous' snap-shot, and that as * soon as it's returned, the true list of executing jobs may be different. *

*/ public List getCurrentlyExecutingJobs() { return jobMgr.getExecutingJobs(); } /////////////////////////////////////////////////////////////////////////// /// /// Scheduling-related Methods /// /////////////////////////////////////////////////////////////////////////// /** *

* Add the {@link org.quartz.Job} identified by the given * {@link org.quartz.JobDetail} to the Scheduler, and * associate the given {@link org.quartz.Trigger} with it. *

* *

* If the given Trigger does not reference any Job, then it * will be set to reference the Job passed with it into this method. *

* * @throws SchedulerException * if the Job or Trigger cannot be added to the Scheduler, or * there is an internal Scheduler error. */ public Date scheduleJob(SchedulingContext ctxt, JobDetail jobDetail, Trigger trigger) throws SchedulerException { validateState(); if (jobDetail == null) { throw new SchedulerException("JobDetail cannot be null", SchedulerException.ERR_CLIENT_ERROR); } if (trigger == null) { throw new SchedulerException("Trigger cannot be null", SchedulerException.ERR_CLIENT_ERROR); } jobDetail.validate(); if (trigger.getJobName() == null) { trigger.setJobName(jobDetail.getName()); trigger.setJobGroup(jobDetail.getGroup()); } else if (trigger.getJobName() != null && !trigger.getJobName().equals(jobDetail.getName())) { throw new SchedulerException( "Trigger does not reference given job!", SchedulerException.ERR_CLIENT_ERROR); } else if (trigger.getJobGroup() != null && !trigger.getJobGroup().equals(jobDetail.getGroup())) { throw new SchedulerException( "Trigger does not reference given job!", SchedulerException.ERR_CLIENT_ERROR); } trigger.validate(); Calendar cal = null; if (trigger.getCalendarName() != null) { cal = resources.getJobStore().retrieveCalendar(ctxt, trigger.getCalendarName()); } Date ft = trigger.computeFirstFireTime(cal); if (ft == null) { throw new SchedulerException( "Based on configured schedule, the given trigger will never fire.", SchedulerException.ERR_CLIENT_ERROR); } resources.getJobStore().storeJobAndTrigger(ctxt, jobDetail, trigger); notifySchedulerListenersJobAdded(jobDetail); notifySchedulerThread(trigger.getNextFireTime().getTime()); notifySchedulerListenersSchduled(trigger); return ft; } /** *

* Schedule the given {@link org.quartz.Trigger} with the * Job identified by the Trigger's settings. *

* * @throws SchedulerException * if the indicated Job does not exist, or the Trigger cannot be * added to the Scheduler, or there is an internal Scheduler * error. */ public Date scheduleJob(SchedulingContext ctxt, Trigger trigger) throws SchedulerException { validateState(); if (trigger == null) { throw new SchedulerException("Trigger cannot be null", SchedulerException.ERR_CLIENT_ERROR); } trigger.validate(); Calendar cal = null; if (trigger.getCalendarName() != null) { cal = resources.getJobStore().retrieveCalendar(ctxt, trigger.getCalendarName()); if(cal == null) { throw new SchedulerException( "Calendar not found: " + trigger.getCalendarName(), SchedulerException.ERR_PERSISTENCE_CALENDAR_DOES_NOT_EXIST); } } Date ft = trigger.computeFirstFireTime(cal); if (ft == null) { throw new SchedulerException( "Based on configured schedule, the given trigger will never fire.", SchedulerException.ERR_CLIENT_ERROR); } resources.getJobStore().storeTrigger(ctxt, trigger, false); notifySchedulerThread(trigger.getNextFireTime().getTime()); notifySchedulerListenersSchduled(trigger); return ft; } /** *

* Add the given Job to the Scheduler - with no associated * Trigger. The Job will be 'dormant' until * it is scheduled with a Trigger, or Scheduler.triggerJob() * is called for it. *

* *

* The Job must by definition be 'durable', if it is not, * SchedulerException will be thrown. *

* * @throws SchedulerException * if there is an internal Scheduler error, or if the Job is not * durable, or a Job with the same name already exists, and * replace is false. */ public void addJob(SchedulingContext ctxt, JobDetail jobDetail, boolean replace) throws SchedulerException { validateState(); if (!jobDetail.isDurable() && !replace) { throw new SchedulerException( "Jobs added with no trigger must be durable.", SchedulerException.ERR_CLIENT_ERROR); } resources.getJobStore().storeJob(ctxt, jobDetail, replace); notifySchedulerThread(0L); notifySchedulerListenersJobAdded(jobDetail); } /** *

* Delete the identified Job from the Scheduler - and any * associated Triggers. *

* * @return true if the Job was found and deleted. * @throws SchedulerException * if there is an internal Scheduler error. */ public boolean deleteJob(SchedulingContext ctxt, String jobName, String groupName) throws SchedulerException { validateState(); if (groupName == null) { groupName = Scheduler.DEFAULT_GROUP; } boolean result = false; Trigger[] triggers = getTriggersOfJob(ctxt, jobName, groupName); for (Trigger trigger : triggers) { if (!unscheduleJob(ctxt, trigger.getName(), trigger.getGroup())) { StringBuilder sb = new StringBuilder().append( "Unable to unschedule trigger [").append( trigger.getKey()).append("] while deleting job [") .append(groupName).append(".").append(jobName).append( "]"); throw new SchedulerException(sb.toString()); } result = true; } result = resources.getJobStore().removeJob(ctxt, jobName, groupName) || result; if (result) { notifySchedulerThread(0L); notifySchedulerListenersJobDeleted(jobName, groupName); } return result; } /** *

* Remove the indicated {@link org.quartz.Trigger} from the * scheduler. *

*/ public boolean unscheduleJob(SchedulingContext ctxt, String triggerName, String groupName) throws SchedulerException { validateState(); if(groupName == null) { groupName = Scheduler.DEFAULT_GROUP; } if (resources.getJobStore().removeTrigger(ctxt, triggerName, groupName)) { notifySchedulerThread(0L); notifySchedulerListenersUnscheduled(triggerName, groupName); } else { return false; } return true; } /** *

* Remove (delete) the {@link org.quartz.Trigger} with the * given name, and store the new given one - which must be associated * with the same job. *

* * @param triggerName * The name of the Trigger to be removed. * @param groupName * The group name of the Trigger to be removed. * @param newTrigger * The new Trigger to be stored. * @return null if a Trigger with the given * name & group was not found and removed from the store, otherwise * the first fire time of the newly scheduled trigger. */ public Date rescheduleJob(SchedulingContext ctxt, String triggerName, String groupName, Trigger newTrigger) throws SchedulerException { validateState(); if(groupName == null) { groupName = Scheduler.DEFAULT_GROUP; } newTrigger.validate(); Calendar cal = null; if (newTrigger.getCalendarName() != null) { cal = resources.getJobStore().retrieveCalendar(ctxt, newTrigger.getCalendarName()); } Date ft = newTrigger.computeFirstFireTime(cal); if (ft == null) { throw new SchedulerException( "Based on configured schedule, the given trigger will never fire.", SchedulerException.ERR_CLIENT_ERROR); } if (resources.getJobStore().replaceTrigger(ctxt, triggerName, groupName, newTrigger)) { notifySchedulerThread(newTrigger.getNextFireTime().getTime()); notifySchedulerListenersUnscheduled(triggerName, groupName); notifySchedulerListenersSchduled(newTrigger); } else { return null; } return ft; } private String newTriggerId() { long r = random.nextLong(); if (r < 0) { r = -r; } return "MT_" + Long.toString(r, 30 + (int) (System.currentTimeMillis() % 7)); } /** *

* Trigger the identified {@link org.quartz.Job} (execute it * now) - with a non-volatile trigger. *

*/ public void triggerJob(SchedulingContext ctxt, String jobName, String groupName, JobDataMap data) throws SchedulerException { validateState(); if(groupName == null) { groupName = Scheduler.DEFAULT_GROUP; } Trigger trig = new org.quartz.SimpleTrigger(newTriggerId(), Scheduler.DEFAULT_MANUAL_TRIGGERS, jobName, groupName, new Date(), null, 0, 0); trig.setVolatility(false); trig.computeFirstFireTime(null); if(data != null) { trig.setJobDataMap(data); } boolean collision = true; while (collision) { try { resources.getJobStore().storeTrigger(ctxt, trig, false); collision = false; } catch (ObjectAlreadyExistsException oaee) { trig.setName(newTriggerId()); } } notifySchedulerThread(trig.getNextFireTime().getTime()); notifySchedulerListenersSchduled(trig); } /** *

* Trigger the identified {@link org.quartz.Job} (execute it * now) - with a volatile trigger. *

*/ public void triggerJobWithVolatileTrigger(SchedulingContext ctxt, String jobName, String groupName, JobDataMap data) throws SchedulerException { validateState(); if(groupName == null) { groupName = Scheduler.DEFAULT_GROUP; } Trigger trig = new org.quartz.SimpleTrigger(newTriggerId(), Scheduler.DEFAULT_MANUAL_TRIGGERS, jobName, groupName, new Date(), null, 0, 0); trig.setVolatility(true); trig.computeFirstFireTime(null); if(data != null) { trig.setJobDataMap(data); } boolean collision = true; while (collision) { try { resources.getJobStore().storeTrigger(ctxt, trig, false); collision = false; } catch (ObjectAlreadyExistsException oaee) { trig.setName(newTriggerId()); } } notifySchedulerThread(trig.getNextFireTime().getTime()); notifySchedulerListenersSchduled(trig); } /** *

* Pause the {@link Trigger} with the given name. *

* */ public void pauseTrigger(SchedulingContext ctxt, String triggerName, String groupName) throws SchedulerException { validateState(); if(groupName == null) { groupName = Scheduler.DEFAULT_GROUP; } resources.getJobStore().pauseTrigger(ctxt, triggerName, groupName); notifySchedulerThread(0L); notifySchedulerListenersPausedTrigger(triggerName, groupName); } /** *

* Pause all of the {@link Trigger}s in the given group. *

* */ public void pauseTriggerGroup(SchedulingContext ctxt, String groupName) throws SchedulerException { validateState(); if(groupName == null) { groupName = Scheduler.DEFAULT_GROUP; } resources.getJobStore().pauseTriggerGroup(ctxt, groupName); notifySchedulerThread(0L); notifySchedulerListenersPausedTrigger(null, groupName); } /** *

* Pause the {@link org.quartz.JobDetail} with the given * name - by pausing all of its current Triggers. *

* */ public void pauseJob(SchedulingContext ctxt, String jobName, String groupName) throws SchedulerException { validateState(); if(groupName == null) { groupName = Scheduler.DEFAULT_GROUP; } resources.getJobStore().pauseJob(ctxt, jobName, groupName); notifySchedulerThread(0L); notifySchedulerListenersPausedJob(jobName, groupName); } /** *

* Pause all of the {@link org.quartz.JobDetail}s in the * given group - by pausing all of their Triggers. *

* */ public void pauseJobGroup(SchedulingContext ctxt, String groupName) throws SchedulerException { validateState(); if(groupName == null) { groupName = Scheduler.DEFAULT_GROUP; } resources.getJobStore().pauseJobGroup(ctxt, groupName); notifySchedulerThread(0L); notifySchedulerListenersPausedJob(null, groupName); } /** *

* Resume (un-pause) the {@link Trigger} with the given * name. *

* *

* If the Trigger missed one or more fire-times, then the * Trigger's misfire instruction will be applied. *

* */ public void resumeTrigger(SchedulingContext ctxt, String triggerName, String groupName) throws SchedulerException { validateState(); if(groupName == null) { groupName = Scheduler.DEFAULT_GROUP; } resources.getJobStore().resumeTrigger(ctxt, triggerName, groupName); notifySchedulerThread(0L); notifySchedulerListenersResumedTrigger(triggerName, groupName); } /** *

* Resume (un-pause) all of the {@link Trigger}s in the * given group. *

* *

* If any Trigger missed one or more fire-times, then the * Trigger's misfire instruction will be applied. *

* */ public void resumeTriggerGroup(SchedulingContext ctxt, String groupName) throws SchedulerException { validateState(); if(groupName == null) { groupName = Scheduler.DEFAULT_GROUP; } resources.getJobStore().resumeTriggerGroup(ctxt, groupName); notifySchedulerThread(0L); notifySchedulerListenersResumedTrigger(null, groupName); } public Set getPausedTriggerGroups(SchedulingContext ctxt) throws SchedulerException { return resources.getJobStore().getPausedTriggerGroups(ctxt); } /** *

* Resume (un-pause) the {@link org.quartz.JobDetail} with * the given name. *

* *

* If any of the Job'sTrigger s missed one * or more fire-times, then the Trigger's misfire * instruction will be applied. *

* */ public void resumeJob(SchedulingContext ctxt, String jobName, String groupName) throws SchedulerException { validateState(); if(groupName == null) { groupName = Scheduler.DEFAULT_GROUP; } resources.getJobStore().resumeJob(ctxt, jobName, groupName); notifySchedulerThread(0L); notifySchedulerListenersResumedJob(jobName, groupName); } /** *

* Resume (un-pause) all of the {@link org.quartz.JobDetail}s * in the given group. *

* *

* If any of the Job s had Trigger s that * missed one or more fire-times, then the Trigger's * misfire instruction will be applied. *

* */ public void resumeJobGroup(SchedulingContext ctxt, String groupName) throws SchedulerException { validateState(); if(groupName == null) { groupName = Scheduler.DEFAULT_GROUP; } resources.getJobStore().resumeJobGroup(ctxt, groupName); notifySchedulerThread(0L); notifySchedulerListenersResumedJob(null, groupName); } /** *

* Pause all triggers - equivalent of calling pauseTriggerGroup(group) * on every group. *

* *

* When resumeAll() is called (to un-pause), trigger misfire * instructions WILL be applied. *

* * @see #resumeAll(SchedulingContext) * @see #pauseTriggerGroup(SchedulingContext, String) * @see #standby() */ public void pauseAll(SchedulingContext ctxt) throws SchedulerException { validateState(); resources.getJobStore().pauseAll(ctxt); notifySchedulerThread(0L); notifySchedulerListenersPausedTrigger(null, null); } /** *

* Resume (un-pause) all triggers - equivalent of calling resumeTriggerGroup(group) * on every group. *

* *

* If any Trigger missed one or more fire-times, then the * Trigger's misfire instruction will be applied. *

* * @see #pauseAll(SchedulingContext) */ public void resumeAll(SchedulingContext ctxt) throws SchedulerException { validateState(); resources.getJobStore().resumeAll(ctxt); notifySchedulerThread(0L); notifySchedulerListenersResumedTrigger(null, null); } /** *

* Get the names of all known {@link org.quartz.Job} groups. *

*/ public String[] getJobGroupNames(SchedulingContext ctxt) throws SchedulerException { validateState(); return resources.getJobStore().getJobGroupNames(ctxt); } /** *

* Get the names of all the {@link org.quartz.Job}s in the * given group. *

*/ public String[] getJobNames(SchedulingContext ctxt, String groupName) throws SchedulerException { validateState(); if(groupName == null) { groupName = Scheduler.DEFAULT_GROUP; } return resources.getJobStore().getJobNames(ctxt, groupName); } /** *

* Get all {@link Trigger} s that are associated with the * identified {@link org.quartz.JobDetail}. *

*/ public Trigger[] getTriggersOfJob(SchedulingContext ctxt, String jobName, String groupName) throws SchedulerException { validateState(); if(groupName == null) { groupName = Scheduler.DEFAULT_GROUP; } return resources.getJobStore().getTriggersForJob(ctxt, jobName, groupName); } /** *

* Get the names of all known {@link org.quartz.Trigger} * groups. *

*/ public String[] getTriggerGroupNames(SchedulingContext ctxt) throws SchedulerException { validateState(); return resources.getJobStore().getTriggerGroupNames(ctxt); } /** *

* Get the names of all the {@link org.quartz.Trigger}s in * the given group. *

*/ public String[] getTriggerNames(SchedulingContext ctxt, String groupName) throws SchedulerException { validateState(); if(groupName == null) { groupName = Scheduler.DEFAULT_GROUP; } return resources.getJobStore().getTriggerNames(ctxt, groupName); } /** *

* Get the {@link JobDetail} for the Job * instance with the given name and group. *

*/ public JobDetail getJobDetail(SchedulingContext ctxt, String jobName, String jobGroup) throws SchedulerException { validateState(); if(jobGroup == null) { jobGroup = Scheduler.DEFAULT_GROUP; } return resources.getJobStore().retrieveJob(ctxt, jobName, jobGroup); } /** *

* Get the {@link Trigger} instance with the given name and * group. *

*/ public Trigger getTrigger(SchedulingContext ctxt, String triggerName, String triggerGroup) throws SchedulerException { validateState(); if(triggerGroup == null) { triggerGroup = Scheduler.DEFAULT_GROUP; } return resources.getJobStore().retrieveTrigger(ctxt, triggerName, triggerGroup); } /** *

* Get the current state of the identified {@link Trigger}. *

* * @see Trigger#STATE_NORMAL * @see Trigger#STATE_PAUSED * @see Trigger#STATE_COMPLETE * @see Trigger#STATE_ERROR */ public int getTriggerState(SchedulingContext ctxt, String triggerName, String triggerGroup) throws SchedulerException { validateState(); if(triggerGroup == null) { triggerGroup = Scheduler.DEFAULT_GROUP; } return resources.getJobStore().getTriggerState(ctxt, triggerName, triggerGroup); } /** *

* Add (register) the given Calendar to the Scheduler. *

* * @throws SchedulerException * if there is an internal Scheduler error, or a Calendar with * the same name already exists, and replace is * false. */ public void addCalendar(SchedulingContext ctxt, String calName, Calendar calendar, boolean replace, boolean updateTriggers) throws SchedulerException { validateState(); resources.getJobStore().storeCalendar(ctxt, calName, calendar, replace, updateTriggers); } /** *

* Delete the identified Calendar from the Scheduler. *

* * @return true if the Calendar was found and deleted. * @throws SchedulerException * if there is an internal Scheduler error. */ public boolean deleteCalendar(SchedulingContext ctxt, String calName) throws SchedulerException { validateState(); return resources.getJobStore().removeCalendar(ctxt, calName); } /** *

* Get the {@link Calendar} instance with the given name. *

*/ public Calendar getCalendar(SchedulingContext ctxt, String calName) throws SchedulerException { validateState(); return resources.getJobStore().retrieveCalendar(ctxt, calName); } /** *

* Get the names of all registered {@link Calendar}s. *

*/ public String[] getCalendarNames(SchedulingContext ctxt) throws SchedulerException { validateState(); return resources.getJobStore().getCalendarNames(ctxt); } /** *

* Add the given {@link org.quartz.JobListener} to the * Scheduler'sglobal list. *

* *

* Listeners in the 'global' list receive notification of execution events * for ALL {@link org.quartz.Job}s. *

*/ public void addGlobalJobListener(JobListener jobListener) { if (jobListener.getName() == null || jobListener.getName().length() == 0) { throw new IllegalArgumentException( "JobListener name cannot be empty."); } synchronized (globalJobListeners) { globalJobListeners.put(jobListener.getName(), jobListener); } } /** *

* Add the given {@link org.quartz.JobListener} to the * Scheduler's list, of registered JobListeners. */ public void addJobListener(JobListener jobListener) { if (jobListener.getName() == null || jobListener.getName().length() == 0) { throw new IllegalArgumentException( "JobListener name cannot be empty."); } synchronized (jobListeners) { jobListeners.put(jobListener.getName(), jobListener); } } /** *

* Remove the identified {@link JobListener} from the Scheduler's * list of global listeners. *

* * @return true if the identified listener was found in the list, and * removed. */ public boolean removeGlobalJobListener(String name) { synchronized (globalJobListeners) { return (globalJobListeners.remove(name) != null); } } /** *

* Remove the identified {@link org.quartz.JobListener} from * the Scheduler's list of registered listeners. *

* * @return true if the identifed listener was found in the list, and * removed. */ public boolean removeJobListener(String name) { synchronized (jobListeners) { return (jobListeners.remove(name) != null); } } /** *

* Get a List containing all of the {@link org.quartz.JobListener} * s in the Scheduler'sglobal list. *

*/ public List getGlobalJobListeners() { synchronized (globalJobListeners) { return new LinkedList(globalJobListeners.values()); } } /** *

* Get a Set containing the names of all the non-global{@link org.quartz.JobListener} * s registered with the Scheduler. *

*/ public Set getJobListenerNames() { synchronized (jobListeners) { return new HashSet(jobListeners.keySet()); } } /** *

* Get the global{@link org.quartz.JobListener} * that has the given name. *

*/ public JobListener getGlobalJobListener(String name) { synchronized (globalJobListeners) { return (JobListener)globalJobListeners.get(name); } } /** *

* Get the non-global{@link org.quartz.JobListener} * that has the given name. *

*/ public JobListener getJobListener(String name) { synchronized (jobListeners) { return (JobListener) jobListeners.get(name); } } /** *

* Add the given {@link org.quartz.TriggerListener} to the * Scheduler'sglobal list. *

* *

* Listeners in the 'global' list receive notification of execution events * for ALL {@link org.quartz.Trigger}s. *

*/ public void addGlobalTriggerListener(TriggerListener triggerListener) { if (triggerListener.getName() == null || triggerListener.getName().length() == 0) { throw new IllegalArgumentException( "TriggerListener name cannot be empty."); } synchronized (globalTriggerListeners) { globalTriggerListeners.put(triggerListener.getName(), triggerListener); } } /** *

* Add the given {@link org.quartz.TriggerListener} to the * Scheduler's list, of registered TriggerListeners. */ public void addTriggerListener(TriggerListener triggerListener) { if (triggerListener.getName() == null || triggerListener.getName().length() == 0) { throw new IllegalArgumentException( "TriggerListener name cannot be empty."); } synchronized (triggerListeners) { triggerListeners.put(triggerListener.getName(), triggerListener); } } /** *

* Remove the identifed {@link TriggerListener} from the Scheduler's * list of global listeners. *

* * @return true if the identifed listener was found in the list, and * removed. */ public boolean removeGlobalTriggerListener(String name) { synchronized (globalTriggerListeners) { return (globalTriggerListeners.remove(name) != null); } } /** *

* Remove the identified {@link org.quartz.TriggerListener} * from the Scheduler's list of registered listeners. *

* * @return true if the identified listener was found in the list, and * removed. */ public boolean removeTriggerListener(String name) { synchronized (triggerListeners) { return (triggerListeners.remove(name) != null); } } /** *

* Get a list containing all of the {@link org.quartz.TriggerListener} * s in the Scheduler'sglobal list. *

*/ public List getGlobalTriggerListeners() { synchronized (globalTriggerListeners) { return new LinkedList(globalTriggerListeners.values()); } } /** *

* Get a Set containing the names of all the non-global{@link org.quartz.TriggerListener} * s registered with the Scheduler. *

*/ public Set getTriggerListenerNames() { synchronized (triggerListeners) { return new HashSet(triggerListeners.keySet()); } } /** *

* Get the global{@link TriggerListener} that * has the given name. *

*/ public TriggerListener getGlobalTriggerListener(String name) { synchronized (globalTriggerListeners) { return (TriggerListener)globalTriggerListeners.get(name); } } /** *

* Get the non-global{@link org.quartz.TriggerListener} * that has the given name. *

*/ public TriggerListener getTriggerListener(String name) { synchronized (triggerListeners) { return (TriggerListener) triggerListeners.get(name); } } /** *

* Register the given {@link SchedulerListener} with the * Scheduler. *

*/ public void addSchedulerListener(SchedulerListener schedulerListener) { synchronized (schedulerListeners) { schedulerListeners.add(schedulerListener); } } /** *

* Remove the given {@link SchedulerListener} from the * Scheduler. *

* * @return true if the identified listener was found in the list, and * removed. */ public boolean removeSchedulerListener(SchedulerListener schedulerListener) { synchronized (schedulerListeners) { return schedulerListeners.remove(schedulerListener); } } /** *

* Get a List containing all of the {@link SchedulerListener} * s registered with the Scheduler. *

*/ public List getSchedulerListeners() { synchronized (schedulerListeners) { return (List)schedulerListeners.clone(); } } protected void notifyJobStoreJobComplete(SchedulingContext ctxt, Trigger trigger, JobDetail detail, int instCode) throws JobPersistenceException { resources.getJobStore().triggeredJobComplete(ctxt, trigger, detail, instCode); } protected void notifyJobStoreJobVetoed(SchedulingContext ctxt, Trigger trigger, JobDetail detail, int instCode) throws JobPersistenceException { resources.getJobStore().triggeredJobComplete(ctxt, trigger, detail, instCode); } protected void notifySchedulerThread(long candidateNewNextFireTime) { if (isSignalOnSchedulingChange()) { signaler.signalSchedulingChange(candidateNewNextFireTime); } } private List buildTriggerListenerList(String[] additionalLstnrs) throws SchedulerException { List triggerListeners = getGlobalTriggerListeners(); for (int i = 0; i < additionalLstnrs.length; i++) { TriggerListener tl = getTriggerListener(additionalLstnrs[i]); if (tl != null) { triggerListeners.add(tl); } else { throw new SchedulerException("TriggerListener '" + additionalLstnrs[i] + "' not found.", SchedulerException.ERR_TRIGGER_LISTENER_NOT_FOUND); } } return triggerListeners; } private List buildJobListenerList(String[] additionalLstnrs) throws SchedulerException { List jobListeners = getGlobalJobListeners(); for (int i = 0; i < additionalLstnrs.length; i++) { JobListener jl = getJobListener(additionalLstnrs[i]); if (jl != null) { jobListeners.add(jl); } else { throw new SchedulerException("JobListener '" + additionalLstnrs[i] + "' not found.", SchedulerException.ERR_JOB_LISTENER_NOT_FOUND); } } return jobListeners; } public boolean notifyTriggerListenersFired(JobExecutionContext jec) throws SchedulerException { // build a list of all trigger listeners that are to be notified... List triggerListeners = buildTriggerListenerList(jec.getTrigger() .getTriggerListenerNames()); boolean vetoedExecution = false; // notify all trigger listeners in the list java.util.Iterator itr = triggerListeners.iterator(); while (itr.hasNext()) { TriggerListener tl = (TriggerListener) itr.next(); try { tl.triggerFired(jec.getTrigger(), jec); if(tl.vetoJobExecution(jec.getTrigger(), jec)) { vetoedExecution = true; } } catch (Exception e) { SchedulerException se = new SchedulerException( "TriggerListener '" + tl.getName() + "' threw exception: " + e.getMessage(), e); se.setErrorCode(SchedulerException.ERR_TRIGGER_LISTENER); throw se; } } return vetoedExecution; } public void notifyTriggerListenersMisfired(Trigger trigger) throws SchedulerException { // build a list of all trigger listeners that are to be notified... List triggerListeners = buildTriggerListenerList(trigger .getTriggerListenerNames()); // notify all trigger listeners in the list java.util.Iterator itr = triggerListeners.iterator(); while (itr.hasNext()) { TriggerListener tl = (TriggerListener) itr.next(); try { tl.triggerMisfired(trigger); } catch (Exception e) { SchedulerException se = new SchedulerException( "TriggerListener '" + tl.getName() + "' threw exception: " + e.getMessage(), e); se.setErrorCode(SchedulerException.ERR_TRIGGER_LISTENER); throw se; } } } public void notifyTriggerListenersComplete(JobExecutionContext jec, int instCode) throws SchedulerException { // build a list of all trigger listeners that are to be notified... List triggerListeners = buildTriggerListenerList(jec.getTrigger() .getTriggerListenerNames()); // notify all trigger listeners in the list java.util.Iterator itr = triggerListeners.iterator(); while (itr.hasNext()) { TriggerListener tl = (TriggerListener) itr.next(); try { tl.triggerComplete(jec.getTrigger(), jec, instCode); } catch (Exception e) { SchedulerException se = new SchedulerException( "TriggerListener '" + tl.getName() + "' threw exception: " + e.getMessage(), e); se.setErrorCode(SchedulerException.ERR_TRIGGER_LISTENER); throw se; } } } public void notifyJobListenersToBeExecuted(JobExecutionContext jec) throws SchedulerException { // build a list of all job listeners that are to be notified... List jobListeners = buildJobListenerList(jec.getJobDetail() .getJobListenerNames()); // notify all job listeners java.util.Iterator itr = jobListeners.iterator(); while (itr.hasNext()) { JobListener jl = (JobListener) itr.next(); try { jl.jobToBeExecuted(jec); } catch (Exception e) { SchedulerException se = new SchedulerException( "JobListener '" + jl.getName() + "' threw exception: " + e.getMessage(), e); se.setErrorCode(SchedulerException.ERR_JOB_LISTENER); throw se; } } } public void notifyJobListenersWasVetoed(JobExecutionContext jec) throws SchedulerException { // build a list of all job listeners that are to be notified... List jobListeners = buildJobListenerList(jec.getJobDetail() .getJobListenerNames()); // notify all job listeners java.util.Iterator itr = jobListeners.iterator(); while (itr.hasNext()) { JobListener jl = (JobListener) itr.next(); try { jl.jobExecutionVetoed(jec); } catch (Exception e) { SchedulerException se = new SchedulerException( "JobListener '" + jl.getName() + "' threw exception: " + e.getMessage(), e); se.setErrorCode(SchedulerException.ERR_JOB_LISTENER); throw se; } } } public void notifyJobListenersWasExecuted(JobExecutionContext jec, JobExecutionException je) throws SchedulerException { // build a list of all job listeners that are to be notified... List jobListeners = buildJobListenerList(jec.getJobDetail() .getJobListenerNames()); // notify all job listeners java.util.Iterator itr = jobListeners.iterator(); while (itr.hasNext()) { JobListener jl = (JobListener) itr.next(); try { jl.jobWasExecuted(jec, je); } catch (Exception e) { SchedulerException se = new SchedulerException( "JobListener '" + jl.getName() + "' threw exception: " + e.getMessage(), e); se.setErrorCode(SchedulerException.ERR_JOB_LISTENER); throw se; } } } public void notifySchedulerListenersError(String msg, SchedulerException se) { // build a list of all scheduler listeners that are to be notified... List schedListeners = getSchedulerListeners(); // notify all scheduler listeners java.util.Iterator itr = schedListeners.iterator(); while (itr.hasNext()) { SchedulerListener sl = (SchedulerListener) itr.next(); try { sl.schedulerError(msg, se); } catch (Exception e) { getLog() .error( "Error while notifying SchedulerListener of error: ", e); getLog().error( " Original error (for notification) was: " + msg, se); } } } public void notifySchedulerListenersSchduled(Trigger trigger) { // build a list of all scheduler listeners that are to be notified... List schedListeners = getSchedulerListeners(); // notify all scheduler listeners java.util.Iterator itr = schedListeners.iterator(); while (itr.hasNext()) { SchedulerListener sl = (SchedulerListener) itr.next(); try { sl.jobScheduled(trigger); } catch (Exception e) { getLog().error( "Error while notifying SchedulerListener of scheduled job." + " Triger=" + trigger.getFullName(), e); } } } public void notifySchedulerListenersUnscheduled(String triggerName, String triggerGroup) { // build a list of all scheduler listeners that are to be notified... List schedListeners = getSchedulerListeners(); // notify all scheduler listeners java.util.Iterator itr = schedListeners.iterator(); while (itr.hasNext()) { SchedulerListener sl = (SchedulerListener) itr.next(); try { sl.jobUnscheduled(triggerName, triggerGroup); } catch (Exception e) { getLog().error( "Error while notifying SchedulerListener of unscheduled job." + " Triger=" + triggerGroup + "." + triggerName, e); } } } public void notifySchedulerListenersFinalized(Trigger trigger) { // build a list of all scheduler listeners that are to be notified... List schedListeners = getSchedulerListeners(); // notify all scheduler listeners java.util.Iterator itr = schedListeners.iterator(); while (itr.hasNext()) { SchedulerListener sl = (SchedulerListener) itr.next(); try { sl.triggerFinalized(trigger); } catch (Exception e) { getLog().error( "Error while notifying SchedulerListener of finalized trigger." + " Triger=" + trigger.getFullName(), e); } } } public void notifySchedulerListenersPausedTrigger(String name, String group) { // build a list of all job listeners that are to be notified... List schedListeners = getSchedulerListeners(); // notify all scheduler listeners java.util.Iterator itr = schedListeners.iterator(); while (itr.hasNext()) { SchedulerListener sl = (SchedulerListener) itr.next(); try { sl.triggersPaused(name, group); } catch (Exception e) { getLog().error( "Error while notifying SchedulerListener of paused trigger/group." + " Triger=" + group + "." + name, e); } } } public void notifySchedulerListenersResumedTrigger(String name, String group) { // build a list of all job listeners that are to be notified... List schedListeners = getSchedulerListeners(); // notify all scheduler listeners java.util.Iterator itr = schedListeners.iterator(); while (itr.hasNext()) { SchedulerListener sl = (SchedulerListener) itr.next(); try { sl.triggersResumed(name, group); } catch (Exception e) { getLog().error( "Error while notifying SchedulerListener of resumed trigger/group." + " Triger=" + group + "." + name, e); } } } public void notifySchedulerListenersPausedJob(String name, String group) { // build a list of all job listeners that are to be notified... List schedListeners = getSchedulerListeners(); // notify all scheduler listeners java.util.Iterator itr = schedListeners.iterator(); while (itr.hasNext()) { SchedulerListener sl = (SchedulerListener) itr.next(); try { sl.jobsPaused(name, group); } catch (Exception e) { getLog().error( "Error while notifying SchedulerListener of paused job/group." + " Job=" + group + "." + name, e); } } } public void notifySchedulerListenersResumedJob(String name, String group) { // build a list of all job listeners that are to be notified... List schedListeners = getSchedulerListeners(); // notify all scheduler listeners java.util.Iterator itr = schedListeners.iterator(); while (itr.hasNext()) { SchedulerListener sl = (SchedulerListener) itr.next(); try { sl.jobsResumed(name, group); } catch (Exception e) { getLog().error( "Error while notifying SchedulerListener of resumed job/group." + " Job=" + group + "." + name, e); } } } public void notifySchedulerListenersInStandbyMode() { // build a list of all job listeners that are to be notified... List schedListeners = getSchedulerListeners(); // notify all scheduler listeners java.util.Iterator itr = schedListeners.iterator(); while (itr.hasNext()) { SchedulerListener sl = (SchedulerListener) itr.next(); try { sl.schedulerInStandbyMode(); } catch (Exception e) { getLog().error( "Error while notifying SchedulerListener of inStandByMode.", e); } } } public void notifySchedulerListenersStarted() { // build a list of all job listeners that are to be notified... List schedListeners = getSchedulerListeners(); // notify all scheduler listeners java.util.Iterator itr = schedListeners.iterator(); while (itr.hasNext()) { SchedulerListener sl = (SchedulerListener) itr.next(); try { sl.schedulerStarted(); } catch (Exception e) { getLog().error( "Error while notifying SchedulerListener of startup.", e); } } } public void notifySchedulerListenersShutdown() { // build a list of all job listeners that are to be notified... List schedListeners = getSchedulerListeners(); // notify all scheduler listeners java.util.Iterator itr = schedListeners.iterator(); while (itr.hasNext()) { SchedulerListener sl = (SchedulerListener) itr.next(); try { sl.schedulerShutdown(); } catch (Exception e) { getLog().error( "Error while notifying SchedulerListener of shutdown.", e); } } } public void notifySchedulerListenersShuttingdown() { // build a list of all job listeners that are to be notified... List schedListeners = getSchedulerListeners(); // notify all scheduler listeners java.util.Iterator itr = schedListeners.iterator(); while (itr.hasNext()) { SchedulerListener sl = (SchedulerListener) itr.next(); try { sl.schedulerShuttingdown(); } catch (Exception e) { getLog().error( "Error while notifying SchedulerListener of shutdown.", e); } } } public void notifySchedulerListenersJobAdded(JobDetail jobDetail) { // build a list of all job listeners that are to be notified... List schedListeners = getSchedulerListeners(); // notify all scheduler listeners java.util.Iterator itr = schedListeners.iterator(); while (itr.hasNext()) { SchedulerListener sl = (SchedulerListener) itr.next(); try { sl.jobAdded(jobDetail); } catch (Exception e) { getLog().error( "Error while notifying SchedulerListener of JobAdded.", e); } } } public void notifySchedulerListenersJobDeleted(String jobName, String groupName) { // build a list of all job listeners that are to be notified... List schedListeners = getSchedulerListeners(); // notify all scheduler listeners java.util.Iterator itr = schedListeners.iterator(); while (itr.hasNext()) { SchedulerListener sl = (SchedulerListener) itr.next(); try { sl.jobDeleted(jobName, groupName); } catch (Exception e) { getLog().error( "Error while notifying SchedulerListener of JobAdded.", e); } } } public void setJobFactory(JobFactory factory) throws SchedulerException { if(factory == null) { throw new IllegalArgumentException("JobFactory cannot be set to null!"); } getLog().info("JobFactory set to: " + factory); this.jobFactory = factory; } public JobFactory getJobFactory() { return jobFactory; } /** * Interrupt all instances of the identified InterruptableJob executing in * this Scheduler instance. * *

* This method is not cluster aware. That is, it will only interrupt * instances of the identified InterruptableJob currently executing in this * Scheduler instance, not across the entire cluster. *

* * @see org.quartz.core.RemotableQuartzScheduler#interrupt(org.quartz.core.SchedulingContext, java.lang.String, java.lang.String) */ public boolean interrupt(SchedulingContext ctxt, String jobName, String groupName) throws UnableToInterruptJobException { if(groupName == null) { groupName = Scheduler.DEFAULT_GROUP; } List jobs = getCurrentlyExecutingJobs(); java.util.Iterator it = jobs.iterator(); JobExecutionContext jec = null; JobDetail jobDetail = null; Job job = null; boolean interrupted = false; while (it.hasNext()) { jec = (JobExecutionContext)it.next(); jobDetail = jec.getJobDetail(); if (jobName.equals(jobDetail.getName()) && groupName.equals(jobDetail.getGroup())){ job = jec.getJobInstance(); if (job instanceof InterruptableJob) { ((InterruptableJob)job).interrupt(); interrupted = true; } else { throw new UnableToInterruptJobException( "Job '" + jobName + "' of group '" + groupName + "' can not be interrupted, since it does not implement " + InterruptableJob.class.getName()); } } } return interrupted; } private void shutdownPlugins() { java.util.Iterator itr = resources.getSchedulerPlugins().iterator(); while (itr.hasNext()) { SchedulerPlugin plugin = (SchedulerPlugin) itr.next(); plugin.shutdown(); } } private void startPlugins() { java.util.Iterator itr = resources.getSchedulerPlugins().iterator(); while (itr.hasNext()) { SchedulerPlugin plugin = (SchedulerPlugin) itr.next(); plugin.start(); } } } ///////////////////////////////////////////////////////////////////////////// // // ErrorLogger - Scheduler Listener Class // ///////////////////////////////////////////////////////////////////////////// class ErrorLogger extends SchedulerListenerSupport { ErrorLogger() { } public void schedulerError(String msg, SchedulerException cause) { getLog().error(msg, cause); } } ///////////////////////////////////////////////////////////////////////////// // // ExecutingJobsManager - Job Listener Class // ///////////////////////////////////////////////////////////////////////////// class ExecutingJobsManager implements JobListener { HashMap executingJobs = new HashMap(); int numJobsFired = 0; ExecutingJobsManager() { } public String getName() { return getClass().getName(); } public int getNumJobsCurrentlyExecuting() { synchronized (executingJobs) { return executingJobs.size(); } } public void jobToBeExecuted(JobExecutionContext context) { numJobsFired++; synchronized (executingJobs) { executingJobs .put(context.getTrigger().getFireInstanceId(), context); } } public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) { synchronized (executingJobs) { executingJobs.remove(context.getTrigger().getFireInstanceId()); } } public int getNumJobsFired() { return numJobsFired; } public List getExecutingJobs() { synchronized (executingJobs) { return java.util.Collections.unmodifiableList(new ArrayList( executingJobs.values())); } } public void jobExecutionVetoed(JobExecutionContext context) { } }