Commits

Anonymous committed 0581b2f
  • Participants
  • Parent commits 3253569

Comments (0)

Files changed (9)

File quartz_1x/releaseNotes.txt

 
 1- Copyright and license of Quartz switched to OpenSymphony
 
-2- 
+2- Dropped compatibility with Java 1.3.  Java 1.4 is now required.
    
 3- Removed previously deprecated org.quartz.helpers.NoOpJob.  It was replaced 
    by org.quartz.jobs.NoOpJob

File quartz_1x/src/java/main/org/quartz/helpers/TriggerUtils.java

 import java.util.Date;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.TimeZone;
 
 import org.quartz.CronTrigger;
 import org.quartz.Scheduler;
         return java.util.Collections.unmodifiableList(lst);
     }
 
-
-// NOT JDK 1.3 compatable.    
-
-//      Translate a date & time from a users timezone to the another
-//      (probably server) timezone to assist in creating a simple trigger with 
-//      the right date & time.
-/*    
+    /**
+     * Translate a date & time from a users timezone to the another
+     * (probably server) timezone to assist in creating a simple trigger with 
+     * the right date & time.
+     */      
     public static Date translateTime(Date date, TimeZone src, TimeZone dest) {
 
         Date newDate = new Date();
         return newDate;
 
     }
-*/    
+    
 }

File quartz_1x/src/java/main/org/quartz/impl/jdbcjobstore/oracle/WebLogicOracleDelegate.java

 /*
- * Copyright James House (c) 2001-2004
- * 
+ * Copyright (c) 2004-2005 by OpenSymphony
  * All rights reserved.
  * 
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met: 1.
- * Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer. 2. Redistributions in
- * binary form must reproduce the above copyright notice, this list of
- * conditions and the following disclaimer in the documentation and/or other
- * materials provided with the distribution.
- * 
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *  
+ * Previously Copyright (c) 2001-2004 James House
  */
 package org.quartz.impl.jdbcjobstore.oracle;
 

File quartz_1x/src/java/main/org/quartz/spi/ClassLoadHelper.java

+/*
+ * Copyright (c) 2004-2005 by OpenSymphony
+ * All rights reserved.
+ * 
+ * Previously Copyright (c) 2001-2004 James House
+ */
+package org.quartz.spi;
+
+/**
+ * An interface for classes wishing to provide the service of loading classes
+ * within the scheduler...
+ * 
+ * @author jhouse
+ */
+public interface ClassLoadHelper {
+
+    /*
+     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+     * 
+     * Interface.
+     * 
+     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+     */
+
+    /**
+     * Called to give the ClassLoadHelper a chance to initialize itself,
+     * including the oportunity to "steal" the class loader off of the calling
+     * thread, which is the thread that is initializing Quartz.
+     */
+    public void initialize();
+
+    /**
+     * Return the class with the given name.
+     */
+    public Class loadClass(String name) throws ClassNotFoundException;
+
+}

File quartz_1x/src/java/main/org/quartz/xml/CalendarBundle.java

+/*
+ * Copyright (c) 2004-2005 by OpenSymphony
+ * All rights reserved.
+ * 
+ * Previously Copyright (c) 2001-2004 James House 
+ * and Copyright Third Eye Consulting, Inc. (c) 2004
+ */
+package org.quartz.xml;
+
+import org.quartz.Calendar;
+
+/**
+ * Wraps a <code>Calendar</code>.
+ * 
+ * @author <a href="mailto:bonhamcm@thirdeyeconsulting.com">Chris Bonham</a>
+ */
+public class CalendarBundle implements Calendar {
+    /*
+     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+     * 
+     * Data members.
+     * 
+     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+     */
+     
+    protected String calendarName;
+
+    protected String className;
+    
+    protected Calendar calendar;
+    
+    protected boolean replace;
+
+    /*
+     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+     * 
+     * Constructors.
+     * 
+     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+     */
+
+    public CalendarBundle() {
+    }
+
+    /*
+     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+     * 
+     * Interface.
+     * 
+     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+     */
+
+    public String getCalendarName() {
+        return calendarName;
+    }
+
+    public void setCalendarName(String calendarName) {
+        this.calendarName = calendarName;
+    }
+    
+    public String getClassName() {
+        return className;
+    }
+
+    public void setClassName(String className)
+        throws ClassNotFoundException, InstantiationException, IllegalAccessException {
+        this.className = className;
+        createCalendar();
+    }
+
+    public Calendar getCalendar() {
+        return calendar;
+    }
+
+    public void setCalendar(Calendar calendar) {
+        this.calendar = calendar;
+    }
+    
+    public boolean getReplace() {
+        return replace;
+    }
+    
+    public void setReplace(boolean replace) {
+        this.replace = replace;
+    }
+    
+    public Calendar getBaseCalendar() {
+        return calendar.getBaseCalendar();
+    }
+    
+    public void setBaseCalendar(Calendar baseCalendar) {
+        if (baseCalendar instanceof CalendarBundle) {
+            baseCalendar = ((CalendarBundle)baseCalendar).getCalendar();
+        }
+        calendar.setBaseCalendar(baseCalendar);
+    }
+    
+    public String getDescription() {
+        return calendar.getDescription();
+    }
+
+    public void setDescription(String description) {
+        calendar.setDescription(description);
+    }
+    
+    public boolean isTimeIncluded(long timeStamp) {
+        return calendar.isTimeIncluded(timeStamp);
+    }
+
+    public long getNextIncludedTime(long timeStamp) {
+        return calendar.getNextIncludedTime(timeStamp);
+    }
+    
+    protected void createCalendar()
+        throws ClassNotFoundException, InstantiationException, IllegalAccessException {
+        Class clazz = Thread.currentThread().getContextClassLoader().loadClass(getClassName());
+        setCalendar((Calendar)clazz.newInstance());
+    }
+}

File quartz_1x/src/java/plugins/org/quartz/plugins/history/LoggingJobHistoryPlugin.java

