Friday 14 November 2014

Spring 3 Quartz - Customize quartz to refresh database on application restart

Let's say one has defined quartz jobs and triggers in Spring's context XML and want to refresh quartz tables whenever web application restarts (say for refreshing triggers), here's a way to do it -

Define your own custom QuartzSchedular bean in Spring's context XML -

<bean id="schedBean" class="com.pearson.rel.service.quartz.QuartzSchedular">
<!-- All dependencies have to be defined here. -->
<property name="schedulerContextAsMap">
<map>
<entry key="platformNotificationManager" value-ref="platformNotificationManager"/>
<entry key="platformMediaManager" value-ref="platformMediaManager"/>
</map>
</property>
<property name="triggers">
       <list>
<ref bean="platformSyncTrigger"/>
<ref bean="platformSyncArchiveTrigger"/>
       </list>
</property>
        <property name="quartzProperties">
            <props>
            <!-- Can be any string, and the value has no meaning to the scheduler itself - but rather serves as a mechanism for
            client code to distinguish schedulers when multiple instances are used within the same program. If one is using the
            clustering features, one must use the same name for every instance in the cluster that is 'logically' the same Scheduler. -->
                <prop key="org.quartz.scheduler.instanceName">Scheduler</prop>
                <!-- Can be any string, but must be unique for all schedulers working as if they are the same 'logical' Scheduler
                within a cluster. One may use the value "AUTO" as the instanceId if one wish the Id to be generated. Or the value "SYS_PROP"
                if one want the value to come from the system property "org.quartz.scheduler.instanceId". -->
                <prop key="org.quartz.scheduler.instanceId">AUTO</prop>
                <!-- The the number of milliseconds the scheduler will 'tolerate' a trigger to pass its next-fire-time by, before being
                considered "misfired". The default value (if one don't make an entry of this property in his configuration) is 60000 (60 seconds). -->
                <prop key="org.quartz.jobStore.misfireThreshold">60000</prop>
                <!-- JDBCJobStore is used to store scheduling information (job, triggers and calendars) within a relational database.
                There are actually two seperate JDBCJobStore classes that one can select between, depending on the transactional behaviour one needs.
                JobStoreTX manages all transactions itself by calling commit() (or rollback()) on the database connection after every action
                (such as the addition of a job). JDBCJobStore is appropriate if one is using Quartz in a stand-alone application, or within a servlet
                container if the application is not using JTA transactions. -->
                <prop key="org.quartz.jobStore.class">org.quartz.impl.jdbcjobstore.JobStoreTX</prop>
                <!-- Driver delegates understand the particular 'dialects' of varies database systems.  -->
                <prop key="org.quartz.jobStore.driverDelegateClass">org.quartz.impl.jdbcjobstore.StdJDBCDelegate</prop>
                <!-- JDBCJobStore's "table prefix" property is a string equal to the prefix given to Quartz's tables that were created in the database.
                One can have multiple sets of Quartz's tables within the same database if they use different table prefixes. -->
                <prop key="org.quartz.jobStore.tablePrefix">QRTZ_</prop>
                <!-- Set to "true" in order to turn on clustering features. This property must be set to "true" if one is having multiple instances of
                Quartz use the same set of database tables... otherwise one will experience havoc. See the configuration docs for clustering for more information. -->
                <prop key="org.quartz.jobStore.isClustered">true</prop>
                <!-- Is the name of the ThreadPool implementation one wish to use. The threadpool that ships with Quartz is "org.quartz.simpl.SimpleThreadPool", and
                should meet the needs of nearly every user. It has very simple behavior and is very well tested. It provides a fixed-size pool of threads that 'live' the lifetime of the Scheduler. -->
                <prop key="org.quartz.threadPool.class">org.quartz.simpl.SimpleThreadPool</prop>
                <!-- Can be any positive integer, although one should realize that only numbers between 1 and 100 are very practical. This is the number of threads
                that are available for concurrent execution of jobs. If one only has a few jobs that fire  few times a day, then 1 thread is plenty! If one has tens of thousands of jobs,
                with many firing every minute, then one probably want a thread count more like 50 or 100 (this highly depends on the nature of the work that one's jobs perform, and one's systems resources!). -->
                <prop key="org.quartz.threadPool.threadCount">25</prop>
                <!-- Can be any int between Thread.MIN_PRIORITY (which is 1) and Thread.MAX_PRIORITY (which is 10). The default is Thread.NORM_PRIORITY (5). -->
                <prop key="org.quartz.threadPool.threadPriority">5</prop>
                <!-- The value of this property must be the name of one the DataSources defined in the configuration properties file. See the configuration docs for DataSources for more information. -->
                <prop key="org.quartz.jobStore.dataSource">relDS</prop>
<!-- DATASOURCE -->
<!-- Must be the java class name of the JDBC driver for the database. -->
                <prop key="org.quartz.dataSource.relDS.driver">${hibernate.connection.driver_class}</prop>
                <!-- The connection URL (host, port, etc.) for connection to the database. -->
                <prop key="org.quartz.dataSource.relDS.URL">${hibernate.connection.url}</prop>
                <!-- The user name to use when connecting to the database. -->
                <prop key="org.quartz.dataSource.relDS.user">${hibernate.connection.username}</prop>
                <!-- The password to use when connecting to the database. -->
                <prop key="org.quartz.dataSource.relDS.password">${hibernate.connection.password}</prop>
                <!-- The maximum number of connections that the DataSource can create in it's pool of connections. -->
                <prop key="org.quartz.dataSource.relDS.maxConnections">5</prop>
                <!-- Is an optional SQL query string that the DataSource can use to detect and replace failed/corrupt connections. For example an oracle user might choose "select table_name from user_tables" - which is a query that should never fail - unless the connection is actually bad. -->
                <prop key="org.quartz.dataSource.relDS.validationQuery">select 1 from dual</prop>
            </props>
      </property>
</bean>

Define the class QuartzSchedular -

public class QuartzSchedular extends SchedulerFactoryBean {
    /**
     * Logger.
     */
    private static final Log LOG = LogFactory.getLog(QuartzSchedular.class);
    /**
     * Application Context.
     */
    @Autowired
    private ApplicationContext appContxt;
    /**
     * {@inheritDoc}
     */
    public void afterPropertiesSet() {
        try {
            super.afterPropertiesSet();
        } catch (Exception e1) {
            LOG.error("Quartz not initialized: " + StringUtil.stackTraceToString(e1));
            return;
        }
        try {
            reScheduleJobs();
        } catch (SchedulerException e) {
            LOG.error("Jobs not rescheduled: " + StringUtil.stackTraceToString(e));
            return;
        }
    }
    /** Function to reschedule all the Quartz Jobs.
     * @throws SchedulerException
     */
    public void reScheduleJobs() throws SchedulerException {
        // Get schedular instance.
        Scheduler sched = getScheduler();
        //Get all the groups
        for (String group : sched.getJobGroupNames()) {
            // Get all the Jobs.
            for (String jobeName : sched.getJobNames(group)) {
                JobDetail jobDetail = sched.getJobDetail(jobeName, group);
                //Get all the triggers.
                for (Trigger trigger : sched.getTriggersOfJob(jobeName, group)) {
                    Trigger quartzTrigger = sched.getTrigger(trigger.getName(), group);
                    //Unscheduling jobs.
                    sched.unscheduleJob(trigger.getName(), group);
                    //Getting triggers from app context
                    Trigger objTrigger = (Trigger) appContxt.getBean(trigger.getName());
                    //Associating triggers with jobs.
                    sched.scheduleJob(jobDetail, objTrigger);
                }
            }
        }
    }
}

No comments:

Post a Comment