Vintage brass orrery with copper gear rings and orbital arms on dark slate, lit by amber spotlight.

Quartz Scheduling and Seam (part 1)

Getting Quartz scheduling to work with Seam proved tricky - here's what I learned navigating sparse docs and setup gotchas.

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:



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:

to read:


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.

No comments yet