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.
Leave a Reply