Quartz Scheduling and Seam (part 1)

Home/Java/JBoss/Seam/Quartz Scheduling and Seam (part 1)

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.

By | 2017-05-18T15:18:37+00:00 November 2nd, 2007|Seam|11 Comments

About the Author:

11 Comments

  1. Terry November 6, 2007 at 9:05 am - Reply

    I’m facing the similar problems in term of lack of doc for using quartz scheduling in SEAM.
    Thanks for your posts on this.

    Anyway, seam really rocks, and I’m sure the next releases will have a more detailed doc.

  2. Techieexchange November 13, 2007 at 11:06 am - Reply

    Hi,
    Nice info for Seam+Quartz.

    I wrote a step-by-step screencast tutorial to make Seam development as RAD – Rapid Application Development with Eclipse and Tomcat, focusing on developer productivity.

    http://techieexchange.wordpress.com/2007/11/11/rad-seam-development-with-eclipse-and-tomcat-step-by-step-tutorial-screencast/

    I hope this tutorial will be useful for J2EE/JEE developers.

    Thanks.

  3. Tom Goring February 15, 2008 at 12:26 am - Reply

    Hi, did you get this working? I’ve got past the

    ClassLoaders found for: org.jboss.seam.async.AsynchronousInvocation

    by adding quartz jar into the EAR lib but now I’m getting a rollback issue?

  4. Devon February 15, 2008 at 10:43 am - Reply

    I haven’t gotten it working, although to be honest I’ve been busy with other projects for a while.

  5. tgodse April 21, 2008 at 4:29 pm - Reply

    Hi first of all thanks a lot Devon your wiki helped a lot but I am still having the same problem that you have.
    I saw Tom Goring is not getting the exception after copying quartz.jar into app-inf/lib folder, but I am still getting org.quartz.JobPersistenceException: Couldn’t recover jobs: Couldn’t store trigger: No ClassLoaders found for: org.jboss.seam.async.AsynchronousInvocation. Just an update at the begining I also had rollback error which was due to seam managed transaction (referring to Tom Gorings problem) which was corrected after entering following two properties in the seam-quartz.properties.
    org.quartz.jobStore.nonManagedTXDataSource
    org.quartz.dataSource.nonManagedTX.jndiURL=
    oh by the way datasouce is also required
    Not sure still why I am getting Asynch error please post here if anybody has found any work around.

  6. tgodse April 23, 2008 at 7:45 am - Reply

    Thanks a lot this blog was extremely helpful. The problem was solved after including file into the EAR/lib directly. Now working on persisting the QuartzTriggerHandle. Earlier I was trying to add quartz.jar into app-inf lib where my other jars are but had to create a lib folder with in EAR to make it work. So far working out good. Thanks again!

  7. Devon April 23, 2008 at 7:54 am - Reply

    Interesting! Thanks for posting your progress!

  8. vasireddy November 6, 2008 at 7:39 pm - Reply

    Hi all!

    this information is useful, but I have some basic doubts to use the Quartz job scheduling in Seams. Those are,
    Where we have to write that method (in Action class, or in entity, or in structure class)
    Where we have to call that method (in Action class, or in jsp page)
    Where we have to receive that QuartzTriggerHandle
    How to use that handle methods resume, pause, cancel( ) and where?
    Do we need some more configuration in seam environment to use Quartz?( I have set up quartz in my components.xml and application.xml and in lib folder)

    Thank you in advance..

  9. Ian Goodrich July 16, 2009 at 12:16 am - Reply

    This thread is six months old, but I came across it, so probably others will too.
    I appear to have this working and persistent.
    I’m using Jboss 4.2.3, Seam 2.1.2, and the quartz that came with Seam (I think 1.6.0) — I’ll try upgrading to 1.6.5 shortly.
    I had to also add the commons-dbcp-1.3.jar
    I created the quartz tables in my database as per the 1.6.5 distro doc/dbTables/mysql innodb script
    My seam.quartz.properties looks like this:

    # begin
    org.quartz.scheduler.instanceName = QuartzScheduler
    org.quartz.scheduler.instanceId = 1
    org.quartz.scheduler.rmi.export = false
    org.quartz.scheduler.rmi.proxy = false

    org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
    org.quartz.threadPool.threadCount = 5

    org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
    org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate
    org.quartz.jobStore.dataSource = @db_name@_ds
    org.quartz.jobStore.tablePrefix = QRTZ_

    org.quartz.dataSource.@db_name@_ds.driver = com.mysql.jdbc.Driver
    org.quartz.dataSource.@db_name@_ds.URL = jdbc:mysql://localhost:3306/@db_name@
    org.quartz.dataSource.@db_name@_ds.user = @db_user@
    org.quartz.dataSource.@db_name@_ds.password = @db_pass@
    org.quartz.dataSource.@db_name@_ds.maxConnections = 10
    #end

    Of course the @db_*@ stuff is ant-replaced which you can do or you can stick you’re own values in. Obviously, I’m using mysql. I did notice that the original poster was using postgres — there’s a different driver (obviously) and less obviously a different driverDelegateClass specifically for postgres in the quartz.jar in the org.quartz.impl.jdbcjobstore package. But it looks like the original poster already caught this.

    The only real trouble I had was that the class that does the work could not be the class called from the UI. I have no idea why, probably something about scope and/or transactions. No error messages on this though, just a one-time fire and then silence.

    I say it is persistent because i was able to Ctrl-C my jboss and restart it and then the job picked up where it left off and ran to completion. My class doing the work as application scope and created @Startup which may have something to do with it’s auto-starting — not sure. I have also checked the db manually and saw stuff persisted. I have not tried serializing the QuartzTriggerHandler and cancelling/pausing/etc. Actually, that was the other little snap I had: I didn’t understand the return null was going to be intercepted by Seam (although it clearly states this now in the seam docs) and replaced with the real live QuartzTriggerHandler.

    Anyway, pretty cool now that it’s working.

    • Devon July 16, 2009 at 10:46 am - Reply

      Very good to know!!! I haven’t tried since my attempt when I wrote this post. If I can get my timers using quartz instead of EJB, I should be able to move some apps from ears to wars, which would be nice. Little lighter weight.

      Thanks!

  10. Wayne Ng October 30, 2009 at 8:53 am - Reply

    Hi Devon,

    In case you are still stuck at this error:
    org.quartz.JobPersistenceException: Couldn’t recover jobs: Couldn’t store trigger: No ClassLoaders found for: org.jboss.seam.async.AsynchronousInvocation

    I managed to solve it by looking at this link you posted eariler:
    http://www.jboss.com/index.html?module=bb&op=viewtopic&t=115884

    1. Ensure that quartz-1.6.5.jar (yes, I am using this version) is copied to the EarContent directory of the ear project.

    2. Next ensure that we add the following to application.xml (yes, it has to be a JAVA module):

    quartz-1.6.5.jar

    3. Ensure that quartz-1.6.5.jar is included within your jboss instance.
    Example: /usr/local/jboss/server/default/lib <- here

    No more pain, hopefully.

Leave A Comment