+/*
+ * Copyright (c) 2004-2005 by OpenSymphony
+ * All rights reserved.
+ * 
+ * Previously Copyright (c) 2001-2004 James House
+ */
+package org.quartz.plugins.history;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+import org.quartz.Scheduler;
+import org.quartz.SchedulerException;
+import org.quartz.Trigger;
+import org.quartz.JobListener;
+import org.quartz.spi.SchedulerPlugin;
+
+import java.text.MessageFormat;
+
+/**
+ * Logs a history of all job executions (and execution vetos) via the 
+ * Jakarta Commons-Logging framework.
+ * 
+ * <p>
+ * The logged message is customizable by setting one of the following message
+ * properties to a String that conforms to the syntax of <code>java.util.MessageFormat</code>.
+ * </p>
+ * 
+ * <p>
+ * JobToBeFiredMessage - available message data are: <table>
+ * <tr>
+ * <th>Element</th>
+ * <th>Data Type</th>
+ * <th>Description</th>
+ * </tr>
+ * <tr>
+ * <td>0</td>
+ * <td>String</td>
+ * <td>The Job's Name.</td>
+ * </tr>
+ * <tr>
+ * <td>1</td>
+ * <td>String</td>
+ * <td>The Job's Group.</td>
+ * </tr>
+ * <tr>
+ * <td>2</td>
+ * <td>Date</td>
+ * <td>The current time.</td>
+ * </tr>
+ * <tr>
+ * <td>3</td>
+ * <td>String</td>
+ * <td>The Trigger's name.</td>
+ * </tr>
+ * <tr>
+ * <td>4</td>
+ * <td>String</td>
+ * <td>The Triggers's group.</td>
+ * </tr>
+ * <tr>
+ * <td>5</td>
+ * <td>Date</td>
+ * <td>The scheduled fire time.</td>
+ * </tr>
+ * <tr>
+ * <td>6</td>
+ * <td>Date</td>
+ * <td>The next scheduled fire time.</td>
+ * </tr>
+ * <tr>
+ * <td>7</td>
+ * <td>Integer</td>
+ * <td>The re-fire count from the JobExecutionContext.</td>
+ * </tr>
+ * </table>
+ * 
+ * The default message text is <i>"Job {1}.{0} fired (by trigger {4}.{3}) at:
+ * {2, date, HH:mm:ss MM/dd/yyyy}"</i>
+ * </p>
+ * 
+ * 
+ * <p>
+ * JobSuccessMessage - available message data are: <table>
+ * <tr>
+ * <th>Element</th>
+ * <th>Data Type</th>
+ * <th>Description</th>
+ * </tr>
+ * <tr>
+ * <td>0</td>
+ * <td>String</td>
+ * <td>The Job's Name.</td>
+ * </tr>
+ * <tr>
+ * <td>1</td>
+ * <td>String</td>
+ * <td>The Job's Group.</td>
+ * </tr>
+ * <tr>
+ * <td>2</td>
+ * <td>Date</td>
+ * <td>The current time.</td>
+ * </tr>
+ * <tr>
+ * <td>3</td>
+ * <td>String</td>
+ * <td>The Trigger's name.</td>
+ * </tr>
+ * <tr>
+ * <td>4</td>
+ * <td>String</td>
+ * <td>The Triggers's group.</td>
+ * </tr>
+ * <tr>
+ * <td>5</td>
+ * <td>Date</td>
+ * <td>The scheduled fire time.</td>
+ * </tr>
+ * <tr>
+ * <td>6</td>
+ * <td>Date</td>
+ * <td>The next scheduled fire time.</td>
+ * </tr>
+ * <tr>
+ * <td>7</td>
+ * <td>Integer</td>
+ * <td>The re-fire count from the JobExecutionContext.</td>
+ * </tr>
+ * <tr>
+ * <td>8</td>
+ * <td>Object</td>
+ * <td>The string value (toString() having been called) of the result (if any) 
+ *      that the Job set on the JobExecutionContext, with on it.  "NULL" if no 
+ *      result was set.</td>
+ * </td>
+ * </tr>
+ * </table>
+ * 
+ * The default message text is <i>"Job {1}.{0} execution complete at {2, date,
+ * HH:mm:ss MM/dd/yyyy} and reports: {8}"</i>
+ * </p>
+ * 
+ * <p>
+ * JobFailedMessage - available message data are: <table>
+ * <tr>
+ * <th>Element</th>
+ * <th>Data Type</th>
+ * <th>Description</th>
+ * </tr>
+ * <tr>
+ * <td>0</td>
+ * <td>String</td>
+ * <td>The Job's Name.</td>
+ * </tr>
+ * <tr>
+ * <td>1</td>
+ * <td>String</td>
+ * <td>The Job's Group.</td>
+ * </tr>
+ * <tr>
+ * <td>2</td>
+ * <td>Date</td>
+ * <td>The current time.</td>
+ * </tr>
+ * <tr>
+ * <td>3</td>
+ * <td>String</td>
+ * <td>The Trigger's name.</td>
+ * </tr>
+ * <tr>
+ * <td>4</td>
+ * <td>String</td>
+ * <td>The Triggers's group.</td>
+ * </tr>
+ * <tr>
+ * <td>5</td>
+ * <td>Date</td>
+ * <td>The scheduled fire time.</td>
+ * </tr>
+ * <tr>
+ * <td>6</td>
+ * <td>Date</td>
+ * <td>The next scheduled fire time.</td>
+ * </tr>
+ * <tr>
+ * <td>7</td>
+ * <td>Integer</td>
+ * <td>The re-fire count from the JobExecutionContext.</td>
+ * </tr>
+ * <tr>
+ * <td>8</td>
+ * <td>String</td>
+ * <td>The message from the thrown JobExecution Exception.
+ * </td>
+ * </tr>
+ * </table>
+ * 
+ * The default message text is <i>"Job {1}.{0} execution failed at {2, date,
+ * HH:mm:ss MM/dd/yyyy} and reports: {8}"</i>
+ * </p>
+ * 
+ * 
+ * <p>
+ * JobWasVetoedMessage - available message data are: <table>
+ * <tr>
+ * <th>Element</th>
+ * <th>Data Type</th>
+ * <th>Description</th>
+ * </tr>
+ * <tr>
+ * <td>0</td>
+ * <td>String</td>
+ * <td>The Job's Name.</td>
+ * </tr>
+ * <tr>
+ * <td>1</td>
+ * <td>String</td>
+ * <td>The Job's Group.</td>
+ * </tr>
+ * <tr>
+ * <td>2</td>
+ * <td>Date</td>
+ * <td>The current time.</td>
+ * </tr>
+ * <tr>
+ * <td>3</td>
+ * <td>String</td>
+ * <td>The Trigger's name.</td>
+ * </tr>
+ * <tr>
+ * <td>4</td>
+ * <td>String</td>
+ * <td>The Triggers's group.</td>
+ * </tr>
+ * <tr>
+ * <td>5</td>
+ * <td>Date</td>
+ * <td>The scheduled fire time.</td>
+ * </tr>
+ * <tr>
+ * <td>6</td>
+ * <td>Date</td>
+ * <td>The next scheduled fire time.</td>
+ * </tr>
+ * <tr>
+ * <td>7</td>
+ * <td>Integer</td>
+ * <td>The re-fire count from the JobExecutionContext.</td>
+ * </tr>
+ * </table>
+ * 
+ * The default message text is <i>"Job {1}.{0} was vetoed.  It was to be fired 
+ * (by trigger {4}.{3}) at: {2, date, HH:mm:ss MM/dd/yyyy}"</i>
+ * </p>
+ * 
+ * 
+ * @author James House
+ */
+public class LoggingJobHistoryPlugin implements SchedulerPlugin, JobListener {
+
+    /*
+     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+     * 
+     * Data members.
+     * 
+     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+     */
+
+    private String name;
+
+    private String jobToBeFiredMessage = "Job {1}.{0} fired (by trigger {4}.{3}) at: {2, date, HH:mm:ss MM/dd/yyyy}";
+    
+    private String jobSuccessMessage = "Job {1}.{0} execution complete at {2, date, HH:mm:ss MM/dd/yyyy} and reports: {8}";
+
+    private String jobFailedMessage = "Job {1}.{0} execution failed at {2, date, HH:mm:ss MM/dd/yyyy} and reports: {8}";
+
+    private String jobWasVetoedMessage = "Job {1}.{0} was vetoed.  It was to be fired (by trigger {4}.{3}) at: {2, date, HH:mm:ss MM/dd/yyyy}";
+    
+    /*
+     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+     * 
+     * Constructors.
+     * 
+     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+     */
+
+    public LoggingJobHistoryPlugin() {
+    }
+
+    /*
+     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+     * 
+     * Interface.
+     * 
+     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+     */
+
+    protected Log getLog() {
+        return LogFactory.getLog(LoggingJobHistoryPlugin.class);
+    }
+
+    /**
+     * Get the message that is logged when a Job successfully completes its 
+     * execution.
+     * 
+     * @return String
+     */
+    public String getJobSuccessMessage() {
+        return jobSuccessMessage;
+    }
+
+    /**
+     * Get the message that is logged when a Job fails its 
+     * execution.
+     * 
+     * @return String
+     */
+    public String getJobFailedMessage() {
+        return jobFailedMessage;
+    }
+
+    /**
+     * Get the message that is logged when a Job is about to execute.
+     * 
+     * @return String
+     */
+    public String getJobToBeFiredMessage() {
+        return jobToBeFiredMessage;
+    }
+
+    /**
+     * Set the message that is logged when a Job successfully completes its 
+     * execution.
+     * 
+     * @param jobCompleteMessage
+     *          String in java.text.MessageFormat syntax.
+     */
+    public void setJobSuccessMessage(String jobSuccessMessage) {
+        this.jobSuccessMessage = jobSuccessMessage;
+    }
+
+    /**
+     * Set the message that is logged when a Job fails its 
+     * execution.
+     * 
+     * @param jobCompleteMessage
+     *          String in java.text.MessageFormat syntax.
+     */
+    public void setJobFailedMessage(String jobFailedMessage) {
+        this.jobFailedMessage = jobFailedMessage;
+    }
+
+    /**
+     * Set the message that is logged when a Job is about to execute.
+     * 
+     * @param jobToBeFiredMessage
+     *          String in java.text.MessageFormat syntax.
+     */
+    public void setJobToBeFiredMessage(String jobToBeFiredMessage) {
+        this.jobToBeFiredMessage = jobToBeFiredMessage;
+    }
+
+    /**
+     * Get the message that is logged when a Job execution is vetoed by a
+     * trigger listener.
+     * 
+     * @return String
+     */
+    public String getJobWasVetoedMessage() {
+        return jobWasVetoedMessage;
+    }
+
+    /**
+     * Set the message that is logged when a Job execution is vetoed by a
+     * trigger listener.
+     * 
+     * @param jobToBeFiredMessage
+     *          String in java.text.MessageFormat syntax.
+     */
+    public void setJobWasVetoedMessage(String jobWasVetoedMessage) {
+        this.jobWasVetoedMessage = jobWasVetoedMessage;
+    }
+
+    /*
+     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+     * 
+     * SchedulerPlugin Interface.
+     * 
+     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+     */
+
+    /**
+     * <p>
+     * Called during creation of the <code>Scheduler</code> in order to give
+     * the <code>SchedulerPlugin</code> a chance to initialize.
+     * </p>
+     * 
+     * @throws SchedulerConfigException
+     *           if there is an error initializing.
+     */
+    public void initialize(String name, Scheduler scheduler)
+            throws SchedulerException {
+        this.name = name;
+        scheduler.addGlobalJobListener(this);
+    }
+
+    public void start() {
+        // do nothing...
+    }
+
+    /**
+     * <p>
+     * Called in order to inform the <code>SchedulerPlugin</code> that it
+     * should free up all of it's resources because the scheduler is shutting
+     * down.
+     * </p>
+     */
+    public void shutdown() {
+        // nothing to do...
+    }
+
+    /*
+     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+     * 
+     * JobListener Interface.
+     * 
+     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+     */
+
+    /*
+     * Object[] arguments = { new Integer(7), new
+     * Date(System.currentTimeMillis()), "a disturbance in the Force" };
+     * 
+     * String result = MessageFormat.format( "At {1,time} on {1,date}, there
+     * was {2} on planet {0,number,integer}.", arguments);
+     */
+
+    public String getName() {
+        return name;
+    }
+
+    /** 
+     * @see org.quartz.JobListener#jobToBeExecuted(JobExecutionContext)
+     */
+    public void jobToBeExecuted(JobExecutionContext context) {
+        if (!getLog().isInfoEnabled()) {
+            return;
+        } 
+        
+        Trigger trigger = context.getTrigger();
+
+        Object[] args = {context.getJobDetail().getName(),
+                context.getJobDetail().getGroup(), new java.util.Date(),
+                trigger.getName(), trigger.getGroup(),
+                trigger.getPreviousFireTime(), trigger.getNextFireTime(),
+                new Integer(context.getRefireCount())};
+
+        getLog().info(MessageFormat.format(getJobToBeFiredMessage(), args));
+    }
+    
+    /** 
+     * @see org.quartz.JobListener#jobWasExecuted(JobExecutionContext, JobExecutionException)
+     */
+    public void jobWasExecuted(JobExecutionContext context,
+            JobExecutionException jobException) {
+
+        Trigger trigger = context.getTrigger();
+        
+        Object[] args = null;
+        
+        if (jobException != null) {
+            if (!getLog().isWarnEnabled()) {
+                return;
+            } 
+            
+            String errMsg = jobException.getMessage();
+            args = new Object[]{context.getJobDetail().getName(),
+                    context.getJobDetail().getGroup(), new java.util.Date(),
+                    trigger.getName(), trigger.getGroup(),
+                    trigger.getPreviousFireTime(), trigger.getNextFireTime(),
+                    new Integer(context.getRefireCount()), errMsg};
+            
+            getLog().warn(MessageFormat.format(getJobFailedMessage(), args), jobException); 
+        }
+        else {
+            if (!getLog().isInfoEnabled()) {
+                return;
+            } 
+            
+            String result = String.valueOf(context.getResult());
+            args = new Object[]{context.getJobDetail().getName(),
+                    context.getJobDetail().getGroup(), new java.util.Date(),
+                    trigger.getName(), trigger.getGroup(),
+                    trigger.getPreviousFireTime(), trigger.getNextFireTime(),
+                    new Integer(context.getRefireCount()), result};
+            
+            getLog().info(MessageFormat.format(getJobSuccessMessage(), args));
+        }
+    }
+
+    /** 
+     * @see org.quartz.JobListener#jobExecutionVetoed(org.quartz.JobExecutionContext)
+     */
+    public void jobExecutionVetoed(JobExecutionContext context) {
+        
+        if (!getLog().isInfoEnabled()) {
+            return;
+        } 
+        
+        Trigger trigger = context.getTrigger();
+
+        Object[] args = {context.getJobDetail().getName(),
+                context.getJobDetail().getGroup(), new java.util.Date(),
+                trigger.getName(), trigger.getGroup(),
+                trigger.getPreviousFireTime(), trigger.getNextFireTime(),
+                new Integer(context.getRefireCount())};
+
+        getLog().info(MessageFormat.format(getJobWasVetoedMessage(), args));
+    }
+
+}
+
+// EOF

