Save This Page
Home » quartz-1.6.0 » org » quartz » core » [javadoc | source]
    1   
    2   /* 
    3    * Copyright 2004-2005 OpenSymphony 
    4    * 
    5    * Licensed under the Apache License, Version 2.0 (the "License"); you may not 
    6    * use this file except in compliance with the License. You may obtain a copy 
    7    * of the License at 
    8    * 
    9    *   http://www.apache.org/licenses/LICENSE-2.0 
   10    *   
   11    * Unless required by applicable law or agreed to in writing, software 
   12    * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 
   13    * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 
   14    * License for the specific language governing permissions and limitations 
   15    * under the License.
   16    * 
   17    */
   18   
   19   /*
   20    * Previously Copyright (c) 2001-2004 James House
   21    */
   22   package org.quartz.core;
   23   
   24   import java.io.IOException;
   25   import java.io.InputStream;
   26   import java.rmi.RemoteException;
   27   import java.rmi.registry.LocateRegistry;
   28   import java.rmi.registry.Registry;
   29   import java.rmi.server.UnicastRemoteObject;
   30   import java.util.ArrayList;
   31   import java.util.Date;
   32   import java.util.HashMap;
   33   import java.util.HashSet;
   34   import java.util.LinkedList;
   35   import java.util.List;
   36   import java.util.Properties;
   37   import java.util.Random;
   38   import java.util.Set;
   39   
   40   import org.apache.commons.logging.Log;
   41   import org.apache.commons.logging.LogFactory;
   42   import org.quartz.Calendar;
   43   import org.quartz.InterruptableJob;
   44   import org.quartz.Job;
   45   import org.quartz.JobDataMap;
   46   import org.quartz.JobDetail;
   47   import org.quartz.JobExecutionContext;
   48   import org.quartz.JobExecutionException;
   49   import org.quartz.JobListener;
   50   import org.quartz.JobPersistenceException;
   51   import org.quartz.ObjectAlreadyExistsException;
   52   import org.quartz.Scheduler;
   53   import org.quartz.SchedulerContext;
   54   import org.quartz.SchedulerException;
   55   import org.quartz.SchedulerListener;
   56   import org.quartz.listeners.SchedulerListenerSupport;
   57   import org.quartz.Trigger;
   58   import org.quartz.TriggerListener;
   59   import org.quartz.UnableToInterruptJobException;
   60   import org.quartz.impl.SchedulerRepository;
   61   import org.quartz.simpl.SimpleJobFactory;
   62   import org.quartz.spi.JobFactory;
   63   import org.quartz.spi.SchedulerPlugin;
   64   import org.quartz.spi.SchedulerSignaler;
   65   
   66   /**
   67    * <p>
   68    * This is the heart of Quartz, an indirect implementation of the <code>{@link org.quartz.Scheduler}</code>
   69    * interface, containing methods to schedule <code>{@link org.quartz.Job}</code>s,
   70    * register <code>{@link org.quartz.JobListener}</code> instances, etc.
   71    * </p>// TODO: more docs...
   72    * 
   73    * @see org.quartz.Scheduler
   74    * @see org.quartz.core.QuartzSchedulerThread
   75    * @see org.quartz.spi.JobStore
   76    * @see org.quartz.spi.ThreadPool
   77    * 
   78    * @author James House
   79    */
   80   public class QuartzScheduler implements RemotableQuartzScheduler {
   81   
   82       /*
   83        * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   84        * 
   85        * Constants.
   86        * 
   87        * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   88        */
   89   
   90       private static String VERSION_MAJOR = "UNKNOWN";
   91       private static String VERSION_MINOR = "UNKNOWN";
   92       private static String VERSION_ITERATION = "UNKNOWN";
   93   
   94       static {
   95           Properties props = new Properties();
   96           try {
   97               InputStream is = 
   98                   QuartzScheduler.class.getResourceAsStream("/build.properties");
   99               if(is != null) {
  100                   props.load(is);
  101                   VERSION_MAJOR = props.getProperty("version.major");
  102                   VERSION_MINOR = props.getProperty("version.minor");
  103                   VERSION_ITERATION = props.getProperty("version.iter");
  104               }
  105           } catch (IOException e) {
  106               (LogFactory.getLog(QuartzScheduler.class)).error(
  107                   "Error loading version info from build.properties.", e);
  108           }
  109       }
  110       
  111   
  112       /*
  113        * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  114        * 
  115        * Data members.
  116        * 
  117        * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  118        */
  119   
  120       private QuartzSchedulerResources resources;
  121   
  122       private QuartzSchedulerThread schedThread;
  123   
  124       private ThreadGroup threadGroup;
  125   
  126       private SchedulerContext context = new SchedulerContext();
  127   
  128       private HashMap jobListeners = new HashMap(10);
  129   
  130       private HashMap globalJobListeners = new HashMap(10);
  131   
  132       private HashMap triggerListeners = new HashMap(10);
  133   
  134       private HashMap globalTriggerListeners = new HashMap(10);
  135   
  136       private ArrayList schedulerListeners = new ArrayList(10);
  137   
  138       private JobFactory jobFactory = new SimpleJobFactory();
  139       
  140       ExecutingJobsManager jobMgr = null;
  141   
  142       ErrorLogger errLogger = null;
  143   
  144       private SchedulerSignaler signaler;
  145   
  146       private Random random = new Random();
  147   
  148       private ArrayList holdToPreventGC = new ArrayList(5);
  149   
  150       private boolean signalOnSchedulingChange = true;
  151   
  152       private boolean closed = false;
  153   
  154       private Date initialStart = null;
  155   
  156       private final Log log = LogFactory.getLog(getClass());
  157   
  158       /*
  159        * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  160        * 
  161        * Constructors.
  162        * 
  163        * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  164        */
  165   
  166       /**
  167        * <p>
  168        * Create a <code>QuartzScheduler</code> with the given configuration
  169        * properties.
  170        * </p>
  171        * 
  172        * @see QuartzSchedulerResources
  173        */
  174       public QuartzScheduler(QuartzSchedulerResources resources,
  175               SchedulingContext ctxt, long idleWaitTime, long dbRetryInterval)
  176           throws SchedulerException {
  177           this.resources = resources;
  178           try {
  179               bind();
  180           } catch (Exception re) {
  181               throw new SchedulerException(
  182                       "Unable to bind scheduler to RMI Registry.", re);
  183           }
  184   
  185           if (resources.getJMXExport()) {
  186               try {
  187                   registerJMX();
  188               } catch (Exception e) {
  189                   throw new SchedulerException(
  190                           "Unable to register scheduler with MBeanServer.", e);
  191               }
  192           }
  193           
  194           this.schedThread = new QuartzSchedulerThread(this, resources, ctxt);
  195           if (idleWaitTime > 0) {
  196               this.schedThread.setIdleWaitTime(idleWaitTime);
  197           }
  198           if (dbRetryInterval > 0) {
  199               this.schedThread.setDbFailureRetryInterval(dbRetryInterval);
  200           }
  201   
  202           jobMgr = new ExecutingJobsManager();
  203           addGlobalJobListener(jobMgr);
  204           errLogger = new ErrorLogger();
  205           addSchedulerListener(errLogger);
  206   
  207           signaler = new SchedulerSignalerImpl(this);
  208           
  209           getLog().info("Quartz Scheduler v." + getVersion() + " created.");
  210       }
  211   
  212       /*
  213        * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  214        * 
  215        * Interface.
  216        * 
  217        * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  218        */
  219   
  220       public String getVersion() {
  221           return getVersionMajor() + "." + getVersionMinor() + "."
  222                   + getVersionIteration();
  223       }
  224   
  225       public static String getVersionMajor() {
  226           return VERSION_MAJOR;
  227       }
  228   
  229       public static String getVersionMinor() {
  230           return VERSION_MINOR;
  231       }
  232   
  233       public static String getVersionIteration() {
  234           return VERSION_ITERATION;
  235       }
  236   
  237       public SchedulerSignaler getSchedulerSignaler() {
  238           return signaler;
  239       }
  240   
  241       public Log getLog() {
  242           return log;
  243       }
  244   
  245       /**
  246        * Register the scheduler in the local MBeanServer.
  247        */
  248       private void registerJMX() throws Exception {
  249           org.apache.commons.modeler.Registry registry = 
  250               org.apache.commons.modeler.Registry.getRegistry(null, null);
  251           
  252           String jmxObjectName = resources.getJMXObjectName();
  253           
  254           registry.registerComponent(this, jmxObjectName, null);
  255           
  256           getLog().info("Scheduler registered with local MBeanServer under name '" + jmxObjectName + "'");
  257       }
  258   
  259       /**
  260        * Unregister the scheduler from the local MBeanServer.
  261        */
  262       private void unregisterJMX() throws Exception {
  263           org.apache.commons.modeler.Registry registry = 
  264               org.apache.commons.modeler.Registry.getRegistry(null, null);
  265           
  266           String jmxObjectName = resources.getJMXObjectName();
  267           
  268           registry.unregisterComponent(jmxObjectName);
  269           
  270           getLog().info("Scheduler unregistered from name '" + jmxObjectName + "' in the local MBeanServer.");
  271       }
  272   
  273       /**
  274        * <p>
  275        * Bind the scheduler to an RMI registry.
  276        * </p>
  277        */
  278       private void bind() throws RemoteException {
  279           String host = resources.getRMIRegistryHost();
  280           // don't export if we're not configured to do so...
  281           if (host == null || host.length() == 0) {
  282               return;
  283           }
  284   
  285           RemotableQuartzScheduler exportable = null;
  286   
  287           if(resources.getRMIServerPort() > 0) {
  288               exportable = (RemotableQuartzScheduler) UnicastRemoteObject
  289                   .exportObject(this, resources.getRMIServerPort());
  290           } else {
  291               exportable = (RemotableQuartzScheduler) UnicastRemoteObject
  292                   .exportObject(this);
  293           }
  294   
  295           Registry registry = null;
  296   
  297           if (resources.getRMICreateRegistryStrategy().equals(
  298                   QuartzSchedulerResources.CREATE_REGISTRY_AS_NEEDED)) {
  299               try {
  300                   // First try to get an existing one, instead of creating it,
  301                   // since if
  302                   // we're in a web-app being 'hot' re-depoloyed, then the JVM
  303                   // still
  304                   // has the registry that we created above the first time...
  305                   registry = LocateRegistry.getRegistry(resources
  306                           .getRMIRegistryPort());
  307                   registry.list();
  308               } catch (Exception e) {
  309                   registry = LocateRegistry.createRegistry(resources
  310                           .getRMIRegistryPort());
  311               }
  312           } else if (resources.getRMICreateRegistryStrategy().equals(
  313                   QuartzSchedulerResources.CREATE_REGISTRY_ALWAYS)) {
  314               try {
  315                   registry = LocateRegistry.createRegistry(resources
  316                           .getRMIRegistryPort());
  317               } catch (Exception e) {
  318                   // Fall back to an existing one, instead of creating it, since
  319                   // if
  320                   // we're in a web-app being 'hot' re-depoloyed, then the JVM
  321                   // still
  322                   // has the registry that we created above the first time...
  323                   registry = LocateRegistry.getRegistry(resources
  324                           .getRMIRegistryPort());
  325               }
  326           } else {
  327               registry = LocateRegistry.getRegistry(resources
  328                       .getRMIRegistryHost(), resources.getRMIRegistryPort());
  329           }
  330   
  331           String bindName = resources.getRMIBindName();
  332           
  333           registry.rebind(bindName, exportable);
  334   
  335           getLog().info("Scheduler bound to RMI registry under name '" + bindName + "'");
  336       }
  337   
  338       /**
  339        * <p>
  340        * Un-bind the scheduler from an RMI registry.
  341        * </p>
  342        */
  343       private void unBind() throws RemoteException {
  344           String host = resources.getRMIRegistryHost();
  345           // don't un-export if we're not configured to do so...
  346           if (host == null || host.length() == 0) {
  347               return;
  348           }
  349   
  350           Registry registry = LocateRegistry.getRegistry(resources
  351                   .getRMIRegistryHost(), resources.getRMIRegistryPort());
  352   
  353           String bindName = resources.getRMIBindName();
  354           
  355           try {
  356               registry.unbind(bindName);
  357               UnicastRemoteObject.unexportObject(this, true);
  358           } catch (java.rmi.NotBoundException nbe) {
  359           }
  360   
  361           getLog().info("Scheduler un-bound from name '" + bindName + "' in RMI registry");
  362       }
  363   
  364       /**
  365        * <p>
  366        * Returns the name of the <code>QuartzScheduler</code>.
  367        * </p>
  368        */
  369       public String getSchedulerName() {
  370           return resources.getName();
  371       }
  372   
  373       /**
  374        * <p>
  375        * Returns the instance Id of the <code>QuartzScheduler</code>.
  376        * </p>
  377        */
  378       public String getSchedulerInstanceId() {
  379           return resources.getInstanceId();
  380       }
  381   
  382       /**
  383        * <p>
  384        * Returns the name of the <code>QuartzScheduler</code>.
  385        * </p>
  386        */
  387       public ThreadGroup getSchedulerThreadGroup() {
  388           if (threadGroup == null) {
  389               threadGroup = new ThreadGroup("QuartzScheduler:"
  390                       + getSchedulerName());
  391               if (resources.getMakeSchedulerThreadDaemon()) {
  392                   threadGroup.setDaemon(true);
  393               }
  394           }
  395   
  396           return threadGroup;
  397       }
  398   
  399       public void addNoGCObject(Object obj) {
  400           holdToPreventGC.add(obj);
  401       }
  402   
  403       public boolean removeNoGCObject(Object obj) {
  404           return holdToPreventGC.remove(obj);
  405       }
  406   
  407       /**
  408        * <p>
  409        * Returns the <code>SchedulerContext</code> of the <code>Scheduler</code>.
  410        * </p>
  411        */
  412       public SchedulerContext getSchedulerContext() throws SchedulerException {
  413           return context;
  414       }
  415   
  416       public boolean isSignalOnSchedulingChange() {
  417           return signalOnSchedulingChange;
  418       }
  419   
  420       public void setSignalOnSchedulingChange(boolean signalOnSchedulingChange) {
  421           this.signalOnSchedulingChange = signalOnSchedulingChange;
  422       }
  423   
  424       ///////////////////////////////////////////////////////////////////////////
  425       ///
  426       /// Schedululer State Management Methods
  427       ///
  428       ///////////////////////////////////////////////////////////////////////////
  429   
  430       /**
  431        * <p>
  432        * Starts the <code>QuartzScheduler</code>'s threads that fire <code>{@link org.quartz.Trigger}s</code>.
  433        * </p>
  434        * 
  435        * <p>
  436        * All <code>{@link org.quartz.Trigger}s</code> that have misfired will
  437        * be passed to the appropriate TriggerListener(s).
  438        * </p>
  439        */
  440       public void start() throws SchedulerException {
  441   
  442           if (closed) {
  443               throw new SchedulerException(
  444                       "The Scheduler cannot be restarted after shutdown() has been called.");
  445           }
  446   
  447           if (initialStart == null) {
  448               initialStart = new Date();
  449               this.resources.getJobStore().schedulerStarted();            
  450               startPlugins();
  451           }
  452   
  453           schedThread.togglePause(false);
  454   
  455           getLog().info(
  456                   "Scheduler " + resources.getUniqueIdentifier() + " started.");
  457       }
  458   
  459       /**
  460        * <p>
  461        * Temporarily halts the <code>QuartzScheduler</code>'s firing of <code>{@link org.quartz.Trigger}s</code>.
  462        * </p>
  463        * 
  464        * <p>
  465        * The scheduler is not destroyed, and can be re-started at any time.
  466        * </p>
  467        */
  468       public void standby() {
  469           schedThread.togglePause(true);
  470           getLog().info(
  471                   "Scheduler " + resources.getUniqueIdentifier() + " paused.");
  472       }
  473   
  474       /**
  475        * <p>
  476        * Reports whether the <code>Scheduler</code> is paused.
  477        * </p>
  478        */
  479       public boolean isInStandbyMode() {
  480           return schedThread.isPaused();
  481       }
  482   
  483       public Date runningSince() {
  484           return initialStart;
  485       }
  486   
  487       public int numJobsExecuted() {
  488           return jobMgr.getNumJobsFired();
  489       }
  490   
  491       public Class getJobStoreClass() {
  492           return resources.getJobStore().getClass();
  493       }
  494   
  495       public boolean supportsPersistence() {
  496           return resources.getJobStore().supportsPersistence();
  497       }
  498   
  499       public Class getThreadPoolClass() {
  500           return resources.getThreadPool().getClass();
  501       }
  502   
  503       public int getThreadPoolSize() {
  504           return resources.getThreadPool().getPoolSize();
  505       }
  506   
  507       /**
  508        * <p>
  509        * Halts the <code>QuartzScheduler</code>'s firing of <code>{@link org.quartz.Trigger}s</code>,
  510        * and cleans up all resources associated with the QuartzScheduler.
  511        * Equivalent to <code>shutdown(false)</code>.
  512        * </p>
  513        * 
  514        * <p>
  515        * The scheduler cannot be re-started.
  516        * </p>
  517        */
  518       public void shutdown() {
  519           shutdown(false);
  520       }
  521   
  522       /**
  523        * <p>
  524        * Halts the <code>QuartzScheduler</code>'s firing of <code>{@link org.quartz.Trigger}s</code>,
  525        * and cleans up all resources associated with the QuartzScheduler.
  526        * </p>
  527        * 
  528        * <p>
  529        * The scheduler cannot be re-started.
  530        * </p>
  531        * 
  532        * @param waitForJobsToComplete
  533        *          if <code>true</code> the scheduler will not allow this method
  534        *          to return until all currently executing jobs have completed.
  535        */
  536       public void shutdown(boolean waitForJobsToComplete) {
  537           
  538           if(closed == true) {
  539               return;
  540           }
  541           
  542           getLog().info(
  543                   "Scheduler " + resources.getUniqueIdentifier()
  544                           + " shutting down.");
  545           standby();
  546   
  547           closed = true;
  548   
  549           schedThread.halt();
  550           
  551           resources.getThreadPool().shutdown(waitForJobsToComplete);
  552   
  553           if (waitForJobsToComplete) {
  554               while (jobMgr.getNumJobsCurrentlyExecuting() > 0) {
  555                   try {
  556                       Thread.sleep(100);
  557                   } catch (Exception ignore) {
  558                   }
  559               }
  560           }
  561   
  562           // Scheduler thread may have be waiting for the fire time of an acquired 
  563           // trigger and need time to release the trigger once halted, so make sure
  564           // the thread is dead before continuing to shutdown the job store.
  565           try {
  566               schedThread.join();
  567           } catch (InterruptedException ignore) {
  568           }
  569   
  570           resources.getJobStore().shutdown();
  571   
  572           notifySchedulerListenersShutdown();
  573   
  574           shutdownPlugins();
  575   
  576           SchedulerRepository.getInstance().remove(resources.getName());
  577   
  578           holdToPreventGC.clear();
  579   
  580           try {
  581               unBind();
  582           } catch (RemoteException re) {
  583           }
  584           
  585           if (resources.getJMXExport()) {
  586               try {
  587                   unregisterJMX();
  588               } catch (Exception e) {
  589               }
  590           }
  591   
  592           getLog().info(
  593                   "Scheduler " + resources.getUniqueIdentifier()
  594                           + " shutdown complete.");
  595       }
  596   
  597       /**
  598        * <p>
  599        * Reports whether the <code>Scheduler</code> has been shutdown.
  600        * </p>
  601        */
  602       public boolean isShutdown() {
  603           return closed;
  604       }
  605   
  606       public void validateState() throws SchedulerException {
  607           if (isShutdown()) {
  608               throw new SchedulerException("The Scheduler has been shutdown.");
  609           }
  610   
  611           // other conditions to check (?)
  612       }
  613   
  614       /**
  615        * <p>
  616        * Return a list of <code>JobExecutionContext</code> objects that
  617        * represent all currently executing Jobs in this Scheduler instance.
  618        * </p>
  619        * 
  620        * <p>
  621        * This method is not cluster aware.  That is, it will only return Jobs
  622        * currently executing in this Scheduler instance, not across the entire
  623        * cluster.
  624        * </p>
  625        * 
  626        * <p>
  627        * Note that the list returned is an 'instantaneous' snap-shot, and that as
  628        * soon as it's returned, the true list of executing jobs may be different.
  629        * </p>
  630        */
  631       public List getCurrentlyExecutingJobs() {
  632           return jobMgr.getExecutingJobs();
  633       }
  634   
  635       ///////////////////////////////////////////////////////////////////////////
  636       ///
  637       /// Scheduling-related Methods
  638       ///
  639       ///////////////////////////////////////////////////////////////////////////
  640   
  641       /**
  642        * <p>
  643        * Add the <code>{@link org.quartz.Job}</code> identified by the given
  644        * <code>{@link org.quartz.JobDetail}</code> to the Scheduler, and
  645        * associate the given <code>{@link org.quartz.Trigger}</code> with it.
  646        * </p>
  647        * 
  648        * <p>
  649        * If the given Trigger does not reference any <code>Job</code>, then it
  650        * will be set to reference the Job passed with it into this method.
  651        * </p>
  652        * 
  653        * @throws SchedulerException
  654        *           if the Job or Trigger cannot be added to the Scheduler, or
  655        *           there is an internal Scheduler error.
  656        */
  657       public Date scheduleJob(SchedulingContext ctxt, JobDetail jobDetail,
  658               Trigger trigger) throws SchedulerException {
  659           validateState();
  660   
  661           if (jobDetail == null) {
  662               throw new SchedulerException("JobDetail cannot be null",
  663                       SchedulerException.ERR_CLIENT_ERROR);
  664           }
  665           
  666           if (trigger == null) {
  667               throw new SchedulerException("Trigger cannot be null",
  668                       SchedulerException.ERR_CLIENT_ERROR);
  669           }
  670           
  671           jobDetail.validate();
  672   
  673           if (trigger.getJobName() == null) {
  674               trigger.setJobName(jobDetail.getName());
  675               trigger.setJobGroup(jobDetail.getGroup());
  676           } else if (trigger.getJobName() != null
  677                   && !trigger.getJobName().equals(jobDetail.getName())) {
  678               throw new SchedulerException(
  679                   "Trigger does not reference given job!",
  680                   SchedulerException.ERR_CLIENT_ERROR);
  681           } else if (trigger.getJobGroup() != null
  682                   && !trigger.getJobGroup().equals(jobDetail.getGroup())) {
  683               throw new SchedulerException(
  684                   "Trigger does not reference given job!",
  685                   SchedulerException.ERR_CLIENT_ERROR);
  686           }
  687   
  688           trigger.validate();
  689   
  690           Calendar cal = null;
  691           if (trigger.getCalendarName() != null) {
  692               cal = resources.getJobStore().retrieveCalendar(ctxt,
  693                       trigger.getCalendarName());
  694           }
  695           Date ft = trigger.computeFirstFireTime(cal);
  696   
  697           if (ft == null) {
  698               throw new SchedulerException(
  699                       "Based on configured schedule, the given trigger will never fire.",
  700                       SchedulerException.ERR_CLIENT_ERROR);
  701           }
  702   
  703           resources.getJobStore().storeJobAndTrigger(ctxt, jobDetail, trigger);
  704           notifySchedulerThread();
  705           notifySchedulerListenersSchduled(trigger);
  706   
  707           return ft;
  708       }
  709   
  710       /**
  711        * <p>
  712        * Schedule the given <code>{@link org.quartz.Trigger}</code> with the
  713        * <code>Job</code> identified by the <code>Trigger</code>'s settings.
  714        * </p>
  715        * 
  716        * @throws SchedulerException
  717        *           if the indicated Job does not exist, or the Trigger cannot be
  718        *           added to the Scheduler, or there is an internal Scheduler
  719        *           error.
  720        */
  721       public Date scheduleJob(SchedulingContext ctxt, Trigger trigger)
  722           throws SchedulerException {
  723           validateState();
  724   
  725           if (trigger == null) {
  726               throw new SchedulerException("Trigger cannot be null",
  727                       SchedulerException.ERR_CLIENT_ERROR);
  728           }
  729   
  730           trigger.validate();
  731   
  732           Calendar cal = null;
  733           if (trigger.getCalendarName() != null) {
  734               cal = resources.getJobStore().retrieveCalendar(ctxt,
  735                       trigger.getCalendarName());
  736               if(cal == null) {
  737                   throw new SchedulerException(
  738                       "Calendar not found: " + trigger.getCalendarName(), 
  739                       SchedulerException.ERR_PERSISTENCE_CALENDAR_DOES_NOT_EXIST);
  740               }
  741           }
  742           Date ft = trigger.computeFirstFireTime(cal);
  743   
  744           if (ft == null) {
  745               throw new SchedulerException(
  746                       "Based on configured schedule, the given trigger will never fire.",
  747                       SchedulerException.ERR_CLIENT_ERROR);
  748           }
  749   
  750           resources.getJobStore().storeTrigger(ctxt, trigger, false);
  751           notifySchedulerThread();
  752           notifySchedulerListenersSchduled(trigger);
  753   
  754           return ft;
  755       }
  756   
  757       /**
  758        * <p>
  759        * Add the given <code>Job</code> to the Scheduler - with no associated
  760        * <code>Trigger</code>. The <code>Job</code> will be 'dormant' until
  761        * it is scheduled with a <code>Trigger</code>, or <code>Scheduler.triggerJob()</code>
  762        * is called for it.
  763        * </p>
  764        * 
  765        * <p>
  766        * The <code>Job</code> must by definition be 'durable', if it is not,
  767        * SchedulerException will be thrown.
  768        * </p>
  769        * 
  770        * @throws SchedulerException
  771        *           if there is an internal Scheduler error, or if the Job is not
  772        *           durable, or a Job with the same name already exists, and
  773        *           <code>replace</code> is <code>false</code>.
  774        */
  775       public void addJob(SchedulingContext ctxt, JobDetail jobDetail,
  776               boolean replace) throws SchedulerException {
  777           validateState();
  778   
  779           if (!jobDetail.isDurable() && !replace) {
  780               throw new SchedulerException(
  781                       "Jobs added with no trigger must be durable.",
  782                       SchedulerException.ERR_CLIENT_ERROR);
  783           }
  784   
  785           resources.getJobStore().storeJob(ctxt, jobDetail, replace);
  786       }
  787   
  788       /**
  789        * <p>
  790        * Delete the identified <code>Job</code> from the Scheduler - and any
  791        * associated <code>Trigger</code>s.
  792        * </p>
  793        * 
  794        * @return true if the Job was found and deleted.
  795        * @throws SchedulerException
  796        *           if there is an internal Scheduler error.
  797        */
  798       public boolean deleteJob(SchedulingContext ctxt, String jobName,
  799               String groupName) throws SchedulerException {
  800           validateState();
  801   
  802           if(groupName == null) {
  803               groupName = Scheduler.DEFAULT_GROUP;
  804           }
  805           
  806           return resources.getJobStore().removeJob(ctxt, jobName, groupName);
  807       }
  808   
  809       /**
  810        * <p>
  811        * Remove the indicated <code>{@link org.quartz.Trigger}</code> from the
  812        * scheduler.
  813        * </p>
  814        */
  815       public boolean unscheduleJob(SchedulingContext ctxt, String triggerName,
  816               String groupName) throws SchedulerException {
  817           validateState();
  818   
  819           if(groupName == null) {
  820               groupName = Scheduler.DEFAULT_GROUP;
  821           }
  822           
  823           if (resources.getJobStore().removeTrigger(ctxt, triggerName, groupName)) {
  824               notifySchedulerThread();
  825               notifySchedulerListenersUnschduled(triggerName, groupName);
  826           } else {
  827               return false;
  828           }
  829   
  830           return true;
  831       }
  832   
  833   
  834       /**
  835        * <p>
  836        * Remove (delete) the <code>{@link org.quartz.Trigger}</code> with the
  837        * given name, and store the new given one - which must be associated
  838        * with the same job.
  839        * </p>
  840        * 
  841        * @param triggerName
  842        *          The name of the <code>Trigger</code> to be removed.
  843        * @param groupName
  844        *          The group name of the <code>Trigger</code> to be removed.
  845        * @param newTrigger
  846        *          The new <code>Trigger</code> to be stored.
  847        * @return <code>null</code> if a <code>Trigger</code> with the given
  848        *         name & group was not found and removed from the store, otherwise
  849        *         the first fire time of the newly scheduled trigger.
  850        */
  851       public Date rescheduleJob(SchedulingContext ctxt, String triggerName,
  852               String groupName, Trigger newTrigger) throws SchedulerException {
  853           validateState();
  854   
  855           if(groupName == null) {
  856               groupName = Scheduler.DEFAULT_GROUP;
  857           }
  858   
  859           newTrigger.validate();
  860   
  861           Calendar cal = null;
  862           if (newTrigger.getCalendarName() != null) {
  863               cal = resources.getJobStore().retrieveCalendar(ctxt,
  864                       newTrigger.getCalendarName());
  865           }
  866           Date ft = newTrigger.computeFirstFireTime(cal);
  867   
  868           if (ft == null) {
  869               throw new SchedulerException(
  870                       "Based on configured schedule, the given trigger will never fire.",
  871                       SchedulerException.ERR_CLIENT_ERROR);
  872           }
  873           
  874           if (resources.getJobStore().replaceTrigger(ctxt, triggerName, groupName, newTrigger)) {
  875               notifySchedulerThread();
  876               notifySchedulerListenersUnschduled(triggerName, groupName);
  877               notifySchedulerListenersSchduled(newTrigger);
  878           } else {
  879               return null;
  880           }
  881   
  882           return ft;
  883           
  884       }
  885       
  886       
  887       private String newTriggerId() {
  888           long r = random.nextLong();
  889           if (r < 0) {
  890               r = -r;
  891           }
  892           return "MT_"
  893                   + Long.toString(r, 30 + (int) (System.currentTimeMillis() % 7));
  894       }
  895   
  896       /**
  897        * <p>
  898        * Trigger the identified <code>{@link org.quartz.Job}</code> (execute it
  899        * now) - with a non-volatile trigger.
  900        * </p>
  901        */
  902       public void triggerJob(SchedulingContext ctxt, String jobName,
  903               String groupName, JobDataMap data) throws SchedulerException {
  904           validateState();
  905   
  906           if(groupName == null) {
  907               groupName = Scheduler.DEFAULT_GROUP;
  908           }
  909           
  910           Trigger trig = new org.quartz.SimpleTrigger(newTriggerId(),
  911                   Scheduler.DEFAULT_MANUAL_TRIGGERS, jobName, groupName,
  912                   new Date(), null, 0, 0);
  913           trig.setVolatility(false);
  914           trig.computeFirstFireTime(null);
  915           if(data != null) {
  916               trig.setJobDataMap(data);
  917           }
  918   
  919           boolean collision = true;
  920           while (collision) {
  921               try {
  922                   resources.getJobStore().storeTrigger(ctxt, trig, false);
  923                   collision = false;
  924               } catch (ObjectAlreadyExistsException oaee) {
  925                   trig.setName(newTriggerId());
  926               }
  927           }
  928   
  929           notifySchedulerThread();
  930           notifySchedulerListenersSchduled(trig);
  931       }
  932   
  933       /**
  934        * <p>
  935        * Trigger the identified <code>{@link org.quartz.Job}</code> (execute it
  936        * now) - with a volatile trigger.
  937        * </p>
  938        */
  939       public void triggerJobWithVolatileTrigger(SchedulingContext ctxt,
  940               String jobName, String groupName, JobDataMap data) throws SchedulerException {
  941           validateState();
  942   
  943           if(groupName == null) {
  944               groupName = Scheduler.DEFAULT_GROUP;
  945           }
  946           
  947           Trigger trig = new org.quartz.SimpleTrigger(newTriggerId(),
  948                   Scheduler.DEFAULT_MANUAL_TRIGGERS, jobName, groupName,
  949                   new Date(), null, 0, 0);
  950           trig.setVolatility(true);
  951           trig.computeFirstFireTime(null);
  952           if(data != null) {
  953               trig.setJobDataMap(data);
  954           }
  955           
  956           boolean collision = true;
  957           while (collision) {
  958               try {
  959                   resources.getJobStore().storeTrigger(ctxt, trig, false);
  960                   collision = false;
  961               } catch (ObjectAlreadyExistsException oaee) {
  962                   trig.setName(newTriggerId());
  963               }
  964           }
  965   
  966           notifySchedulerThread();
  967           notifySchedulerListenersSchduled(trig);
  968       }
  969   
  970       /**
  971        * <p>
  972        * Pause the <code>{@link Trigger}</code> with the given name.
  973        * </p>
  974        *  
  975        */
  976       public void pauseTrigger(SchedulingContext ctxt, String triggerName,
  977               String groupName) throws SchedulerException {
  978           validateState();
  979   
  980           if(groupName == null) {
  981               groupName = Scheduler.DEFAULT_GROUP;
  982           }
  983           
  984           resources.getJobStore().pauseTrigger(ctxt, triggerName, groupName);
  985           notifySchedulerThread();
  986           notifySchedulerListenersPausedTrigger(triggerName, groupName);
  987       }
  988   
  989       /**
  990        * <p>
  991        * Pause all of the <code>{@link Trigger}s</code> in the given group.
  992        * </p>
  993        *  
  994        */
  995       public void pauseTriggerGroup(SchedulingContext ctxt, String groupName)
  996           throws SchedulerException {
  997           validateState();
  998   
  999           if(groupName == null) {
 1000               groupName = Scheduler.DEFAULT_GROUP;
 1001           }
 1002           
 1003           resources.getJobStore().pauseTriggerGroup(ctxt, groupName);
 1004           notifySchedulerThread();
 1005           notifySchedulerListenersPausedTrigger(null, groupName);
 1006       }
 1007   
 1008       /**
 1009        * <p>
 1010        * Pause the <code>{@link org.quartz.JobDetail}</code> with the given
 1011        * name - by pausing all of its current <code>Trigger</code>s.
 1012        * </p>
 1013        *  
 1014        */
 1015       public void pauseJob(SchedulingContext ctxt, String jobName,
 1016               String groupName) throws SchedulerException {
 1017           validateState();
 1018   
 1019           if(groupName == null) {
 1020               groupName = Scheduler.DEFAULT_GROUP;
 1021           }
 1022   
 1023           resources.getJobStore().pauseJob(ctxt, jobName, groupName);
 1024           notifySchedulerThread();
 1025           notifySchedulerListenersPausedJob(jobName, groupName);
 1026       }
 1027   
 1028       /**
 1029        * <p>
 1030        * Pause all of the <code>{@link org.quartz.JobDetail}s</code> in the
 1031        * given group - by pausing all of their <code>Trigger</code>s.
 1032        * </p>
 1033        *  
 1034        */
 1035       public void pauseJobGroup(SchedulingContext ctxt, String groupName)
 1036           throws SchedulerException {
 1037           validateState();
 1038   
 1039           if(groupName == null) {
 1040               groupName = Scheduler.DEFAULT_GROUP;
 1041           }
 1042           
 1043           resources.getJobStore().pauseJobGroup(ctxt, groupName);
 1044           notifySchedulerThread();
 1045           notifySchedulerListenersPausedJob(null, groupName);
 1046       }
 1047   
 1048       /**
 1049        * <p>
 1050        * Resume (un-pause) the <code>{@link Trigger}</code> with the given
 1051        * name.
 1052        * </p>
 1053        * 
 1054        * <p>
 1055        * If the <code>Trigger</code> missed one or more fire-times, then the
 1056        * <code>Trigger</code>'s misfire instruction will be applied.
 1057        * </p>
 1058        *  
 1059        */
 1060       public void resumeTrigger(SchedulingContext ctxt, String triggerName,
 1061               String groupName) throws SchedulerException {
 1062           validateState();
 1063   
 1064           if(groupName == null) {
 1065               groupName = Scheduler.DEFAULT_GROUP;
 1066           }
 1067           
 1068           resources.getJobStore().resumeTrigger(ctxt, triggerName, groupName);
 1069           notifySchedulerThread();
 1070           notifySchedulerListenersResumedTrigger(triggerName, groupName);
 1071       }
 1072   
 1073       /**
 1074        * <p>
 1075        * Resume (un-pause) all of the <code>{@link Trigger}s</code> in the
 1076        * given group.
 1077        * </p>
 1078        * 
 1079        * <p>
 1080        * If any <code>Trigger</code> missed one or more fire-times, then the
 1081        * <code>Trigger</code>'s misfire instruction will be applied.
 1082        * </p>
 1083        *  
 1084        */
 1085       public void resumeTriggerGroup(SchedulingContext ctxt, String groupName)
 1086           throws SchedulerException {
 1087           validateState();
 1088   
 1089           if(groupName == null) {
 1090               groupName = Scheduler.DEFAULT_GROUP;
 1091           }
 1092           
 1093           resources.getJobStore().resumeTriggerGroup(ctxt, groupName);
 1094           notifySchedulerThread();
 1095           notifySchedulerListenersResumedTrigger(null, groupName);
 1096       }
 1097   
 1098       public Set getPausedTriggerGroups(SchedulingContext ctxt) throws SchedulerException {
 1099           return resources.getJobStore().getPausedTriggerGroups(ctxt);
 1100       }
 1101       
 1102       /**
 1103        * <p>
 1104        * Resume (un-pause) the <code>{@link org.quartz.JobDetail}</code> with
 1105        * the given name.
 1106        * </p>
 1107        * 
 1108        * <p>
 1109        * If any of the <code>Job</code>'s<code>Trigger</code> s missed one
 1110        * or more fire-times, then the <code>Trigger</code>'s misfire
 1111        * instruction will be applied.
 1112        * </p>
 1113        *  
 1114        */
 1115       public void resumeJob(SchedulingContext ctxt, String jobName,
 1116               String groupName) throws SchedulerException {
 1117           validateState();
 1118   
 1119           if(groupName == null) {
 1120               groupName = Scheduler.DEFAULT_GROUP;
 1121           }
 1122           
 1123           resources.getJobStore().resumeJob(ctxt, jobName, groupName);
 1124           notifySchedulerThread();
 1125           notifySchedulerListenersResumedJob(jobName, groupName);
 1126       }
 1127   
 1128       /**
 1129        * <p>
 1130        * Resume (un-pause) all of the <code>{@link org.quartz.JobDetail}s</code>
 1131        * in the given group.
 1132        * </p>
 1133        * 
 1134        * <p>
 1135        * If any of the <code>Job</code> s had <code>Trigger</code> s that
 1136        * missed one or more fire-times, then the <code>Trigger</code>'s
 1137        * misfire instruction will be applied.
 1138        * </p>
 1139        *  
 1140        */
 1141       public void resumeJobGroup(SchedulingContext ctxt, String groupName)
 1142           throws SchedulerException {
 1143           validateState();
 1144   
 1145           if(groupName == null) {
 1146               groupName = Scheduler.DEFAULT_GROUP;
 1147           }
 1148           
 1149           resources.getJobStore().resumeJobGroup(ctxt, groupName);
 1150           notifySchedulerThread();
 1151           notifySchedulerListenersResumedJob(null, groupName);
 1152       }
 1153   
 1154       /**
 1155        * <p>
 1156        * Pause all triggers - equivalent of calling <code>pauseTriggerGroup(group)</code>
 1157        * on every group.
 1158        * </p>
 1159        * 
 1160        * <p>
 1161        * When <code>resumeAll()</code> is called (to un-pause), trigger misfire
 1162        * instructions WILL be applied.
 1163        * </p>
 1164        * 
 1165        * @see #resumeAll(SchedulingContext)
 1166        * @see #pauseTriggerGroup(SchedulingContext, String)
 1167        * @see #standby()
 1168        */
 1169       public void pauseAll(SchedulingContext ctxt) throws SchedulerException {
 1170           validateState();
 1171   
 1172           resources.getJobStore().pauseAll(ctxt);
 1173           notifySchedulerThread();
 1174           notifySchedulerListenersPausedTrigger(null, null);
 1175       }
 1176   
 1177       /**
 1178        * <p>
 1179        * Resume (un-pause) all triggers - equivalent of calling <code>resumeTriggerGroup(group)</code>
 1180        * on every group.
 1181        * </p>
 1182        * 
 1183        * <p>
 1184        * If any <code>Trigger</code> missed one or more fire-times, then the
 1185        * <code>Trigger</code>'s misfire instruction will be applied.
 1186        * </p>
 1187        * 
 1188        * @see #pauseAll(SchedulingContext)
 1189        */
 1190       public void resumeAll(SchedulingContext ctxt) throws SchedulerException {
 1191           validateState();
 1192   
 1193           resources.getJobStore().resumeAll(ctxt);
 1194           notifySchedulerThread();
 1195           notifySchedulerListenersResumedTrigger(null, null);
 1196       }
 1197   
 1198       /**
 1199        * <p>
 1200        * Get the names of all known <code>{@link org.quartz.Job}</code> groups.
 1201        * </p>
 1202        */
 1203       public String[] getJobGroupNames(SchedulingContext ctxt)
 1204           throws SchedulerException {
 1205           validateState();
 1206   
 1207           return resources.getJobStore().getJobGroupNames(ctxt);
 1208       }
 1209   
 1210       /**
 1211        * <p>
 1212        * Get the names of all the <code>{@link org.quartz.Job}s</code> in the
 1213        * given group.
 1214        * </p>
 1215        */
 1216       public String[] getJobNames(SchedulingContext ctxt, String groupName)
 1217           throws SchedulerException {
 1218           validateState();
 1219   
 1220           if(groupName == null) {
 1221               groupName = Scheduler.DEFAULT_GROUP;
 1222           }
 1223           
 1224           return resources.getJobStore().getJobNames(ctxt, groupName);
 1225       }
 1226   
 1227       /**
 1228        * <p>
 1229        * Get all <code>{@link Trigger}</code> s that are associated with the
 1230        * identified <code>{@link org.quartz.JobDetail}</code>.
 1231        * </p>
 1232        */
 1233       public Trigger[] getTriggersOfJob(SchedulingContext ctxt, String jobName,
 1234               String groupName) throws SchedulerException {
 1235           validateState();
 1236   
 1237           if(groupName == null) {
 1238               groupName = Scheduler.DEFAULT_GROUP;
 1239           }
 1240           
 1241           return resources.getJobStore().getTriggersForJob(ctxt, jobName,
 1242                   groupName);
 1243       }
 1244   
 1245       /**
 1246        * <p>
 1247        * Get the names of all known <code>{@link org.quartz.Trigger}</code>
 1248        * groups.
 1249        * </p>
 1250        */
 1251       public String[] getTriggerGroupNames(SchedulingContext ctxt)
 1252           throws SchedulerException {
 1253           validateState();
 1254   
 1255           return resources.getJobStore().getTriggerGroupNames(ctxt);
 1256       }
 1257   
 1258       /**
 1259        * <p>
 1260        * Get the names of all the <code>{@link org.quartz.Trigger}s</code> in
 1261        * the given group.
 1262        * </p>
 1263        */
 1264       public String[] getTriggerNames(SchedulingContext ctxt, String groupName)
 1265           throws SchedulerException {
 1266           validateState();
 1267   
 1268           if(groupName == null) {
 1269               groupName = Scheduler.DEFAULT_GROUP;
 1270           }
 1271           
 1272           return resources.getJobStore().getTriggerNames(ctxt, groupName);
 1273       }
 1274   
 1275       /**
 1276        * <p>
 1277        * Get the <code>{@link JobDetail}</code> for the <code>Job</code>
 1278        * instance with the given name and group.
 1279        * </p>
 1280        */
 1281       public JobDetail getJobDetail(SchedulingContext ctxt, String jobName,
 1282               String jobGroup) throws SchedulerException {
 1283           validateState();
 1284   
 1285           if(jobGroup == null) {
 1286               jobGroup = Scheduler.DEFAULT_GROUP;
 1287           }
 1288           
 1289           return resources.getJobStore().retrieveJob(ctxt, jobName, jobGroup);
 1290       }
 1291   
 1292       /**
 1293        * <p>
 1294        * Get the <code>{@link Trigger}</code> instance with the given name and
 1295        * group.
 1296        * </p>
 1297        */
 1298       public Trigger getTrigger(SchedulingContext ctxt, String triggerName,
 1299               String triggerGroup) throws SchedulerException {
 1300           validateState();
 1301   
 1302           if(triggerGroup == null) {
 1303               triggerGroup = Scheduler.DEFAULT_GROUP;
 1304           }
 1305           
 1306           return resources.getJobStore().retrieveTrigger(ctxt, triggerName,
 1307                   triggerGroup);
 1308       }
 1309   
 1310       /**
 1311        * <p>
 1312        * Get the current state of the identified <code>{@link Trigger}</code>.
 1313        * </p>
 1314        * 
 1315        * @see Trigger#STATE_NORMAL
 1316        * @see Trigger#STATE_PAUSED
 1317        * @see Trigger#STATE_COMPLETE
 1318        * @see Trigger#STATE_ERROR
 1319        */
 1320       public int getTriggerState(SchedulingContext ctxt, String triggerName,
 1321               String triggerGroup) throws SchedulerException {
 1322           validateState();
 1323   
 1324           if(triggerGroup == null) {
 1325               triggerGroup = Scheduler.DEFAULT_GROUP;
 1326           }
 1327           
 1328           return resources.getJobStore().getTriggerState(ctxt, triggerName,
 1329                   triggerGroup);
 1330       }
 1331   
 1332       /**
 1333        * <p>
 1334        * Add (register) the given <code>Calendar</code> to the Scheduler.
 1335        * </p>
 1336        * 
 1337        * @throws SchedulerException
 1338        *           if there is an internal Scheduler error, or a Calendar with
 1339        *           the same name already exists, and <code>replace</code> is
 1340        *           <code>false</code>.
 1341        */
 1342       public void addCalendar(SchedulingContext ctxt, String calName,
 1343               Calendar calendar, boolean replace, boolean updateTriggers) throws SchedulerException {
 1344           validateState();
 1345   
 1346           resources.getJobStore().storeCalendar(ctxt, calName, calendar, replace, updateTriggers);
 1347       }
 1348   
 1349       /**
 1350        * <p>
 1351        * Delete the identified <code>Calendar</code> from the Scheduler.
 1352        * </p>
 1353        * 
 1354        * @return true if the Calendar was found and deleted.
 1355        * @throws SchedulerException
 1356        *           if there is an internal Scheduler error.
 1357        */
 1358       public boolean deleteCalendar(SchedulingContext ctxt, String calName)
 1359           throws SchedulerException {
 1360           validateState();
 1361   
 1362           return resources.getJobStore().removeCalendar(ctxt, calName);
 1363       }
 1364   
 1365       /**
 1366        * <p>
 1367        * Get the <code>{@link Calendar}</code> instance with the given name.
 1368        * </p>
 1369        */
 1370       public Calendar getCalendar(SchedulingContext ctxt, String calName)
 1371           throws SchedulerException {
 1372           validateState();
 1373   
 1374           return resources.getJobStore().retrieveCalendar(ctxt, calName);
 1375       }
 1376   
 1377       /**
 1378        * <p>
 1379        * Get the names of all registered <code>{@link Calendar}s</code>.
 1380        * </p>
 1381        */
 1382       public String[] getCalendarNames(SchedulingContext ctxt)
 1383           throws SchedulerException {
 1384           validateState();
 1385   
 1386           return resources.getJobStore().getCalendarNames(ctxt);
 1387       }
 1388   
 1389       /**
 1390        * <p>
 1391        * Add the given <code>{@link org.quartz.JobListener}</code> to the
 1392        * <code>Scheduler</code>'s<i>global</i> list.
 1393        * </p>
 1394        * 
 1395        * <p>
 1396        * Listeners in the 'global' list receive notification of execution events
 1397        * for ALL <code>{@link org.quartz.Job}</code>s.
 1398        * </p>
 1399        */
 1400       public void addGlobalJobListener(JobListener jobListener) {
 1401           if (jobListener.getName() == null
 1402                   || jobListener.getName().length() == 0) {
 1403               throw new IllegalArgumentException(
 1404                       "JobListener name cannot be empty.");
 1405           }
 1406           
 1407           synchronized (globalJobListeners) {
 1408               globalJobListeners.put(jobListener.getName(), jobListener);
 1409           }
 1410       }
 1411   
 1412       /**
 1413        * <p>
 1414        * Add the given <code>{@link org.quartz.JobListener}</code> to the
 1415        * <code>Scheduler</code>'s list, of registered <code>JobListener</code>s.
 1416        */
 1417       public void addJobListener(JobListener jobListener) {
 1418           if (jobListener.getName() == null
 1419                   || jobListener.getName().length() == 0) {
 1420               throw new IllegalArgumentException(
 1421                       "JobListener name cannot be empty.");
 1422           }
 1423   
 1424           synchronized (jobListeners) {
 1425               jobListeners.put(jobListener.getName(), jobListener);
 1426           }
 1427       }
 1428   
 1429       /**
 1430        * <p>
 1431        * Remove the given <code>{@link org.quartz.JobListener}</code> from the
 1432        * <code>Scheduler</code>'s list of <i>global</i> listeners.
 1433        * </p>
 1434        * 
 1435        * @return true if the identifed listener was found in the list, and
 1436        *         removed.
 1437        *         
 1438        * @deprecated Use <code>{@link #removeGlobalJobListener(String)}</code>
 1439        */
 1440       public boolean removeGlobalJobListener(JobListener jobListener) {
 1441           return removeGlobalJobListener((jobListener == null) ? null : jobListener.getName());
 1442       }
 1443   
 1444       /**
 1445        * <p>
 1446        * Remove the identifed <code>{@link JobListener}</code> from the <code>Scheduler</code>'s
 1447        * list of <i>global</i> listeners.
 1448        * </p>
 1449        * 
 1450        * @return true if the identifed listener was found in the list, and
 1451        *         removed.
 1452        */
 1453       public boolean removeGlobalJobListener(String name) {
 1454           synchronized (globalJobListeners) {
 1455               return (globalJobListeners.remove(name) != null);
 1456           }
 1457       }
 1458       
 1459       /**
 1460        * <p>
 1461        * Remove the identifed <code>{@link org.quartz.JobListener}</code> from
 1462        * the <code>Scheduler</code>'s list of registered listeners.
 1463        * </p>
 1464        * 
 1465        * @return true if the identifed listener was found in the list, and
 1466        *         removed.
 1467        */
 1468       public boolean removeJobListener(String name) {
 1469           synchronized (jobListeners) {
 1470               return (jobListeners.remove(name) != null);
 1471           }
 1472       }
 1473   
 1474       /**
 1475        * <p>
 1476        * Get a List containing all of the <code>{@link org.quartz.JobListener}</code>
 1477        * s in the <code>Scheduler</code>'s<i>global</i> list.
 1478        * </p>
 1479        */
 1480       public List getGlobalJobListeners() {
 1481           synchronized (globalJobListeners) {
 1482               return new LinkedList(globalJobListeners.values());
 1483           }
 1484       }
 1485   
 1486       /**
 1487        * <p>
 1488        * Get a Set containing the names of all the <i>non-global</i><code>{@link org.quartz.JobListener}</code>
 1489        * s registered with the <code>Scheduler</code>.
 1490        * </p>
 1491        */
 1492       public Set getJobListenerNames() {
 1493           synchronized (jobListeners) {
 1494               return new HashSet(jobListeners.keySet());
 1495           }
 1496       }
 1497   
 1498       /**
 1499        * <p>
 1500        * Get the <i>global</i><code>{@link org.quartz.JobListener}</code>
 1501        * that has the given name.
 1502        * </p>
 1503        */
 1504       public JobListener getGlobalJobListener(String name) {
 1505           synchronized (globalJobListeners) {
 1506               return (JobListener)globalJobListeners.get(name);
 1507           }
 1508       }
 1509   
 1510       /**
 1511        * <p>
 1512        * Get the <i>non-global</i><code>{@link org.quartz.JobListener}</code>
 1513        * that has the given name.
 1514        * </p>
 1515        */
 1516       public JobListener getJobListener(String name) {
 1517           synchronized (jobListeners) {
 1518               return (JobListener) jobListeners.get(name);
 1519           }
 1520       }
 1521   
 1522       /**
 1523        * <p>
 1524        * Add the given <code>{@link org.quartz.TriggerListener}</code> to the
 1525        * <code>Scheduler</code>'s<i>global</i> list.
 1526        * </p>
 1527        * 
 1528        * <p>
 1529        * Listeners in the 'global' list receive notification of execution events
 1530        * for ALL <code>{@link org.quartz.Trigger}</code>s.
 1531        * </p>
 1532        */
 1533       public void addGlobalTriggerListener(TriggerListener triggerListener) {
 1534           if (triggerListener.getName() == null
 1535                   || triggerListener.getName().length() == 0) {
 1536               throw new IllegalArgumentException(
 1537                       "TriggerListener name cannot be empty.");
 1538           }
 1539   
 1540           synchronized (globalTriggerListeners) {
 1541               globalTriggerListeners.put(triggerListener.getName(), triggerListener);
 1542           }
 1543       }
 1544   
 1545       /**
 1546        * <p>
 1547        * Add the given <code>{@link org.quartz.TriggerListener}</code> to the
 1548        * <code>Scheduler</code>'s list, of registered <code>TriggerListener</code>s.
 1549        */
 1550       public void addTriggerListener(TriggerListener triggerListener) {
 1551           if (triggerListener.getName() == null
 1552                   || triggerListener.getName().length() == 0) {
 1553               throw new IllegalArgumentException(
 1554                       "TriggerListener name cannot be empty.");
 1555           }
 1556   
 1557           synchronized (triggerListeners) {
 1558               triggerListeners.put(triggerListener.getName(), triggerListener);
 1559           }
 1560       }
 1561   
 1562       /**
 1563        * <p>
 1564        * Remove the given <code>{@link org.quartz.TriggerListener}</code> from
 1565        * the <code>Scheduler</code>'s list of <i>global</i> listeners.
 1566        * </p>
 1567        * 
 1568        * @return true if the identifed listener was found in the list, and
 1569        *         removed.
 1570        *         
 1571        * @deprecated Use <code>{@link #removeGlobalTriggerListener(String)}</code>
 1572        */
 1573       public boolean removeGlobalTriggerListener(TriggerListener triggerListener) {
 1574           return removeGlobalTriggerListener((triggerListener == null) ? null : triggerListener.getName());
 1575       }
 1576   
 1577       /**
 1578        * <p>
 1579        * Remove the identifed <code>{@link TriggerListener}</code> from the <code>Scheduler</code>'s
 1580        * list of <i>global</i> listeners.
 1581        * </p>
 1582        * 
 1583        * @return true if the identifed listener was found in the list, and
 1584        *         removed.
 1585        */
 1586       public boolean removeGlobalTriggerListener(String name) {
 1587           synchronized (globalTriggerListeners) {
 1588               return (globalTriggerListeners.remove(name) != null);
 1589           }
 1590       }
 1591       
 1592       /**
 1593        * <p>
 1594        * Remove the identifed <code>{@link org.quartz.TriggerListener}</code>
 1595        * from the <code>Scheduler</code>'s list of registered listeners.
 1596        * </p>
 1597        * 
 1598        * @return true if the identifed listener was found in the list, and
 1599        *         removed.
 1600        */
 1601       public boolean removeTriggerListener(String name) {
 1602           synchronized (triggerListeners) {
 1603               return (triggerListeners.remove(name) != null);
 1604           }
 1605       }
 1606   
 1607       /**
 1608        * <p>
 1609        * Get a list containing all of the <code>{@link org.quartz.TriggerListener}</code>
 1610        * s in the <code>Scheduler</code>'s<i>global</i> list.
 1611        * </p>
 1612        */
 1613       public List getGlobalTriggerListeners() {
 1614           synchronized (globalTriggerListeners) {
 1615               return new LinkedList(globalTriggerListeners.values());
 1616           }
 1617       }
 1618   
 1619       /**
 1620        * <p>
 1621        * Get a Set containing the names of all the <i>non-global</i><code>{@link org.quartz.TriggerListener}</code>
 1622        * s registered with the <code>Scheduler</code>.
 1623        * </p>
 1624        */
 1625       public Set getTriggerListenerNames() {
 1626           synchronized (triggerListeners) {
 1627               return new HashSet(triggerListeners.keySet());
 1628           }
 1629       }
 1630   
 1631       /**
 1632        * <p>
 1633        * Get the <i>global</i><code>{@link TriggerListener}</code> that
 1634        * has the given name.
 1635        * </p>
 1636        */
 1637       public TriggerListener getGlobalTriggerListener(String name) {
 1638           synchronized (globalTriggerListeners) {
 1639               return (TriggerListener)globalTriggerListeners.get(name);
 1640           }
 1641       }
 1642       
 1643       /**
 1644        * <p>
 1645        * Get the <i>non-global</i><code>{@link org.quartz.TriggerListener}</code>
 1646        * that has the given name.
 1647        * </p>
 1648        */
 1649       public TriggerListener getTriggerListener(String name) {
 1650           synchronized (triggerListeners) {
 1651               return (TriggerListener) triggerListeners.get(name);
 1652           }
 1653       }
 1654   
 1655       /**
 1656        * <p>
 1657        * Register the given <code>{@link SchedulerListener}</code> with the
 1658        * <code>Scheduler</code>.
 1659        * </p>
 1660        */
 1661       public void addSchedulerListener(SchedulerListener schedulerListener) {
 1662           synchronized (schedulerListeners) {
 1663               schedulerListeners.add(schedulerListener);
 1664           }
 1665       }
 1666   
 1667       /**
 1668        * <p>
 1669        * Remove the given <code>{@link SchedulerListener}</code> from the
 1670        * <code>Scheduler</code>.
 1671        * </p>
 1672        * 
 1673        * @return true if the identifed listener was found in the list, and
 1674        *         removed.
 1675        */
 1676       public boolean removeSchedulerListener(SchedulerListener schedulerListener) {
 1677           synchronized (schedulerListeners) {
 1678               return schedulerListeners.remove(schedulerListener);
 1679           }
 1680       }
 1681   
 1682       /**
 1683        * <p>
 1684        * Get a List containing all of the <code>{@link SchedulerListener}</code>
 1685        * s registered with the <code>Scheduler</code>.
 1686        * </p>
 1687        */
 1688       public List getSchedulerListeners() {
 1689           synchronized (schedulerListeners) {
 1690               return (List)schedulerListeners.clone();
 1691           }
 1692       }
 1693   
 1694       protected void notifyJobStoreJobComplete(SchedulingContext ctxt,
 1695               Trigger trigger, JobDetail detail, int instCode)
 1696           throws JobPersistenceException {
 1697   
 1698           resources.getJobStore().triggeredJobComplete(ctxt, trigger, detail,
 1699                   instCode);
 1700       }
 1701   
 1702       protected void notifyJobStoreJobVetoed(SchedulingContext ctxt,
 1703               Trigger trigger, JobDetail detail, int instCode)
 1704           throws JobPersistenceException {
 1705   
 1706           resources.getJobStore().triggeredJobComplete(ctxt, trigger, detail, instCode);
 1707       }
 1708   
 1709       protected void notifySchedulerThread() {
 1710           if (isSignalOnSchedulingChange()) {
 1711               schedThread.signalSchedulingChange();
 1712           }
 1713       }
 1714   
 1715       private List buildTriggerListenerList(String[] additionalLstnrs)
 1716           throws SchedulerException {
 1717           List triggerListeners = getGlobalTriggerListeners();
 1718           for (int i = 0; i < additionalLstnrs.length; i++) {
 1719               TriggerListener tl = getTriggerListener(additionalLstnrs[i]);
 1720   
 1721               if (tl != null) {
 1722                   triggerListeners.add(tl);
 1723               } else {
 1724                   throw new SchedulerException("TriggerListener '"
 1725                           + additionalLstnrs[i] + "' not found.",
 1726                           SchedulerException.ERR_TRIGGER_LISTENER_NOT_FOUND);
 1727               }
 1728           }
 1729   
 1730           return triggerListeners;
 1731       }
 1732   
 1733       private List buildJobListenerList(String[] additionalLstnrs)
 1734           throws SchedulerException {
 1735           List jobListeners = getGlobalJobListeners();
 1736           for (int i = 0; i < additionalLstnrs.length; i++) {
 1737               JobListener jl = getJobListener(additionalLstnrs[i]);
 1738   
 1739               if (jl != null) {
 1740                   jobListeners.add(jl);
 1741               } else {
 1742                   throw new SchedulerException("JobListener '"
 1743                           + additionalLstnrs[i] + "' not found.",
 1744                           SchedulerException.ERR_JOB_LISTENER_NOT_FOUND);
 1745               }
 1746           }
 1747   
 1748           return jobListeners;
 1749       }
 1750   
 1751       public boolean notifyTriggerListenersFired(JobExecutionContext jec)
 1752           throws SchedulerException {
 1753           // build a list of all trigger listeners that are to be notified...
 1754           List triggerListeners = buildTriggerListenerList(jec.getTrigger()
 1755                   .getTriggerListenerNames());
 1756   
 1757           boolean vetoedExecution = false;
 1758           
 1759           // notify all trigger listeners in the list
 1760           java.util.Iterator itr = triggerListeners.iterator();
 1761           while (itr.hasNext()) {
 1762               TriggerListener tl = (TriggerListener) itr.next();
 1763               try {
 1764                   tl.triggerFired(jec.getTrigger(), jec);
 1765                   
 1766                   if(tl.vetoJobExecution(jec.getTrigger(), jec)) {
 1767                       vetoedExecution = true;
 1768                   }
 1769               } catch (Exception e) {
 1770                   SchedulerException se = new SchedulerException(
 1771                           "TriggerListener '" + tl.getName()
 1772                                   + "' threw exception: " + e.getMessage(), e);
 1773                   se.setErrorCode(SchedulerException.ERR_TRIGGER_LISTENER);
 1774                   throw se;
 1775               }
 1776           }
 1777           
 1778           return vetoedExecution;
 1779       }
 1780       
 1781   
 1782       public void notifyTriggerListenersMisfired(Trigger trigger)
 1783           throws SchedulerException {
 1784           // build a list of all trigger listeners that are to be notified...
 1785           List triggerListeners = buildTriggerListenerList(trigger
 1786                   .getTriggerListenerNames());
 1787   
 1788           // notify all trigger listeners in the list
 1789           java.util.Iterator itr = triggerListeners.iterator();
 1790           while (itr.hasNext()) {
 1791               TriggerListener tl = (TriggerListener) itr.next();
 1792               try {
 1793                   tl.triggerMisfired(trigger);
 1794               } catch (Exception e) {
 1795                   SchedulerException se = new SchedulerException(
 1796                           "TriggerListener '" + tl.getName()
 1797                                   + "' threw exception: " + e.getMessage(), e);
 1798                   se.setErrorCode(SchedulerException.ERR_TRIGGER_LISTENER);
 1799                   throw se;
 1800               }
 1801           }
 1802       }    
 1803   
 1804       public void notifyTriggerListenersComplete(JobExecutionContext jec,
 1805               int instCode) throws SchedulerException {
 1806           // build a list of all trigger listeners that are to be notified...
 1807           List triggerListeners = buildTriggerListenerList(jec.getTrigger()
 1808                   .getTriggerListenerNames());
 1809   
 1810           // notify all trigger listeners in the list
 1811           java.util.Iterator itr = triggerListeners.iterator();
 1812           while (itr.hasNext()) {
 1813               TriggerListener tl = (TriggerListener) itr.next();
 1814               try {
 1815                   tl.triggerComplete(jec.getTrigger(), jec, instCode);
 1816               } catch (Exception e) {
 1817                   SchedulerException se = new SchedulerException(
 1818                           "TriggerListener '" + tl.getName()
 1819                                   + "' threw exception: " + e.getMessage(), e);
 1820                   se.setErrorCode(SchedulerException.ERR_TRIGGER_LISTENER);
 1821                   throw se;
 1822               }
 1823           }
 1824       }
 1825   
 1826       public void notifyJobListenersToBeExecuted(JobExecutionContext jec)
 1827           throws SchedulerException {
 1828           // build a list of all job listeners that are to be notified...
 1829           List jobListeners = buildJobListenerList(jec.getJobDetail()
 1830                   .getJobListenerNames());
 1831   
 1832           // notify all job listeners
 1833           java.util.Iterator itr = jobListeners.iterator();
 1834           while (itr.hasNext()) {
 1835               JobListener jl = (JobListener) itr.next();
 1836               try {
 1837                   jl.jobToBeExecuted(jec);
 1838               } catch (Exception e) {
 1839                   SchedulerException se = new SchedulerException(
 1840                           "JobListener '" + jl.getName() + "' threw exception: "
 1841                                   + e.getMessage(), e);
 1842                   se.setErrorCode(SchedulerException.ERR_JOB_LISTENER);
 1843                   throw se;
 1844               }
 1845           }
 1846       }
 1847   
 1848       public void notifyJobListenersWasVetoed(JobExecutionContext jec)
 1849           throws SchedulerException {
 1850           // build a list of all job listeners that are to be notified...
 1851           List jobListeners = buildJobListenerList(jec.getJobDetail()
 1852                   .getJobListenerNames());
 1853   
 1854           // notify all job listeners
 1855           java.util.Iterator itr = jobListeners.iterator();
 1856           while (itr.hasNext()) {
 1857               JobListener jl = (JobListener) itr.next();
 1858               try {
 1859                   jl.jobExecutionVetoed(jec);
 1860               } catch (Exception e) {
 1861                   SchedulerException se = new SchedulerException(
 1862                           "JobListener '" + jl.getName() + "' threw exception: "
 1863                           + e.getMessage(), e);
 1864                   se.setErrorCode(SchedulerException.ERR_JOB_LISTENER);
 1865                   throw se;
 1866               }
 1867           }
 1868       }
 1869   
 1870       public void notifyJobListenersWasExecuted(JobExecutionContext jec,
 1871               JobExecutionException je) throws SchedulerException {
 1872           // build a list of all job listeners that are to be notified...
 1873           List jobListeners = buildJobListenerList(jec.getJobDetail()
 1874                   .getJobListenerNames());
 1875   
 1876           // notify all job listeners
 1877           java.util.Iterator itr = jobListeners.iterator();
 1878           while (itr.hasNext()) {
 1879               JobListener jl = (JobListener) itr.next();
 1880               try {
 1881                   jl.jobWasExecuted(jec, je);
 1882               } catch (Exception e) {
 1883                   SchedulerException se = new SchedulerException(
 1884                           "JobListener '" + jl.getName() + "' threw exception: "
 1885                                   + e.getMessage(), e);
 1886                   se.setErrorCode(SchedulerException.ERR_JOB_LISTENER);
 1887                   throw se;
 1888               }
 1889           }
 1890       }
 1891   
 1892       public void notifySchedulerListenersError(String msg, SchedulerException se) {
 1893           // build a list of all scheduler listeners that are to be notified...
 1894           List schedListeners = getSchedulerListeners();
 1895   
 1896           // notify all scheduler listeners
 1897           java.util.Iterator itr = schedListeners.iterator();
 1898           while (itr.hasNext()) {
 1899               SchedulerListener sl = (SchedulerListener) itr.next();
 1900               try {
 1901                   sl.schedulerError(msg, se);
 1902               } catch (Exception e) {
 1903                   getLog()
 1904                           .error(
 1905                                   "Error while notifying SchedulerListener of error: ",
 1906                                   e);
 1907                   getLog().error(
 1908                           "  Original error (for notification) was: " + msg, se);
 1909               }
 1910           }
 1911       }
 1912   
 1913       public void notifySchedulerListenersSchduled(Trigger trigger) {
 1914           // build a list of all scheduler listeners that are to be notified...
 1915           List schedListeners = getSchedulerListeners();
 1916   
 1917           // notify all scheduler listeners
 1918           java.util.Iterator itr = schedListeners.iterator();
 1919           while (itr.hasNext()) {
 1920               SchedulerListener sl = (SchedulerListener) itr.next();
 1921               try {
 1922                   sl.jobScheduled(trigger);
 1923               } catch (Exception e) {
 1924                   getLog().error(
 1925                           "Error while notifying SchedulerListener of scheduled job."
 1926                                   + "  Triger=" + trigger.getFullName(), e);
 1927               }
 1928           }
 1929       }
 1930   
 1931       public void notifySchedulerListenersUnschduled(String triggerName,
 1932               String triggerGroup) {
 1933           // build a list of all scheduler listeners that are to be notified...
 1934           List schedListeners = getSchedulerListeners();
 1935   
 1936           // notify all scheduler listeners
 1937           java.util.Iterator itr = schedListeners.iterator();
 1938           while (itr.hasNext()) {
 1939               SchedulerListener sl = (SchedulerListener) itr.next();
 1940               try {
 1941                   sl.jobUnscheduled(triggerName, triggerGroup);
 1942               } catch (Exception e) {
 1943                   getLog().error(
 1944                           "Error while notifying SchedulerListener of unscheduled job."
 1945                                   + "  Triger=" + triggerGroup + "."
 1946                                   + triggerName, e);
 1947               }
 1948           }
 1949       }
 1950   
 1951       public void notifySchedulerListenersFinalized(Trigger trigger) {
 1952           // build a list of all scheduler listeners that are to be notified...
 1953           List schedListeners = getSchedulerListeners();
 1954   
 1955           // notify all scheduler listeners
 1956           java.util.Iterator itr = schedListeners.iterator();
 1957           while (itr.hasNext()) {
 1958               SchedulerListener sl = (SchedulerListener) itr.next();
 1959               try {
 1960                   sl.triggerFinalized(trigger);
 1961               } catch (Exception e) {
 1962                   getLog().error(
 1963                           "Error while notifying SchedulerListener of finalized trigger."
 1964                                   + "  Triger=" + trigger.getFullName(), e);
 1965               }
 1966           }
 1967       }
 1968   
 1969       public void notifySchedulerListenersPausedTrigger(String name, String group) {
 1970           // build a list of all job listeners that are to be notified...
 1971           List schedListeners = getSchedulerListeners();
 1972   
 1973           // notify all scheduler listeners
 1974           java.util.Iterator itr = schedListeners.iterator();
 1975           while (itr.hasNext()) {
 1976               SchedulerListener sl = (SchedulerListener) itr.next();
 1977               try {
 1978                   sl.triggersPaused(name, group);
 1979               } catch (Exception e) {
 1980                   getLog().error(
 1981                           "Error while notifying SchedulerListener of paused trigger/group."
 1982                                   + "  Triger=" + group + "." + name, e);
 1983               }
 1984           }
 1985       }
 1986   
 1987       public void notifySchedulerListenersResumedTrigger(String name, String group) {
 1988           // build a list of all job listeners that are to be notified...
 1989           List schedListeners = getSchedulerListeners();
 1990   
 1991           // notify all scheduler listeners
 1992           java.util.Iterator itr = schedListeners.iterator();
 1993           while (itr.hasNext()) {
 1994               SchedulerListener sl = (SchedulerListener) itr.next();
 1995               try {
 1996                   sl.triggersResumed(name, group);
 1997               } catch (Exception e) {
 1998                   getLog().error(
 1999                           "Error while notifying SchedulerListener of resumed trigger/group."
 2000                                   + "  Triger=" + group + "." + name, e);
 2001               }
 2002           }
 2003       }
 2004   
 2005       public void notifySchedulerListenersPausedJob(String name, String group) {
 2006           // build a list of all job listeners that are to be notified...
 2007           List schedListeners = getSchedulerListeners();
 2008   
 2009           // notify all scheduler listeners
 2010           java.util.Iterator itr = schedListeners.iterator();
 2011           while (itr.hasNext()) {
 2012               SchedulerListener sl = (SchedulerListener) itr.next();
 2013               try {
 2014                   sl.jobsPaused(name, group);
 2015               } catch (Exception e) {
 2016                   getLog().error(
 2017                           "Error while notifying SchedulerListener of paused job/group."
 2018                                   + "  Job=" + group + "." + name, e);
 2019               }
 2020           }
 2021       }
 2022   
 2023       public void notifySchedulerListenersResumedJob(String name, String group) {
 2024           // build a list of all job listeners that are to be notified...
 2025           List schedListeners = getSchedulerListeners();
 2026   
 2027           // notify all scheduler listeners
 2028           java.util.Iterator itr = schedListeners.iterator();
 2029           while (itr.hasNext()) {
 2030               SchedulerListener sl = (SchedulerListener) itr.next();
 2031               try {
 2032                   sl.jobsResumed(name, group);
 2033               } catch (Exception e) {
 2034                   getLog().error(
 2035                           "Error while notifying SchedulerListener of resumed job/group."
 2036                                   + "  Job=" + group + "." + name, e);
 2037               }
 2038           }
 2039       }
 2040   
 2041       public void notifySchedulerListenersShutdown() {
 2042           // build a list of all job listeners that are to be notified...
 2043           List schedListeners = getSchedulerListeners();
 2044   
 2045           // notify all scheduler listeners
 2046           java.util.Iterator itr = schedListeners.iterator();
 2047           while (itr.hasNext()) {
 2048               SchedulerListener sl = (SchedulerListener) itr.next();
 2049               try {
 2050                   sl.schedulerShutdown();
 2051               } catch (Exception e) {
 2052                   getLog().error(
 2053                           "Error while notifying SchedulerListener of shutdown.",
 2054                           e);
 2055               }
 2056           }
 2057       }
 2058   
 2059       public void setJobFactory(JobFactory factory) throws SchedulerException {
 2060   
 2061           if(factory == null) {
 2062               throw new IllegalArgumentException("JobFactory cannot be set to null!");
 2063           }
 2064   
 2065           getLog().info("JobFactory set to: " + factory);
 2066   
 2067           this.jobFactory = factory;
 2068       }
 2069       
 2070       public JobFactory getJobFactory()  {
 2071           return jobFactory;
 2072       }
 2073       
 2074       
 2075       /**
 2076        * Interrupt all instances of the identified InterruptableJob executing in 
 2077        * this Scheduler instance.
 2078        *  
 2079        * <p>
 2080        * This method is not cluster aware.  That is, it will only interrupt 
 2081        * instances of the identified InterruptableJob currently executing in this 
 2082        * Scheduler instance, not across the entire cluster.
 2083        * </p>
 2084        * 
 2085        * @see org.quartz.core.RemotableQuartzScheduler#interrupt(org.quartz.core.SchedulingContext, java.lang.String, java.lang.String)
 2086        */
 2087       public boolean interrupt(SchedulingContext ctxt, String jobName, String groupName) throws UnableToInterruptJobException {
 2088   
 2089           if(groupName == null) {
 2090               groupName = Scheduler.DEFAULT_GROUP;
 2091           }
 2092           
 2093           List jobs = getCurrentlyExecutingJobs();
 2094           java.util.Iterator it = jobs.iterator();
 2095           
 2096           JobExecutionContext jec = null;
 2097           JobDetail jobDetail = null;
 2098           Job job = null;
 2099           
 2100           boolean interrupted = false;
 2101           
 2102           while (it.hasNext()) {
 2103               jec = (JobExecutionContext)it.next();
 2104               jobDetail = jec.getJobDetail();
 2105               if (jobName.equals(jobDetail.getName())
 2106                   && groupName.equals(jobDetail.getGroup())){
 2107                   job = jec.getJobInstance();
 2108                   if (job instanceof InterruptableJob) {
 2109                       ((InterruptableJob)job).interrupt();
 2110                       interrupted = true;
 2111                   } else {
 2112                       throw new UnableToInterruptJobException(
 2113                           "Job '"
 2114                           + jobName
 2115                           + "' of group '"
 2116                           + groupName
 2117                           + "' can not be interrupted, since it does not implement "
 2118                           + InterruptableJob.class.getName());
 2119                       
 2120                   }
 2121               }                        
 2122           }
 2123           
 2124           return interrupted;
 2125       }
 2126       
 2127       private void shutdownPlugins() {
 2128           java.util.Iterator itr = resources.getSchedulerPlugins().iterator();
 2129           while (itr.hasNext()) {
 2130               SchedulerPlugin plugin = (SchedulerPlugin) itr.next();
 2131               plugin.shutdown();
 2132           }
 2133       }
 2134   
 2135       private void startPlugins() {
 2136           java.util.Iterator itr = resources.getSchedulerPlugins().iterator();
 2137           while (itr.hasNext()) {
 2138               SchedulerPlugin plugin = (SchedulerPlugin) itr.next();
 2139               plugin.start();
 2140           }
 2141       }
 2142   
 2143   }
 2144   
 2145   /////////////////////////////////////////////////////////////////////////////
 2146   //
 2147   // ErrorLogger - Scheduler Listener Class
 2148   //
 2149   /////////////////////////////////////////////////////////////////////////////
 2150   
 2151   class ErrorLogger extends SchedulerListenerSupport {
 2152       ErrorLogger() {
 2153       }
 2154       
 2155       public void schedulerError(String msg, SchedulerException cause) {
 2156           getLog().error(msg, cause);
 2157       }
 2158   }
 2159   
 2160   /////////////////////////////////////////////////////////////////////////////
 2161   //
 2162   // ExecutingJobsManager - Job Listener Class
 2163   //
 2164   /////////////////////////////////////////////////////////////////////////////
 2165   
 2166   class ExecutingJobsManager implements JobListener {
 2167       HashMap executingJobs = new HashMap();
 2168   
 2169       int numJobsFired = 0;
 2170   
 2171       ExecutingJobsManager() {
 2172       }
 2173   
 2174       public String getName() {
 2175           return getClass().getName();
 2176       }
 2177   
 2178       public int getNumJobsCurrentlyExecuting() {
 2179           synchronized (executingJobs) {
 2180               return executingJobs.size();
 2181           }
 2182       }
 2183   
 2184       public void jobToBeExecuted(JobExecutionContext context) {
 2185           numJobsFired++;
 2186   
 2187           synchronized (executingJobs) {
 2188               executingJobs
 2189                       .put(context.getTrigger().getFireInstanceId(), context);
 2190           }
 2191       }
 2192   
 2193       public void jobWasExecuted(JobExecutionContext context,
 2194               JobExecutionException jobException) {
 2195           synchronized (executingJobs) {
 2196               executingJobs.remove(context.getTrigger().getFireInstanceId());
 2197           }
 2198       }
 2199   
 2200       public int getNumJobsFired() {
 2201           return numJobsFired;
 2202       }
 2203   
 2204       public List getExecutingJobs() {
 2205           synchronized (executingJobs) {
 2206               return java.util.Collections.unmodifiableList(new ArrayList(
 2207                       executingJobs.values()));
 2208           }
 2209       }
 2210   
 2211       public void jobExecutionVetoed(JobExecutionContext context) {
 2212           
 2213       }
 2214   }

Save This Page
Home » quartz-1.6.0 » org » quartz » core » [javadoc | source]