I am working on a new application, which will require some scheduled jobs. I used EJB3 Timers in 10MinuteMail, but now Seam includes and uses Quartz, an open source scheduling system. So I figured I’d try the new hotness.

So far, it’s been a rough road, and I’m not 100% up and running yet (hence the part 1 in the title), but I wanted to share what I’ve learned along the way as the Seam documentation is lacking a lot of information (although some of what’s below mirrors the Seam documentation).

First you have to enable the Quartz engine for handling @Asynchronous methods. Add this line to your components.xml file:

<async:quartz-dispatcher />

I also found that I had to define the async namespace in my component.xml (generated by seam-gen from Seam 2.0 CR1):

xmlns:async="http://jboss.com/products/seam/async"

Then I set up my method:

@Asynchronous
public QuartzTriggerHandle scheduleSiteCheck(@Expiration
Date pWhen, @IntervalDuration
Long pInterval, Long pSiteId) {
Site site = (Site) entityManager.createQuery("from Site where id = :id").setParameter("id", pSiteId)
.getSingleResult();
checkSite(site);
return null;
}

In the method where I called this from I was taking the returned QuartzTriggerHandle and persisting it for use later. First it wouldn’t persist a large object into Postgres without a transaction around it, so I wrapped that method in a @Transactional annotation. So far so good. But now it won’t deserialize them when I load up the object the Handle was on. I had to annotate the entity property with @Lob for it to work, even though I thought that Hibernate would handle the serialization by default. That change meant a table drop, so Hibernate could reset up the column. Then it worked.

So far so good, right?

But I wanted to persist the jobs, as they really just need to run all the time, across restarts.

No good documentation on this within Seam.

I created a seam.quartz.properties file inside the resources/ directory in my project.

My file looks like this:

#============================================================================
# Configure Main Scheduler Properties
#============================================================================

org.quartz.scheduler.instanceName Sched1
org.quartz.scheduler.instanceId AUTO
org.quartz.scheduler.rmi.export false
org.quartz.scheduler.rmi.proxy false

#============================================================================
# Configure ThreadPool
#============================================================================

org.quartz.threadPool.class org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount 100

#============================================================================
# Configure JobStore
#============================================================================

org.quartz.jobStore.misfireThreshold 60000

org.quartz.jobStore.class org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
org.quartz.jobStore.useProperties false
org.quartz.jobStore.dataSource sitemonitorDS
org.quartz.jobStore.tablePrefix qrtz_

#============================================================================
# Configure Datasources
#============================================================================

org.quartz.dataSource.sitemonitorDS.jndiURL java:/sitemonitorDatasource

Some things to note. You probably don’t need 100 threads. 3 is probably fine.

org.quartz.threadPool.threadCount 100

Also, just to make it easy, the DataSource name referred to here:

org.quartz.jobStore.dataSource sitemonitorDS

just has to match what you have here:

org.quartz.dataSource.sitemonitorDS.jndiURL java:/sitemonitorDatasource

between “dataSource.” and “.jndiURL”. The URL should point to your datasource defined in your ds.xml for your project.

I had to modify my build.xml to build the file into my project’s jar file as well. In the target jar, I had to change this block:
<copy todir="${jar.dir}">
<fileset dir="${basedir}/resources">
<include name="seam.properties" />
</fileset>
</copy>

to read:

<copy todir="${jar.dir}">
<fileset dir="${basedir}/resources">
<include name="seam.properties" />
<include name="seam.quartz.properties" />
</fileset>
</copy>

Almost there. Seam still wasn’t finding it. Turns out there was a bug in Seam, where it was loading the wrong file name. So after upgrading to the latest Seam (2.0 CR.3) it worked.

Except the tables didn’t exist. I had to download the full quartz distribution to find the sql scripts. Quartz sql for Postgres 8.

One more hurdle down. Then I kept getting errors about violations of the NOT NULL constraint on the priority column. Finally figured out that it’s the column in the qrtz_fired_triggers table which was the problem. This seems like a Quartz bug, as all of this was happening deep in the Quartz code, not in Seam or my code at all. I removed the NOT NULL constraints from the column, which seems to have fixed it just fine.

Now I’m stuck getting with this error:

org.quartz.JobPersistenceException: Couldn't recover jobs: Couldn't store trigger: No ClassLoaders found for: org.jboss.seam.async.AsynchronousInvocation

Which I found reference to on this JBoss Forums thread. However, it’s not clear to me if there is a fix, or if Quartz job persistence simply isn’t supported under Seam.

If it isn’t supported then I have a few options. I can go back to EJB3 Timers, which DO persist just fine. Or I can write a Startup service which loads and starts all the jobs I need started at application start time, basically handling the persistence myself. Not ideal.

If I make any progress I will post an update here.

I hope this helps someone get going with Quartz on Seam.