File quartz_1x/src/java/plugins/org/quartz/plugins/history/LoggingTriggerHistoryPlugin.java

+/*
+ * Copyright (c) 2004-2005 by OpenSymphony
+ * All rights reserved.
+ * 
+ * Previously Copyright (c) 2001-2004 James House
+ */
+package org.quartz.plugins.history;
+
+import java.text.MessageFormat;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.quartz.JobExecutionContext;
+import org.quartz.Scheduler;
+import org.quartz.SchedulerException;
+import org.quartz.Trigger;
+import org.quartz.TriggerListener;
+import org.quartz.spi.SchedulerPlugin;
+
+/**
+ * Logs a history of all trigger firings via the Jakarta Commons-Logging
+ * framework.
+ * 
+ * <p>
+ * The logged message is customizable by setting one of the following message
+ * properties to a String that conforms to the syntax of <code>java.util.MessageFormat</code>.
+ * </p>
+ * 
+ * <p>
+ * TriggerFiredMessage - available message data are: <table>
+ * <tr>
+ * <th>Element</th>
+ * <th>Data Type</th>
+ * <th>Description</th>
+ * </tr>
+ * <tr>
+ * <td>0</td>
+ * <td>String</td>
+ * <td>The Trigger's Name.</td>
+ * </tr>
+ * <tr>
+ * <td>1</td>
+ * <td>String</td>
+ * <td>The Trigger's Group.</td>
+ * </tr>
+ * <tr>
+ * <td>2</td>
+ * <td>Date</td>
+ * <td>The scheduled fire time.</td>
+ * </tr>
+ * <tr>
+ * <td>3</td>
+ * <td>Date</td>
+ * <td>The next scheduled fire time.</td>
+ * </tr>
+ * <tr>
+ * <td>4</td>
+ * <td>Date</td>
+ * <td>The actual fire time.</td>
+ * </tr>
+ * <tr>
+ * <td>5</td>
+ * <td>String</td>
+ * <td>The Job's name.</td>
+ * </tr>
+ * <tr>
+ * <td>6</td>
+ * <td>String</td>
+ * <td>The Job's group.</td>
+ * </tr>
+ * <tr>
+ * <td>7</td>
+ * <td>Integer</td>
+ * <td>The re-fire count from the JobExecutionContext.</td>
+ * </tr>
+ * </table>
+ * 
+ * The default message text is <i>"Trigger {1}.{0} fired job {6}.{5} at: {4,
+ * date, HH:mm:ss MM/dd/yyyy}"</i>
+ * </p>
+ * 
+ * <p>
+ * TriggerMisfiredMessage - available message data are: <table>
+ * <tr>
+ * <th>Element</th>
+ * <th>Data Type</th>
+ * <th>Description</th>
+ * </tr>
+ * <tr>
+ * <td>0</td>
+ * <td>String</td>
+ * <td>The Trigger's Name.</td>
+ * </tr>
+ * <tr>
+ * <td>1</td>
+ * <td>String</td>
+ * <td>The Trigger's Group.</td>
+ * </tr>
+ * <tr>
+ * <td>2</td>
+ * <td>Date</td>
+ * <td>The scheduled fire time.</td>
+ * </tr>
+ * <tr>
+ * <td>3</td>
+ * <td>Date</td>
+ * <td>The next scheduled fire time.</td>
+ * </tr>
+ * <tr>
+ * <td>4</td>
+ * <td>Date</td>
+ * <td>The actual fire time. (the time the misfire was detected/handled)</td>
+ * </tr>
+ * <tr>
+ * <td>5</td>
+ * <td>String</td>
+ * <td>The Job's name.</td>
+ * </tr>
+ * <tr>
+ * <td>6</td>
+ * <td>String</td>
+ * <td>The Job's group.</td>
+ * </tr>
+ * </table>
+ * 
+ * The default message text is <i>"Trigger {1}.{0} misfired job {6}.{5} at:
+ * {4, date, HH:mm:ss MM/dd/yyyy}. Should have fired at: {3, date, HH:mm:ss
+ * MM/dd/yyyy}"</i>
+ * </p>
+ * 
+ * <p>
+ * TriggerCompleteMessage - available message data are: <table>
+ * <tr>
+ * <th>Element</th>
+ * <th>Data Type</th>
+ * <th>Description</th>
+ * </tr>
+ * <tr>
+ * <td>0</td>
+ * <td>String</td>
+ * <td>The Trigger's Name.</td>
+ * </tr>
+ * <tr>
+ * <td>1</td>
+ * <td>String</td>
+ * <td>The Trigger's Group.</td>
+ * </tr>
+ * <tr>
+ * <td>2</td>
+ * <td>Date</td>
+ * <td>The scheduled fire time.</td>
+ * </tr>
+ * <tr>
+ * <td>3</td>
+ * <td>Date</td>
+ * <td>The next scheduled fire time.</td>
+ * </tr>
+ * <tr>
+ * <td>4</td>
+ * <td>Date</td>
+ * <td>The job completion time.</td>
+ * </tr>
+ * <tr>
+ * <td>5</td>
+ * <td>String</td>
+ * <td>The Job's name.</td>
+ * </tr>
+ * <tr>
+ * <td>6</td>
+ * <td>String</td>
+ * <td>The Job's group.</td>
+ * </tr>
+ * <tr>
+ * <td>7</td>
+ * <td>Integer</td>
+ * <td>The re-fire count from the JobExecutionContext.</td>
+ * </tr>
+ * <tr>
+ * <td>8</td>
+ * <td>Integer</td>
+ * <td>The trigger's resulting instruction code.</td>
+ * </tr>
+ * <tr>
+ * <td>9</td>
+ * <td>String</td>
+ * <td>A human-readable translation of the trigger's resulting instruction
+ * code.</td>
+ * </tr>
+ * </table>
+ * 
+ * The default message text is <i>"Trigger {1}.{0} completed firing job
+ * {6}.{5} at {4, date, HH:mm:ss MM/dd/yyyy} with resulting trigger instruction
+ * code: {9}"</i>
+ * </p>
+ * 
+ * @author James House
+ */
+public class LoggingTriggerHistoryPlugin implements SchedulerPlugin,
+        TriggerListener {
+
+    /*
+     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+     * 
+     * Data members.
+     * 
+     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+     */
+
+    private String name;
+
+    private String triggerFiredMessage = "Trigger {1}.{0} fired job {6}.{5} at: {4, date, HH:mm:ss MM/dd/yyyy}";
+
+    private String triggerMisfiredMessage = "Trigger {1}.{0} misfired job {6}.{5}  at: {4, date, HH:mm:ss MM/dd/yyyy}.  Should have fired at: {3, date, HH:mm:ss MM/dd/yyyy}";
+
+    private String triggerCompleteMessage = "Trigger {1}.{0} completed firing job {6}.{5} at {4, date, HH:mm:ss MM/dd/yyyy} with resulting trigger instruction code: {9}";
+
+    /*
+     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+     * 
+     * Constructors.
+     * 
+     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+     */
+
+    public LoggingTriggerHistoryPlugin() {
+    }
+
+    /*
+     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+     * 
+     * Interface.
+     * 
+     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+     */
+
+    protected Log getLog() {
+        return LogFactory.getLog(LoggingTriggerHistoryPlugin.class);
+    }
+
+    /**
+     * Get the message that is printed upon the completion of a trigger's
+     * firing.
+     * 
+     * @return String
+     */
+    public String getTriggerCompleteMessage() {
+        return triggerCompleteMessage;
+    }
+
+    /**
+     * Get the message that is printed upon a trigger's firing.
+     * 
+     * @return String
+     */
+    public String getTriggerFiredMessage() {
+        return triggerFiredMessage;
+    }
+
+    /**
+     * Get the message that is printed upon a trigger's mis-firing.
+     * 
+     * @return String
+     */
+    public String getTriggerMisfiredMessage() {
+        return triggerMisfiredMessage;
+    }
+
+    /**
+     * Set the message that is printed upon the completion of a trigger's
+     * firing.
+     * 
+     * @param triggerCompleteMessage
+     *          String in java.text.MessageFormat syntax.
+     */
+    public void setTriggerCompleteMessage(String triggerCompleteMessage) {
+        this.triggerCompleteMessage = triggerCompleteMessage;
+    }
+
+    /**
+     * Set the message that is printed upon a trigger's firing.
+     * 
+     * @param triggerFiredMessage
+     *          String in java.text.MessageFormat syntax.
+     */
+    public void setTriggerFiredMessage(String triggerFiredMessage) {
+        this.triggerFiredMessage = triggerFiredMessage;
+    }
+
+    /**
+     * Set the message that is printed upon a trigger's firing.
+     * 
+     * @param triggerMisfiredMessage
+     *          String in java.text.MessageFormat syntax.
+     */
+    public void setTriggerMisfiredMessage(String triggerMisfiredMessage) {
+        this.triggerMisfiredMessage = triggerMisfiredMessage;
+    }
+
+    /*
+     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+     * 
+     * SchedulerPlugin Interface.
+     * 
+     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+     */
+
+    /**
+     * <p>
+     * Called during creation of the <code>Scheduler</code> in order to give
+     * the <code>SchedulerPlugin</code> a chance to initialize.
+     * </p>
+     * 
+     * @throws SchedulerConfigException
+     *           if there is an error initializing.
+     */
+    public void initialize(String name, Scheduler scheduler)
+            throws SchedulerException {
+        this.name = name;
+
+        scheduler.addGlobalTriggerListener(this);
+    }
+
+    public void start() {
+        // do nothing...
+    }
+
+    /**
+     * <p>
+     * Called in order to inform the <code>SchedulerPlugin</code> that it
+     * should free up all of it's resources because the scheduler is shutting
+     * down.
+     * </p>
+     */
+    public void shutdown() {
+        // nothing to do...
+    }
+
+    /*
+     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+     * 
+     * TriggerListener Interface.
+     * 
+     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+     */
+
+    /*
+     * Object[] arguments = { new Integer(7), new
+     * Date(System.currentTimeMillis()), "a disturbance in the Force" };
+     * 
+     * String result = MessageFormat.format( "At {1,time} on {1,date}, there
+     * was {2} on planet {0,number,integer}.", arguments);
+     */
+
+    public String getName() {
+        return name;
+    }
+
+    public void triggerFired(Trigger trigger, JobExecutionContext context) {
+        if (!getLog().isInfoEnabled()) {
+            return;
+        } 
+        
+        Object[] args = {trigger.getName(), trigger.getGroup(),
+                trigger.getPreviousFireTime(), trigger.getNextFireTime(),
+                new java.util.Date(), context.getJobDetail().getName(),
+                context.getJobDetail().getGroup(),
+                new Integer(context.getRefireCount())};
+
+        getLog().info(MessageFormat.format(getTriggerFiredMessage(), args));
+    }
+
+    public void triggerMisfired(Trigger trigger) {
+        if (!getLog().isInfoEnabled()) {
+            return;
+        } 
+        
+        Object[] args = {trigger.getName(), trigger.getGroup(),
+                trigger.getPreviousFireTime(), trigger.getNextFireTime(),
+                new java.util.Date(), trigger.getJobGroup(),
+                trigger.getJobGroup(),};
+
+        getLog().info(MessageFormat.format(getTriggerMisfiredMessage(), args));
+    }
+
+    public void triggerComplete(Trigger trigger, JobExecutionContext context,
+            int triggerInstructionCode) {
+        if (!getLog().isInfoEnabled()) {
+            return;
+        } 
+        
+        String instrCode = "UNKNOWN";
+        if (triggerInstructionCode == Trigger.INSTRUCTION_DELETE_TRIGGER) instrCode = "DELETE TRIGGER";
+        else if (triggerInstructionCode == Trigger.INSTRUCTION_NOOP) instrCode = "DO NOTHING";
+        else if (triggerInstructionCode == Trigger.INSTRUCTION_RE_EXECUTE_JOB) instrCode = "RE-EXECUTE JOB";
+        else if (triggerInstructionCode == Trigger.INSTRUCTION_SET_ALL_JOB_TRIGGERS_COMPLETE) instrCode = "SET ALL OF JOB'S TRIGGERS COMPLETE";
+        else if (triggerInstructionCode == Trigger.INSTRUCTION_SET_TRIGGER_COMPLETE)
+                instrCode = "SET THIS TRIGGER COMPLETE";
+
+        Object[] args = {trigger.getName(), trigger.getGroup(),
+                trigger.getPreviousFireTime(), trigger.getNextFireTime(),
+                new java.util.Date(), context.getJobDetail().getName(),
+                context.getJobDetail().getGroup(),
+                new Integer(context.getRefireCount()),
+                new Integer(triggerInstructionCode), instrCode};
+
+        getLog().info(MessageFormat.format(getTriggerCompleteMessage(), args));
+    }
+
+    public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context) {
+        return false;
+    }
+
+}

