Saturday, October 30, 2010

Quartz

I’ve been setting up a Quartz scheduler recently. We wanted it to store the jobs in a Database. Quartz helpfully comes with a set of sql scripts for virtually all DB’s to create and manage all jobs, triggers, schedules etc via a transaction and cluster aware scheduler, so there is no need to think about that aspect of it. The scripts are all stored in the Quartz distribution under docs/dbTables folder.

Once the tables are all set up you can use the example application provided with Quartz to test out the DB persistence (Example 13). Configure it to point to your database by updating the instance1.properties file

We then wanted to Spring-ify it. This was straightforward

e.g.

public class RunSchedule {

static Logger _log = LoggerFactory.getLogger(RunSchedule.class);

public static void main(String[] args) throws Exception {

BeanFactory springContext = new ClassPathXmlApplicationContext("Spring-Quartz.xml");

cleanUp((Scheduler)springContext.getBean("plannedDowntimeScheduler"));

}

public static void cleanUp(Scheduler inScheduler) throws Exception {

_log.warn("***** Deleting existing jobs/triggers *****");

// unschedule jobs

String[] groups = inScheduler.getTriggerGroupNames();

for (int i = 0; i <>

String[] names = inScheduler.getTriggerNames(groups[i]);

for (int j = 0; j <>

inScheduler.unscheduleJob(names[j], groups[i]);

}

}

// delete jobs

groups = inScheduler.getJobGroupNames();

for (int i = 0; i <>

String[] names = inScheduler.getJobNames(groups[i]);

for (int j = 0; j <>

inScheduler.deleteJob(names[j], groups[i]);

}

}

}

}

And

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"xmlns:jee="http://www.springframework.org/schema/jee"

xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xmlns:jms="http://www.springframework.org/schema/jms"xmlns:util="http://www.springframework.org/schema/util"

xsi:schemaLocation="

http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd

http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd

http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd

http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms-2.5.xsd

http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd">

<bean id="runMeTask" class="ie.bge.middleware.RunMeTask" />

<bean name="runMeJob"

class="org.springframework.scheduling.quartz.JobDetailBean">

<property name="jobClass" value="ie.bge.middleware.RunMeJob" />

<property name="jobDataAsMap">

<map>

<entry key="runMeTask" value-ref="runMeTask" />

<map>

<property>

<bean>

<bean id="simpleTrigger"

class="org.springframework.scheduling.quartz.SimpleTriggerBean">

<property name="jobDetail" ref="runMeJob" />

<property name="repeatInterval" value="5000" />

<property name="startDelay" value="1000" />

<bean>

<bean id="cronTrigger"

class="org.springframework.scheduling.quartz.CronTriggerBean">

<property name="jobDetail" ref="runMeJob" />

<property name="cronExpression" value="0/5 * * * * ?" />

<bean>

<bean name="plannedDowntimeScheduler"

class="org.springframework.scheduling.quartz.SchedulerFactoryBean" init-method=”start”>

<property name="quartzProperties">

<util:properties location="/quartz.properties" />

property>

<property name="applicationContextSchedulerContextKey"

value="applicationContext" />

<property name="waitForJobsToCompleteOnShutdown" value="true" />

<bean>

<beans>

All works as expected.

N.B. Note the init-method attribute for SchedulerFactoryBean. This is to automate the starting of the scheduler after it is instantiated. It is better than having a code based startup, (which would implement the Spring InitializingBean)

When we integrated it into our main application however we started having issues.

Code:

Caused by: org.quartz.SchedulerConfigException: Failure occured during job recovery. [See nested exception: org.quartz.impl.jdbcjobstore.LockException: Failure obtaining db row lock: ORA-00942: table or view does not exist

[See nested exception: java.sql.SQLException: ORA-00942: table or view does not exist

]]

The problem was due to autowiring. We had a datasource defined for a different purpose that was getting autowired into our SchedulerFactoryBean. (We were defining the datasource via the quartz.properties, and not injecting the datasource).


Manually wiring the correct dataSource fixed the problem. (or turninng off autowiring should also work as suggested by jrsisson)

http://forum.springsource.org/showthread.php?p=326612&posted=1#post326612

No comments: