Absolutely no idea

A blog by Lukas

Configuring Quartz Scheduler with Spring

June 26th, 2007 by lukas

Quartz and Spring

In our previous project we required a web application to poll a directory on the filesystem to check for any new files that needed processing. We were using Spring 2.0 and Hibernate and were looking for a timer or polling solution that could integrate nicely with these technologies and allow us access to our Spring configured beans. Step in Quartz.

Quartz is amongst others, (see http://opensymphony.com/quartz/) a scheduling component that lets you run external tasks from within your web application. “Yikes!” - you may think - running threads outside the container… but in fact Quartz is the most widely used framework to do just that. It has very nice Spring integration and enables us to write minimal code. Also, is instantiated via Spring itself and therefore has access to all Spring configured components. Some benefits of the integration are:

  • The only code to write is the Job class (the actual task to run) - everything else is configured in the applicationContext.xml
  • It lets us instantiate the Quartz scheduler from within Spring and at the same time that Spring is instantiated and therefore…
  • It gives Quartz jobs access to any classes within the Spring context including implicit access to the SessionFactory (and therefore our DAOs) as well as any transaction wrappers used during declarative transaction handling.

Basically the processing of our job is implemented as follows:

  • Implement a Quartz job by extending a QuartzJobBean and populate the executeInternal() method. This method will contain the actual business processing - in our case the following steps to process the files on the filesystem:
    1. Job reads input directory for new files
    2. The list of files is passed to a Handler class that processes them one by one. (This reads them in, parses and saves to the database. Once a file is successfully processed it is deleted from the incoming directory).
  • After implementing the job above, we configure it as a bean inside Spring’s applicationContext.xml:
  • the above job is configured and given a name:
<bean name="fileReaderJob" class="org.springframework.scheduling.quartz.JobDetailBean">
    <property name="jobClass" value="com.myapplication.scheduler.FileReaderJob"/>
    <property name="name" value="fileReaderJob"/>
</bean>
  • The job bean is attached as a jobDetail to a Quartz Trigger, for example a Cron-like trigger that fires every 30 minutes to check for new files:
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
	<property name="jobDetail" ref="fileReaderJob"/>
 	<!-- run every 30 minutes -->
	<property name="cronExpression" value="0 0/30 * * * ?"/>
</bean>
  • Finally the cron trigger is wired up to the SchedulerFactoryBean which instantiates the scheduler and starts the job when the Spring Context is initialised.
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <property name="triggers">
        <list>
		<ref bean="cronTrigger" />
	</list>
	</property>
</bean

That’s it. Once the Spring Context is loaded during application start-up the scheduler starts and your trigger (and attached job) is run.

Setting JobBeans properties from a configuration file

One additional enhancement to the above was to move the cronExpression setting from the applicationContext.xml into a JFig config file. This way changes to this setting can be controlled through config.xml files rather than having to change applicationContext.xml. Again with Spring this is pretty easy to do:

1. Subclass the org.springframework.scheduling.quartz.CronTriggerBean
2. Override setCronExpression(String cronExpression) method to retrieve the expression from a config file:

public class ConfigurableCronTriggerBean extends CronTriggerBean
{
    public void setCronExpression(String cronExpression) throws ParseException 
    {
        //ignore the dummy value from the applicationContext and set the cron from Jfig
        super.setCronExpression(ConfigFactory.getConfig().getValue(”scheduler”,”cronExpression”));
    }
}

3. Replace the CronTriggerBean with the new class created above and set the cronExpression in the applicationContext with a dummy parameter:

<bean id="cronTrigger" class="com.myapplication.scheduler.ConfigurableCronTriggerBean">
        <property name="jobDetail" ref="fileReaderJob" />
        <property name="cronExpression" value=”dummy” /> <!–this will call setter in the trigger bean–>
</bean>

Setting the cronExpression property is still required - this will force Spring to executes the setter for the cronExpression. The “dummy” value is irrelevant as it will be replaced with the value from the config anyway.

Initially I thought that the cronExpression property is not required but not having it declared as part of the CronTriggeBean did not actually execute the setter and the following exception was thrown:

[6/6/07 15:03:33:265 NZST] 00000013 SystemErr     R Caused by: org.quartz.SchedulerException: Based on configured schedule, the given trigger will never fire.at org.quartz.core.QuartzScheduler.scheduleJob(QuartzScheduler.java:745)at org.quartz.impl.StdScheduler.scheduleJob(StdScheduler.java:266)
at org.springframework.scheduling.quartz.SchedulerFactoryBean.addTriggerToScheduler(SchedulerFactoryBean.java:846)
at org.springframework.scheduling.quartz.SchedulerFactoryBean.registerJobsAndTriggers(SchedulerFactoryBean.java:776)
at org.springframework.scheduling.quartz.SchedulerFactoryBean.afterPropertiesSet(SchedulerFactoryBean.java:599)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1175)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1145)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:427)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:251)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:144)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:248)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:160)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:276)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:360)
at org.springframework.web.context.ContextLoader.createWebApplicationContext(ContextLoader.java:241)
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:184)
at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:49)

Posted in spring, programming | No Comments »

Instantiating Spring’s Application Context

May 30th, 2007 by lukas

The applicationContext.xml file is the brain of the Spring framework. It holds the configuration of all beans (classes) that the application uses and the Spring container manages. They are wired up through Spring’s use of the Dependency Injection Pattern. It can hold the wiring for things like datasources, DAOs, the SessionFactory, classes required for transaction handling and heaps more. I will not go into these - have a look at Spring’s Reference Documentation for more details.

Instantiating the context in a web application:

The easiest way to do this is by creating it as part of application start-up - as a ServletContextListener:

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.support.WebApplicationContextUtils;
 
/**
* Get the application context when it is available and set it in the SpringWrapper.
*/
public class ServletContextListener extends ContextLoaderListener
{
	public void contextInitialized(ServletContextEvent event)
	{
		super.contextInitialized(event);
		ServletContext servletContext = event.getServletContext();
		ApplicationContext appContext =
		WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
		SpringWrapper.setApplicationContext(appContext);
	}
}

All that is then required is to add the listener in the web.xml:

<listener>
	<listener-class>com.company.util.ServletContextListener</listener-class>
</listener>

Now, when the application starts the Spring context is loaded up and set in a static variable inside the SpringWrapper class. (See here for full code of this class - it includes convenience methods for unsetting and overloading the default context with another one to be used when unit testing)

Instantiating some beans is now as simple as:

SpringWrapper.getApplicationContext().getBean("someBeanName");

Instantiating an alternative applicationContext during unit testing:

When unit testing it is often useful to have an alternative context load up your Spring dependencies. For example for overwriting the default datasource bean that is setup from the JndiObjectFactoryBean (which is the preferred way for web apps) with one that sets up a datasource directly from a DriverManagerDataSource so that you can access the db from your unit or integration tests. Another reason why you may want to have a separate test-applicationContext is to stub out certain beans or replace them with mock objects for the purposes of isolating the bean being tested. Whatever the need, this is easy to do by instantiating the context using the FileSystemXmlApplicationContext(String[] locations) constructor. The locations are read one by one and the xml files found in those read in one by one - configurations from the latter extending any previous ones.

The implementation of this can be seen here SpringWrapper.java.

I find that it is not unusual to have several test-applicationContext.xml files each stubbing out different beans and/or parameters to properly isolate classes for unit testing.

Posted in spring, programming | No Comments »