File quartz_1x/src/java/plugins/org/quartz/plugins/management/ShutdownHookPlugin.java

+/*
+ * Copyright (c) 2004-2005 by OpenSymphony
+ * All rights reserved.
+ * 
+ * Previously Copyright (c) 2001-2004 James House
+ */
+package org.quartz.plugins.management;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.quartz.Scheduler;
+import org.quartz.SchedulerException;
+import org.quartz.spi.SchedulerPlugin;
+
+/**
+ * This plugin catches the event of the JVM terminating (such as upon a CRTL-C)
+ * and tells the scheuler to shutdown.
+ * 
+ * @see org.quartz.Scheduler#shutdown(boolean)
+ * 
+ * @author James House
+ */
+public class ShutdownHookPlugin implements SchedulerPlugin {
+
+    /*
+     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+     * 
+     * Data members.
+     * 
+     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+     */
+
+    private String name;
+
+    private Scheduler scheduler;
+
+    private boolean cleanShutdown = true;
+
+    /*
+     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+     * 
+     * Constructors.
+     * 
+     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+     */
+
+    public ShutdownHookPlugin() {
+    }
+
+    /*
+     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+     * 
+     * Interface.
+     * 
+     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+     */
+
+    /**
+     * Determine whether or not the plug-in is configured to cause a clean
+     * shutdown of the scheduler.
+     * 
+     * <p>
+     * The default value is <code>true</code>.
+     * </p>
+     * 
+     * @see org.quartz.Scheduler#shutdown(boolean)
+     */
+    public boolean isCleanShutdown() {
+        return cleanShutdown;
+    }
+
+    /**
+     * Set whether or not the plug-in is configured to cause a clean shutdown
+     * of the scheduler.
+     * 
+     * <p>
+     * The default value is <code>true</code>.
+     * </p>
+     * 
+     * @see org.quartz.Scheduler#shutdown(boolean)
+     */
+    public void setCleanShutdown(boolean b) {
+        cleanShutdown = b;
+    }
+
+    protected static Log getLog() {
+        return LogFactory.getLog(ShutdownHookPlugin.class);
+    }
+
+    /*
+     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+     * 
+     * SchedulerPlugin Interface.
+     * 
+     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+     */
+
+    /**
+     * <p>
+     * Called during creation of the <code>Scheduler</code> in order to give
+     * the <code>SchedulerPlugin</code> a chance to initialize.
+     * </p>
+     * 
+     * @throws SchedulerConfigException
+     *           if there is an error initializing.
+     */
+    public void initialize(String name, final Scheduler scheduler)
+            throws SchedulerException {
+        this.name = name;
+        this.scheduler = scheduler;
+
+        getLog().info("Registering Quartz shutdown hook.");
+
+        Thread t = new Thread("Quartz Shutdown-Hook "
+                + scheduler.getSchedulerName()) {
+            public void run() {
+                getLog().info("Shutting down Quartz...");
+                try {
+                    scheduler.shutdown(true);
+                } catch (SchedulerException e) {
+                    getLog().info(
+                            "Error shutting down Quartz: " + e.getMessage(), e);
+                }
+            }
+        };
+
+        Runtime.getRuntime().addShutdownHook(t);
+    }
+
+    public void start() {
+        // do nothing.
+    }
+
+    /**
+     * <p>
+     * Called in order to inform the <code>SchedulerPlugin</code> that it
+     * should free up all of it's resources because the scheduler is shutting
+     * down.
+     * </p>
+     */
+    public void shutdown() {
+        // nothing to do in this case (since the scheduler is already shutting
+        // down)
+    }
+
+}
+
+// EOF

File quartz_1x/src/java/plugins/org/quartz/plugins/xml/JobInitializationPlugin.java

+/*
+ * Copyright (c) 2004-2005 by OpenSymphony
+ * All rights reserved.
+ * 
+ * Previously Copyright (c) 2001-2004 James House
+ */
+package org.quartz.plugins.xml;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Date;
+import java.net.URL;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.quartz.JobDetail;
+import org.quartz.Scheduler;
+import org.quartz.SchedulerException;
+import org.quartz.SimpleTrigger;
+import org.quartz.jobs.FileScanJob;
+import org.quartz.jobs.FileScanListener;
+import org.quartz.spi.SchedulerPlugin;
+
+import org.quartz.xml.*;
+
+/**
+* This plugin loads an XML file to add jobs and schedule them with triggers
+ * as the scheduler is initialized, and can optionally periodically scan the
+ * file for changes.
+ * 
+ * @author James House
+ * @author Pierre Awaragi
+ */
+public class JobInitializationPlugin implements SchedulerPlugin, FileScanListener {
+
+    /*
+     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+     * 
+     * Data members.
+     * 
+     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+     */
+
+    private String name;
+
+    private Scheduler scheduler;
+
+    private boolean overWriteExistingJobs = false;
+
+    private boolean failOnFileNotFound = true;
+
+    private boolean fileFound = false;
+
+    private String fileName = JobSchedulingDataProcessor.QUARTZ_XML_FILE_NAME;
+    
+    private String filePath = null;
+    
+    private boolean validating = true;
+    
+    private boolean validatingSchema = true;
+
+    private long scanInterval = 0; 
+    
+    boolean initializing = true;
+    
+    boolean started = false;
+    
+    /*
+     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+     * 
+     * Constructors.
+     * 
+     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+     */
+
+    public JobInitializationPlugin() {
+    }
+
+    /*
+     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+     * 
+     * Interface.
+     * 
+     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+     */
+
+    /**
+     * The file name (and path) to the XML file that should be read.
+     * 
+     * @return
+     */
+    public String getFileName() {
+        return fileName;
+    }
+
+    /**
+     * The file name (and path) to the XML file that should be read.
+     * 
+     * @param fileName
+     */
+    public void setFileName(String fileName) {
+        this.fileName = fileName;
+    }
+
+    /**
+     * Whether or not jobs defined in the XML file should be overwrite existing
+     * jobs with the same name.
+     * 
+     * @return
+     */
+    public boolean isOverWriteExistingJobs() {
+        return overWriteExistingJobs;
+    }
+
+    /**
+     * Whether or not jobs defined in the XML file should be overwrite existing
+     * jobs with the same name.
+     * 
+     * @param overWriteExistingJobs
+     */
+    public void setOverWriteExistingJobs(boolean overWriteExistingJobs) {
+        this.overWriteExistingJobs = overWriteExistingJobs;
+    }
+
+    /**
+     * The interval (in seconds) at which to scan for changes to the file.  
+     * If the file has been changed, it is re-loaded and parsed.   The default 
+     * value for the interval is 0, which disables scanning.
+     * 
+     * @return Returns the scanInterval.
+     */
+    public long getScanInterval() {
+        return scanInterval / 1000;
+    }
+
+    /**
+     * The interval (in seconds) at which to scan for changes to the file.  
+     * If the file has been changed, it is re-loaded and parsed.   The default 
+     * value for the interval is 0, which disables scanning.
+     * 
+     * @param scanInterval The scanInterval to set.
+     */
+    public void setScanInterval(long scanInterval) {
+        this.scanInterval = scanInterval * 1000;
+    }
+    
+    /**
+     * Whether or not initialization of the plugin should fail (throw an
+     * exception) if the file cannot be found. Default is <code>true</code>.
+     * 
+     * @return
+     */
+    public boolean isFailOnFileNotFound() {
+        return failOnFileNotFound;
+    }
+
+    /**
+     * Whether or not initialization of the plugin should fail (throw an
+     * exception) if the file cannot be found. Default is <code>true</code>.
+     * 
+     * @param overWriteExistingJobs
+     */
+    public void setFailOnFileNotFound(boolean failOnFileNotFound) {
+        this.failOnFileNotFound = failOnFileNotFound;
+    }
+    
+    /**
+     * Whether or not the XML should be validated. Default is <code>true</code>.
+     * 
+     * @return
+     */
+    public boolean isValidating() {
+        return validating;
+    }
+
+    /**
+     * Whether or not the XML should be validated. Default is <code>true</code>.
+     * 
+     * @param validating
+     */
+    public void setValidating(boolean validating) {
+        this.validating = validating;
+    }
+    
+    /**
+     * Whether or not the XML schema should be validated. Default is <code>true</code>.
+     * 
+     * @return
+     */
+    public boolean isValidatingSchema() {
+        return validatingSchema;
+    }
+
+    /**
+     * Whether or not the XML schema should be validated. Default is <code>true</code>.
+     * 
+     * @param validatingSchema
+     */
+    public void setValidatingSchema(boolean validatingSchema) {
+        this.validatingSchema = validatingSchema;
+    }
+
+    protected static Log getLog() {
+        return LogFactory.getLog(JobInitializationPlugin.class);
+    }
+
+    /*
+     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+     * 
+     * SchedulerPlugin Interface.
+     * 
+     * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+     */
+
+    /**
+     * <p>
+     * Called during creation of the <code>Scheduler</code> in order to give
+     * the <code>SchedulerPlugin</code> a chance to initialize.
+     * </p>
+     * 
+     * @throws SchedulerConfigException
+     *           if there is an error initializing.
+     */
+    public void initialize(String name, final Scheduler scheduler)
+            throws SchedulerException {
+        
+        initializing = true;
+        try {
+            this.name = name;
+            this.scheduler = scheduler;
+    
+            getLog().info("Registering Quartz Job Initialization Plug-in.");
+            
+            findFile();
+            
+            if(scanInterval > 0) {
+                SimpleTrigger trig = new SimpleTrigger(
+                        "JobInitializationPlugin_"+name, 
+                        "JobInitializationPlugin", 
+                        new Date(), null, 
+                        SimpleTrigger.REPEAT_INDEFINITELY, scanInterval);
+                trig.setVolatility(true);
+                JobDetail job = new JobDetail(
+                        "JobInitializationPlugin_"+name, 
+                        "JobInitializationPlugin",
+                        FileScanJob.class);
+                job.setVolatility(true);
+                job.getJobDataMap().put(FileScanJob.FILE_NAME, getFilePath());
+                job.getJobDataMap().put(FileScanJob.FILE_SCAN_LISTENER_NAME, "JobInitializationPlugin_"+name);
+                
+                scheduler.getContext().put("JobInitializationPlugin_"+name, this);
+                scheduler.scheduleJob(job, trig);
+            }
+        }
+        finally {
+            initializing = false;
+        }
+    }
+
+    private String getFilePath() throws SchedulerException {
+        if(this.filePath == null) {
+            findFile();         
+        }
+        return this.filePath;
+    }
+    
+    /**
+     * 
+     */
+    private void findFile() throws SchedulerException {
+        java.io.InputStream f = null;
+        
+        File file = new File(getFileName()); // files in filesystem
+        if (file == null || !file.exists()) {
+            // files in classpath
+            URL url = Thread.currentThread()
+                .getContextClassLoader()
+                .getResource(getFileName());
+            if(url != null) {
+                file = new File(url.getPath()); 
+            }
+        }        
+        try {              
+            f = new java.io.FileInputStream(file);
+        }catch (FileNotFoundException e) {
+            // ignore
+        }
+        
+        if (f == null && isFailOnFileNotFound()) {
+            throw new SchedulerException("File named '" + getFileName()
+                    + "' does not exist.");
+        } else if (f == null) {
+            getLog().warn("File named '" + getFileName() + "' does not exist.");
+        } else {
+            fileFound = true;
+            try {
+                this.filePath = file.getPath();
+                f.close();
+            } catch (IOException ioe) {
+                getLog()
+                        .warn("Error closing file named '" + getFileName(), ioe);
+            }
+        }
+    }
+
+    public void start() {
+        try {
+            processFile();
+        }
+        finally {
+            started = true;
+        }
+    }
+
+    /**
+     * <p>
+     * Called in order to inform the <code>SchedulerPlugin</code> that it
+     * should free up all of it's resources because the scheduler is shutting
+     * down.
+     * </p>
+     */
+    public void shutdown() {
+        // nothing to do
+    }
+
+    
+    public void processFile() {
+        if (!fileFound) return;
+
+        JobSchedulingDataProcessor processor = 
+            new JobSchedulingDataProcessor(isValidating(), isValidatingSchema());
+
+        try {
+            processor.processFileAndScheduleJobs(fileName, scheduler, true);
+        } catch (Exception e) {
+            getLog().error("Error scheduling jobs: " + e.getMessage(), e);
+        }
+    }
+
+    /** 
+     * @see org.quartz.jobs.FileScanListener#fileUpdated(java.lang.String)
+     */
+    public void fileUpdated(String fileName) {
+        if(started)
+            processFile();
+    }
+    
+}
+
+// EOF