Spring, Quartz, and Persistence

Spring is great, Spring is fun, Spring is best when it’s one on one (props to George Michael).

No, really, Spring is a great architecture. Its easily configured, highly flexible, and comes with a ton of already coded utils for doing anything from REST based integration calls to quickly deployed Servlets.

One of the packages that you may eventually end up using it the Spring Scheduler. The Spring Scheduler is a Spring wrapper to the very nice scheduling library, Quartz. The Spring Scheduler is simple and easy until you have to worry about more than one server. As soon as you get into a clustered environment (especially one that you back the scheduling with a database) it starts to swagger like a frat boy during homecoming. Unless your careful it just may puke on you.

So you don’t waste 3 (yes 3) days on getting it setup and working I thought I’d go over what it takes to get Quartz to run in a Spring clustered environment with a db backend and actual usable Spring beans (autowiring, etc).

The first thing you will want to do it get the Quartz libraries. Careful now because the latest Quartz libs (2.x) don’t work with Spring. You have to use something earlier (at the time of this writing it’s at 1.8.6).
Here is the Maven dependencies for it:

	<dependency>
		<groupId>org.quartz-scheduler</groupId>
		<artifactId>quartz</artifactId>
		<version>1.8.5</version>
		<exclusions>
			<exclusion>
				<groupId>com.mchange.c3p0</groupId>
				<artifactId>com.springsource.com.mchange.v2.c3p0</artifactId>
			</exclusion>
		</exclusions>
	</dependency>

        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz-oracle</artifactId>
            <version>1.8.5</version>
        </dependency>

Please note that I excluded C3P0 because I already use it in most of my projects. If you don’t use C3P0 then your Maven dependencies will be:

	<dependency>
		<groupId>org.quartz-scheduler</groupId>
		<artifactId>quartz</artifactId>
		<version>1.8.5</version>
	</dependency>

        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz-oracle</artifactId>
            <version>1.8.5</version>
        </dependency>

I’m assuming your application already has a dataSource and a transactionManager defined. Something like:

	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
		destroy-method="close">
		<property name="driverClass" value="${driver}"></property>
		<property name="jdbcUrl" value="${url}" />
		<property name="user" value="${username}" />
		<property name="password" value="${password}" />
		<property name="initialPoolSize" value="2" />
		<property name="minPoolSize" value="2" />
		<property name="maxPoolSize" value="5" />
		<property name="checkoutTimeout" value="100" />
		<property name="maxStatements" value="500" />
		<property name="testConnectionOnCheckin" value="false" />
		<property name="maxIdleTime" value="180" />
		<property name="idleConnectionTestPeriod" value="1000" />
	</bean>

	<bean id="entityManagerFactory"
		class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
		<property name="persistenceProvider" ref="hibernatePersistence" />
		<property name="persistenceUnitName" value="model" />
		<property name="dataSource" ref="dataSource" />
		<property name="jpaVendorAdapter">
			<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
				<property name="showSql" value="true" />
				<property name="databasePlatform" value="org.hibernate.dialect.Oracle10gDialect" />
			</bean>
		</property>
	</bean>

	<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
		<property name="entityManagerFactory" ref="entityManagerFactory" />
		<property name="dataSource" ref="dataSource" />
	</bean>

You will need an dataSource and transactionManager for Quartz to use. Quartz, in order to make sure that multiple servers are not firing the same event, uses the database to keep track of what has executed and what needs to be executed. This allows multiple servers that have the application on it to not fire the same event at the same time. Only one server will fire the job (the server that will do the job is random).

Next we need to set up Quartz to run under Spring. Now, I mentioned Spring Scheduler above. It has it’s one tags, <task>, that work great in single server environments. Anything other than that and you will be using regular Spring bean definitions. There’s quite a bit to the configuration so I’ll go over each piece and then present the whole configuration as one file.

First lets setup the scheduler that will run the jobs for us. This uses the Spring framework SchedulerFactoryBean to create the scheduler that wraps Quartz and runs our jobs for us.

<bean id="scheduler"
	class="org.springframework.scheduling.quartz.SchedulerFactoryBean" lazy-init="false">

Next we need to setup some basic configuration parameters to get the system working:

<property name="autoStartup" value="true"/> 
<property name="applicationContextSchedulerContextKey" value="applicationContext"/>
<property name="waitForJobsToCompleteOnShutdown" value="true"/>
<property name="overwriteExistingJobs" value="true"/>
<property name="dataSource">
	<ref bean="dataSource"/>
</property>
<property name="transactionManager">
	<ref bean="transactionManager"/>
</property>
  • autoStartup – Start the Scheduler as soon as it is instantiated
  • applicationContextSchedulerContextKey – The key in the map that will be passed to our jobs that has the Spring Application Context in it
  • waitForJobsToCompleteOnShutdown – Should we not shut down if a job is in the middle of executing
  • overwriteExistingJobs – If the configuration in this file changes from what Quartz has in the database, should the database values be overwritten
  • dataSource – The dataSource (already defined as a bean somewhere) that should be used for database connectivity
  • transactionManager – The transactionManager (already defined as a bean somewhere) that should be used to retrieve transactions

There are additional properties but they merit more individual attention:

		<property name="quartzProperties">
			<props>
				<!-- Job store -->
				<prop key="org.quartz.jobStore.misfireThreshold">6000000</prop>
				<prop key="org.quartz.jobStore.driverDelegateClass">org.quartz.impl.jdbcjobstore.oracle.OracleDelegate</prop>

				<prop key="org.quartz.jobStore.isClustered">true</prop>
				<prop key="org.quartz.jobStore.clusterCheckinInterval">20000</prop>

				<prop key="org.quartz.scheduler.instanceName">ClusteredScheduler</prop>
				<prop key="org.quartz.scheduler.instanceId">AUTO</prop>
				<prop key="org.quartz.scheduler.jmx.export">true</prop>
			</props>
		</property>
  • misfireThreshold – The the number of milliseconds the scheduler will ‘tolerate’ a trigger to pass its next-fire-time by, before being considered “misfired”
  • driverDelegateClass – The class to use to connect to your database instance. I use Oracle. This should be changed to your database type
  • isClustered – Is this a clustered instance. Yes because we are running on more than one server
  • clusterCheckinInterval – How often should things be checked
  • instanceName – The name of this cluster instance
  • instanceId – How will the instances be id’ed? AUTO lets Quartz deal with it
  • jmx.export – Should management beans be exported? I like MBeans so put it as true. This is not required for persistence
		<property name="triggers">
			<list>
				<ref bean="caseArchiveSuspendedJobTrigger"/>
		         </list>
		</property>
  • triggers – The triggers are a list of jobs that need to be executed. More on that in a bit

The last property that needs defined is the factory that will create the Jobs. Spring Scheduler ships with a version that works by creating a new instance of a job each time the job is to be executed. The issue with this is that the job is not in the scope of the Spring application because of this. The Job does have its parameters set but any autowiring or, more importantly, transactions, will not exists. This means that if you job does any database manipulation that requires a commit, it will not work. I have created my own SpringBeanJobFactory that gets around this by creating the job as a regular Spring bean and then using that instance of the bean to execute the job.

		<property name="jobFactory">
			<bean class="mycode.scheduled.SpringBeanJobFactory"/>
		</property>

And the code for the class:

package mycode.scheduled;

import org.quartz.SchedulerContext;
import org.quartz.spi.TriggerFiredBundle;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyAccessorFactory;
import org.springframework.scheduling.quartz.SpringBeanJobFactory;
import org.springframework.web.context.support.XmlWebApplicationContext;

/**
 * @author sseaman - mostly a cut/paste from the source class
 *
 */
public class SpringBeanJobFactory 
	extends org.springframework.scheduling.quartz.SpringBeanJobFactory
{

	private String[] ignoredUnknownProperties;

	private SchedulerContext schedulerContext;


	@Override
	public void setIgnoredUnknownProperties(String[] ignoredUnknownProperties) {
		super.setIgnoredUnknownProperties(ignoredUnknownProperties);
		this.ignoredUnknownProperties = ignoredUnknownProperties;
	}
	
	@Override
	public void setSchedulerContext(SchedulerContext schedulerContext) {
		super.setSchedulerContext(schedulerContext);
		this.schedulerContext = schedulerContext;
	}
	
	/**
	 * An implementation of SpringBeanJobFactory that retrieves the bean from
	 * the Spring context so that autowiring and transactions work
	 * 
	 * This method is overriden.
	 * @see org.springframework.scheduling.quartz.SpringBeanJobFactory#createJobInstance(org.quartz.spi.TriggerFiredBundle)
	 */
	@Override
	protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
		XmlWebApplicationContext ctx = ((XmlWebApplicationContext)schedulerContext.get("applicationContext"));
		Object job = ctx.getBean(bundle.getJobDetail().getName());
		BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(job);
		if (isEligibleForPropertyPopulation(bw.getWrappedInstance())) {
			MutablePropertyValues pvs = new MutablePropertyValues();
			if (this.schedulerContext != null) {
				pvs.addPropertyValues(this.schedulerContext);
			}
			pvs.addPropertyValues(bundle.getJobDetail().getJobDataMap());
			pvs.addPropertyValues(bundle.getTrigger().getJobDataMap());
			if (this.ignoredUnknownProperties != null) {
				for (String propName : this.ignoredUnknownProperties) {
					if (pvs.contains(propName) && !bw.isWritableProperty(propName)) {
						pvs.removePropertyValue(propName);
					}
				}
				bw.setPropertyValues(pvs);
			}
			else {
				bw.setPropertyValues(pvs, true);
			}
		}
		return job;
	}
	
}

As you can see the code gets the applicationContext and then uses that to retrieve the bean from Spring. This bean will be in any defined transactions and will have all Autowiring already done.

The last steps are to define the trigger and the bean that represents the job. For this we will use more of the Spring Scheduler classes.

	<bean id="caseArchiveSuspendedJobTrigger"
		class="org.springframework.scheduling.quartz.CronTriggerBean">
		<property name="jobDetail">
			<bean name="caseArchiveSuspendedJob" class="org.springframework.scheduling.quartz.JobDetailBean">
				<property name="name" value="caseArchiveSuspendedJob"/>
				<property name="jobClass" value="mycode.job.CaseArchiveSuspendedJob"/>
			</bean>
		</property>
		<property name="cronExpression" value="0 0 3 * * ?"/>
	</bean>
	
	<bean id="caseArchiveSuspendedJob" class="mycode.job.CaseArchiveSuspendedJob">
		<property name="daysToArchiveSuspended" value="6"/>	
	</bean>

First, create a definition for the type of trigger. Again, mine is Cron based. The CronTriggerBean requires two properties:

  • jobDetail
  • cronExpression

The cronExpression is straightforward. The jobDetail requires some explanation.

The jobDetail uses an instance of the JobDetailBean but not in the way Spring Scheduler intended. The name is set to correspond to the bean id of the bean that represents the actual job. The jobClass is the class of the bean (same as defined in the bean class attribute). This allows the SpringBeanJobFactory to get the actual Spring instance of the bean by using the name while still satisfying the requirements of the JobDetailBean (jobClass is required).

Lastly, we just define our job bean as we would any other bean.

Here is a skeleton version of my job:

public class CaseArchiveSuspendedJob {
    @Override
	@Transactional(propagation = Propagation.REQUIRED, readOnly = false)
	public void execute(JobExecutionContext context)
		throws JobExecutionException
	{
            // do something with the db in a transaction
     }
}

And now, here is everything in one file:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	default-autowire="byName"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
	
	<bean id="scheduler"
		class="org.springframework.scheduling.quartz.SchedulerFactoryBean" lazy-init="false">
		<property name="autoStartup" value="true"/>
		<property name="applicationContextSchedulerContextKey" value="applicationContext"/>
		<property name="waitForJobsToCompleteOnShutdown" value="true"/>
		<property name="overwriteExistingJobs" value="true"/>
		<property name="dataSource">
			<ref bean="dataSource"/>
		</property>
		<property name="transactionManager">
			<ref bean="transactionManager"/>
		</property>
		<property name="jobFactory">
			<bean class="mycode.scheduled.SpringBeanJobFactory"/>
		</property>
		<property name="quartzProperties">
			<props>
				<prop key="org.quartz.jobStore.misfireThreshold">6000000</prop>
				<prop key="org.quartz.jobStore.driverDelegateClass">org.quartz.impl.jdbcjobstore.oracle.OracleDelegate</prop>

				<prop key="org.quartz.jobStore.isClustered">true</prop>
				<prop key="org.quartz.jobStore.clusterCheckinInterval">20000</prop>

				<prop key="org.quartz.scheduler.instanceName">SgsClusteredScheduler</prop>
				<prop key="org.quartz.scheduler.instanceId">AUTO</prop>
				<prop key="org.quartz.scheduler.jmx.export">true</prop>
			</props>
		</property>
		<property name="triggers">
			<list>
				<ref bean="caseArchiveSuspendedJobTrigger"/>
			</list>
		</property>
	</bean>
	
	<bean id="caseArchiveSuspendedJobTrigger"
		class="org.springframework.scheduling.quartz.CronTriggerBean">
		<property name="jobDetail">
			<bean name="caseArchiveSuspendedJob" class="org.springframework.scheduling.quartz.JobDetailBean">
				<property name="name" value="caseArchiveSuspendedJob"/>
				<property name="jobClass" value="mycode.job.CaseArchiveSuspendedJob"/>
			</bean>
		</property>
		<property name="cronExpression" value="* * * * * * ?"/>
	</bean>
	
	<bean id="caseArchiveSuspendedJob" class="mycode.job.CaseArchiveSuspendedJob">
		<property name="daysToArchiveSuspended" value="10"/>	
	</bean>
</beans>

As you can see, there is quite a lot setting up Spring and Quartz to easily work within a transaction. I spent way too much time researching this. I hope this helps someone….

About sseaman

Connect with me on Google+
This entry was posted in Java, Programming and tagged , , . Bookmark the permalink.

121 Responses to Spring, Quartz, and Persistence

  1. Srinivas says:

    A wonderful article. It is exactly what i was looking for. Thanks a ton.

  2. Mathias says:

    Thanks man, was surfing around after a good way to set up the autowiring. Glad i didn’t have to resort to the appcontext.getBean() … 🙂

  3. Srinivas says:

    Hi,

    I have a query for you. I suppose, you had created the tables from docs\dbTables which comes Quartz documentation for persisting the jobs in database. Assume a situation where you have scheduled a job for July 4,2011 and the scheduler has scheduled it and it is pesisted in the QUARTZ tables. So, now, if i have to change this schedule to July 5,2011. Can i change the QUARTZ tables manually and be rest-assured that the Scheduler picks up the latest schedule rather than the old schedule?

    Let me know, if there is a better way to do this kind of stuff where i can change the scheduled job’s execution time after a give job is scheduled?

    Thanks in advance.

    • sseaman says:

      Good question.

      First, some basic info (some of which I bet you already know ;)):

      Quartz stored the time to execute in the QRTZ_TRIGGERS table.
      The field we are interested in is NEXT_FIRE_TIME.
      The value of the field is the next time to execute in EPOCH time down to the millisecond. If you want to see what the time is in a more friendly way, use http://www.wolframalpha.com and search for the value in NEXT_FIRE_TIME but remove the last three 0’s and add ‘ epoch time’.
      Example:
      Orig value: 1309265800000
      Search for: 1309265800 epoch time

      Now, for my testing I changed the value in the field and Quartz did in fact use the next time. Pretty straightforward.

      The part I’m not sure of is what occurs when you restart your server. In the example above I have overwriteExistingJobs=true. This should cause Quartz to reset the NEXT_FIRE_TIME each time you restart the server. To ensure that your changes stay you should also change your Cron bean configuration for that specific job.

      Hope that helps!

      • Srinivas says:

        Thanks a lot for your timely help and guidance.

        Please explain the last statement of your response i.e.,”To ensure that your changes stay you should also change your Cron bean configuration for that specific job”. The cron bean configuration is something which is already there ,say, in the spring configuration file. Isnt it? Or Am i mising something here?

        • sseaman says:

          Let me go into a bit more detail::

          If the intent of your database change is to just have the Quartz job execute at a new time and only do so for that one time that you change the database then you do not need to change your spring configuration since you are not changing the scheduled execution time but just the next time it will execute.

          If the intent is to make Quartz always execute the job at a different time than what it was originally scheduled for, then you will need to change the spring configuration as well as the database (in this case you would actually have to change another Quartz table (QRTZ_CRON_TRIGGERS).

          Does that explain it better?

          • Srinivas says:

            Thanks a lot for taking your time out and explaining things. Things are pretty clear now.

            I have created a design which i would like to share with you, to get your valuable inputs. Let me know, if it is OK to drop you a mail.

            • sseaman says:

              Feel free to email me at:
              s l o a n at pobox d0t com

              Pardon the cryptic email… attempting to avoid spam bots 🙂

  4. Choudari says:

    Thanks for the great article.

    overwriteExistingJobs=true causes only the jobs associated with the scheduler in the spring configuration file to be over-written when the server is re-started. If there are other jobs which were created programmatically in the JdbcJobStore, they are not over-written.

    Do we need to change anything in the Scheduler start-up?
    Your help is much appreciated.

    • sseaman says:

      Sorry, but I don’t quite follow your question.

      Do you need to change anything in the start-up code? Not that I am aware of.

      If I’m misunderstanding your question please explain further and I will try and help…

      • Srinivas says:

        I have followed your example to certain extent and the code seems to be working fine in cluster.

        I am creating jobs dynamically which are getting persisted in the persistent job store as well. And, I also have overwriteExistingJobs=true in the spring configuration. Now, when i re-start the server, the jobs which are dynamically created are not getting overwritten. They get executed multiple times. Say, if i have a dynamic job created to execute every two minutes. And, i stop the server and start it after ten minutes, the job executes five times as soon as the server starts. But, if there is a job which is part of the spring configuration , like the one you have given configuration, it is over-written when the server re-starts.

        Hope it is clear. Please do the neeful.

        • Srinivas says:

          My observation has been that for jobs which are configured in the spring configuration file and added to the org.springframework.scheduling.quartz.SchedulerFactoryBean, the PREV_FIRE_TIME in QRTZ_TRIGGERS table gets updated to ‘-1’ but for dynamically created jobs it is not over-written.

          • sseaman says:

            My initial thought was to just set the SimpleTrigger.setPreviousFireTime() to either null or some date in the past but according to this post that won’t work.

            Now, the posting states that it won’t work because the job is being executed programmatically from the users code. Since you are using Quartz to execute the job it might work.

            Honestly, I’m not really sure on this one. If you figure it out could you please post what you did here?

        • sseaman says:

          For multiple executions:
          First, make sure that you job is a StatefulJob. That should ensure that Quartz won’t run it concurrently in multiple threads when it tried to catch up.
          Now, what I mean by ‘catch up’ is that when Quartz starts up, if it thinks it has missed jobs (such as in your example above) it will attempt to execute all the jobs it has missed.
          In Quartz this is called a misfire.
          There are a few options you can take when dealing with a misfire. One of them MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT looks like it might do what you are asking. It should reschedule the job to fire at its normal time without running it over and over again to ‘catch up’.

          Let me state that I haven’t tried this myself so please correct me if I am mistaken.

  5. Srinivas says:

    Hi Sloan,

    You have been a savior for me. I am really indebted to you.
    Your valuable insights helped me crack this. The fix is as follows:
    a) I have CronTriggers associated with dynamic jobs so what i did was to provide the mis-fire instruction.

    JobDetail jobDetail = new JobDetail(job.getDescription(), job.getName(), job.getClass());
    CronTrigger crTrigger = new CronTrigger( "cronTrigger", job.getName(), cronExpression); 
    crTrigger.setStartTime(firstFireTime);          crTrigger.setMisfireInstruction(CronTrigger.MISFIRE_INSTRUCTION_DO_NOTHING);      
    scheduler.scheduleJob(jobDetail, crTrigger);    
    

    b)Our mis-fire threshold was pretty high (6000000). So, what i did was to reduce the misfire threshold and it worked like a charm.

    There is a problem with overwriteExistingjobs=true. I will post this in the Spring Forum.

    • sseaman says:

      Not a problem. Glad I could help and please keep me posted on the overwriteExistingJobs issue.

    • San says:

      Hi Srinivas,

      we also have similar problem the PREV_FIRE_TIME is set to -1 and its not triggered after.
      Could you please tell me where and all you’ve done the changes and also provide me the link where you’ve posted regarding this issue in the Spring Forum.
      Thanks.

  6. Marcoartz says:

    Hi!

    Very good article.. but unfortunately it not resolve my problem.

    I use quartz to keep synchronized a table in mssql db with a table in mysql db.

    When I run my algorithm in Test environvent it works.. but when I run it using quartz it not commit changes to db. It do all except commit :@

    Have you ever heard about problem like this?

    Marco

    • sseaman says:

      So it’s able to query the db but just not commit?

      If that’s the case I’m betting it has to do with auto-commit being turned off for the transaction or the wrong transaction being used.

      Where do you have the @Transactional defined? It is on the actual job or do you have it in some sort of wrapper?

      Also, if you are not doing it exactly as my example above, make sure you follow the Quartz documentation on transactions and use JobStoreTX

  7. brian says:

    Hi there,

    fantastic article…however I’m getting the strange error when I try to implement in MySQL

    Caused by: org.quartz.JobPersistenceException: Couldn't store trigger 'caseArchiveSuspendedJobTrigger' for 'caseArchiveSuspendedJob' job:Parameter index out of range (2 &gt; number of parameters, which is 1). [See nested exception: java.sql.SQLException: Parameter index out of range (2 &gt; number of parameters, which is 1).]
    	at org.quartz.impl.jdbcjobstore.JobStoreSupport.storeTrigger(JobStoreSupport.java:1250)
    	at org.quartz.impl.jdbcjobstore.JobStoreSupport$5.execute(JobStoreSupport.java:1156)
    	at org.quartz.impl.jdbcjobstore.JobStoreSupport$40.execute(JobStoreSupport.java:3696)
    	at org.quartz.impl.jdbcjobstore.JobStoreCMT.executeInLock(JobStoreCMT.java:242)
    	at org.quartz.impl.jdbcjobstore.JobStoreSupport.executeInLock(JobStoreSupport.java:3692)
    	at org.quartz.impl.jdbcjobstore.JobStoreSupport.storeTrigger(JobStoreSupport.java:1152)
    	at org.quartz.core.QuartzScheduler.scheduleJob(QuartzScheduler.java:851)
    	at org.quartz.impl.StdScheduler.scheduleJob(StdScheduler.java:254)
    	at org.springframework.scheduling.quartz.SchedulerAccessor.addTriggerToScheduler(SchedulerAccessor.java:340)
    	at org.springframework.scheduling.quartz.SchedulerAccessor.registerJobsAndTriggers(SchedulerAccessor.java:271)
    	at org.springframework.scheduling.quartz.SchedulerFactoryBean.afterPropertiesSet(SchedulerFactoryBean.java:513)
    	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1477)
    	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1417)
    	... 27 more
    Caused by: java.sql.SQLException: Parameter index out of range (2 &gt; number of parameters, which is 1).
    	at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1073)
    	at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:987)
    	at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:982)
    	at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:927)
    	at com.mysql.jdbc.PreparedStatement.checkBounds(PreparedStatement.java:3709)
    	at com.mysql.jdbc.PreparedStatement.setInternal(PreparedStatement.java:3693)
    	at com.mysql.jdbc.PreparedStatement.setString(PreparedStatement.java:4544)
    	at com.mysql.jdbc.BlobFromLocator.length(BlobFromLocator.java:333)
    	at org.quartz.impl.jdbcjobstore.StdJDBCDelegate.getObjectFromBlob(StdJDBCDelegate.java:3463)
    	at org.quartz.impl.jdbcjobstore.StdJDBCDelegate.selectJobDetail(StdJDBCDelegate.java:904)
    	at org.quartz.impl.jdbcjobstore.JobStoreSupport.storeTrigger(JobStoreSupport.java:1206)
    	... 39 more
    

    any ideas?

    • sseaman says:

      That error would be caused by a database issue when trying to write information to the QUARTZ_TRIGGERS tables. Specifically it would appear that the code is setting two parameters when the SQL only specifies one.

      I would try and see the underlying SQL that is being generated as that would give you the best idea of what is going on. If you can get to it please post it and I’ll help however I can.

      Second, I would make sure that you are not running Quartz 2.0 (as it doesn’t work with Spring) and that the tables were created correctly.

      I tried to dig into the source code via Google but didn’t find much.

      Good luck…

      • brian says:

        Thank you so much for your help. I have done what you suggested and attached the source of quartz to investigate further. Bizarrely, the problem appears to come from the following piece of code in StdJDBCDelegate.java (which is the appropriate delegate for MySQL)


        protected Object getObjectFromBlob(ResultSet rs, String colName)
        throws ClassNotFoundException, IOException, SQLException {
        Object obj = null;

        Blob blobLocator = rs.getBlob(colName);
        if (blobLocator != null && blobLocator.length() != 0) {

        So, the exception gets thrown on the very last line when “blobLocator.length()” is invoked. This throws an SQLException.

        Interestingly, when I inspect the contents of the blob at that point, I get the following messed up information for Blob Column Name:

        Blob Column Name: `��

        • brian says:

          Sorry, that formatting cut off the rest of my post.

          Apart from the Blob Column formatting looking a bit weird, other interesting components of the Blob appear to be:

          numPrimaryKeys: 2
          primaryKeyColumns: [`JOB_NAME`, `JOB_GROUP`]
          primaryKeyValues:[caseArchiveSuspendedJob, DEFAULT]
          tableName: `mydomain`.`qrtz_job_details`

          Perhaps this could be a clue to the problem? As the underlying exception from SQL when blob.length() is called comes in as:

          java.sql.SQLException: Parameter index out of range (2 > number of parameters, which is 1)

          Finally, it’s worth noting that the outer sql that is being invoked is:

          SELECT * FROM QRTZ_JOB_DETAILS WHERE JOB_NAME = ? AND JOB_GROUP = ?

          However, that seems to execute fine and it’s only further on when it executes the lines:


          Map map = null;
          if (canUseProperties()) {
          map = getMapFromProperties(rs);
          } else {
          map = (Map) getObjectFromBlob(rs, COL_JOB_DATAMAP);
          }

          where it actually has a problem retrieving stuff from the Blob.

          I hope this makes sense. Any ideas? Thanks so much for your help.

          cheers,
          Brian

          • brian says:

            Interestingly, I saw that there is a quartz property which I can set which determines if you get the JobData from the Blob or not in the code I posted just above. I set this to true


            org.quartz.jobStore.useProperties

            The code then takes a slightly different route but eventually ends up invoking:


            if (canUseProperties()) {
            Blob blobLocator = rs.getBlob(colName);

            Again the blob column name comes in corrupted (but different!)


            `#
            #Wed Aug 03 13:40:58 EDT 2011
            `

            The resultant sql exception then reads:


            com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Unknown column '#
            #Wed Aug 03 13:40:58 EDT 2011
            ' in 'field list'

            Not sure if this helps any…

            thanks again,
            Brian

            • sseaman says:

              Wow, this is getting interesting.

              Humm. It looks like the SQL is being corrupted somehow. The SQL you posted clearly has 2 parameters (JOB_NAME = ? AND JOB_GROUP = ?) so that doesn’t seem like it would cause the issue.

              Have you tried not storing it as a blob? Perhaps MySql has issues with Blobs for some reason in this case (though what that issue may be I don’t know).

              It just seems like there is some sort of Deserialization issue with the blob, but what I just don’t know.

              At this point I’m past my scope of knowledge and think that I may have to unfortunately tell you to try posting to the actual Quartz forums. The developers seem to respond in a fairly timely manner.

              Sorry. If you do find a solution from the forum could you please post it here?

              • brian says:

                Thanks for your help. Yep, I’ll post it on their site. As for your suggestion to not store it as a Blob…I’m using the default create table scripts from Quartz so I suppose I don’t really want to mess with them…I’ll post it on their forum and post a solution if I get one. Thanks again.

              • brian says:

                Just as a last issue, I think that the problem might be a character encoding issue with the Blob. In both cases (whether or not I use the property ‘
                org.quartz.jobStore.useProperties’) the blob columnName comes in corrupt. And in the first case it’s a bunch of messed up symbols which always leads me to think…character encoding. I’ve posted on the Quartz forum, so hopefully will get some insight there…

              • brian says:

                yay! fixed it…it’s related to this bug:

                http://bugs.mysql.com/bug.php?id=24594

                I had emulateLocators=true in my jdbc connection url.

                Removing that fixed the problem

  8. Jagadeesh says:

    Hi,

    I am new to QUARTZ

    1. I have a scenario like i have to schedule new jobs,suspend and delete existing job at run time,and also i have display all the list of schedule jobs in my screen. here i am using JDBC job store to persisting job in QUARTZ tables.

    2. I have schedule new job using existing configuration with different cron expression

    please help me out which approach i have to follow.

    • sseaman says:

      Jagadeesh,

      You would need to implement the code above and they write a web based GUI that can manipulate the Quartz tables to suspend/run/delete the jobs.

      • Jagadeesh says:

        Hi,
        Thanks for replay,
        Is spring supports Quartz 2.0 Version ?

        • sseaman says:

          Please re-read the article as it clearly states that it does not.

          • Jagadeesh says:

            Hi sseaman,

            How do we know that job has been scheduled successfully?
            when ever we are scheduling job it is returning date
            so is their any way we can find out the job is scheduled successfully ?
            in my application after scheduling is done i need to add the scheduled job details into my own database tables with specific ID,then after i need to show scheduled job details in to my screen like :
            JobName :
            Schduled by:
            start date:
            End date:
            status of job

            please guide me how i have to handle this type of scenario.

            • sseaman says:

              If you have quartz storing information in the database (as in my example above) you just need to write code to read the tables. It will tell you most of those things.

  9. Rip says:

    Hi Guys,

    I had a Quartz job running fine in Spring environment .
    But when I changed the job store to JdbcCMT, it gives me the below
    Caused by: java.io.NotSerializableException: Unable to serialize JobDataMap for insertion into database because the value of property ‘userDTO’ is not serializable.

    userDTO has got some information which is populated runtime through the screen before scheduling the job using scheduleCronJob().
    Is there any way to implement this ?

    Thanks,
    Rip

  10. sseaman says:

    Rip,

    It sounds like your userDTO object does not implement Serializable. Try having it implement Serializable. That should fix it.

    Also, remember that any variables in userDTO should be serializable. For instance, if you have a DB connection or File in there, that won’t work and you will need to mark them as transient.

    Can you try that and tell me if it works?

  11. Flabbybaggins says:

    I’ve used Quartz scheduling plenty with Spring on a single server.

    Now I’m working in a clustered environment and need to introduce Quartz for distributed scheduling. Lucky for me Google brought me here first.

    You indeed saved me a good three days of figuring this out on my own.

    Props to you!

  12. xiaofancn says:

    Have such forms of code?

    @JobTaskPool
    @JobDataSource("")
    public class ScheduleJobFactory {
    
    	@Resource
    	MobuserUpdateService mobuserUpdateService;
    	
    	@JobDataSource(tableName="")
    	@Scheduled(cron = "2 * * * * ? ")
    	void println() {
    		System.out.println("ScheduleJobFactory.........................");
    		mobuserUpdateService.MatchingMobUser();
    	}
    }
    
  13. Kevin says:

    I can’t seem to get any Spring dependency injected fields to work with my job, based on this guide. I have the following job class:

    public class MainProcess extends QuartzJobBean {
            @Resource
    	private MyDAO dao;
    
    	public void executeInternal(JobExecutionContext context) {
                //use dao...
            }
            //getters &amp; setters...
    

    And the following quartz xml file:

    			
    				org.quartz.impl.jdbcjobstore.JobStoreTX
    				1800000
    				org.quartz.impl.jdbcjobstore.MSSQLDelegate
    				
    				true
    				900000
    				CLIP_Quartz_Clustered_Scheduler
    				AUTO
    				true
    

    Any ideas what I’m missing?

    • sseaman says:

      Hi Kevin,

      First, ensure that other dependencies are working. For instance, can you get the MyDAO object in another bean that is not using in the scheduling?

      If you can, then check that you did the SpringBeanJobFactory step as that should be what is doing it for you.

      If it still doesn’t work, try autowiring something that isn’t a @Resource to see if at least autowiring works. If it does then we know it is specific to @Resource and not the whole autowiring mechanism.

      Tell me the results and we can go from there

      • Kevin says:

        Thanks for your help in looking into this. I figured out what the issue was: Since I’m also using JSF and MyBatis in my application, MyDAO is actually a MyBatis mapper interface. So while I was able to inject it into any non-Quartz bean (i.e. JSF managed beans), for what ever reason, MyBatis wasn’t providing a proxy implementation class for my mapper interface when my Quartz job ran. As a result, I was getting NullPointerExceptions for all my DAOs. After testing with a non-MyBatis bean, your guide works as described. I’ll just have to look into a workaround for using this with MyBatis mapper interfaces. Again, thanks for your help and guide!

  14. Kevin says:

    Hello again,

    I can’t seem to get transactions working… I’ve followed your guide and here’s my job bean setup:

    public class MainProcess extends QuartzJobBean {
    	@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    	public void executeInternal(JobExecutionContext context) {
    		//use DAO to do stuff with DB...
    	}
    }
    

    I’ve defined the following as my datasource and transaction manager in my applicationContext.xml: (Please excuse the formatting, but your blog doesn’t allow for code tags in comments…)

    &lt;bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"&gt;
    	&lt;property name="dataSource" ref="dataSource" /&gt;
    &lt;/bean&gt;
    &lt;bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean"&gt;
    	&lt;property name="jndiName" value="jdbc/cla_clip" /&gt;
    &lt;/bean&gt;
    

    I’ve added both the dataSource and transactionManager property to my Quartz SchedulerFactoryBean config to reference the above beans. I can confirm that the above datasource and transactionManager work for operations started from non-Quartz beans, even when accessing the same DAO. However, Spring doesn’t seem to recognize that my Quartz Job bean is a Spring bean, and thus, the @Transactional annotation doesn’t seem to have any affect. I’m running this on WebSphere 7 with JSF 2, Spring 3, and Quartz 1.8.5. Any thoughts?

    • sseaman says:

      Hi Kevin,

      First, this is a WordPress blog so to post code do a:
      [language] [/language] where language is xml, java, whatever.

      As to your issues, since you are running Websphere 7 I assume you already have all the upgrades to use JPA 2.0 so that eliminates that possibility.

      I see you are extending QuartzJobBean. That could be the issue. Spring only weaves transactions on public methods that get directly called. Since you are using a method that is actually called by another method in the class (execute()) Spring will not weaving the transaction in.

      Could you try it with the execute method and see if that fixes it?

  15. Komal says:

    Hi,

    I have a spring application. Can you tell me how to configure quartz to read from some other properties file instead of quartz.properties.

    Thanks,
    Komal

  16. Vikram says:

    Sloan,

    This is Vikram. I guess you recognized me.

    I have a job that gets triggered twice a day say example 9:00 am and 5 :00 pm
    If the job that triggered at 9:00 am is still running until 5:00 PM. I don’t want it to trigger the job. So I will make this job a Stateful job.

    My question is if I want to trigger a job manually once I restart the server or when ever the job is running. If I change the next_fire_time in qrtz_triggers table(for example to 11:00 am). And this job with trigger at 11:00 am. But what would be the next_fire_time value.

  17. sseaman says:

    Hey Vikram! Hope all is well!

    Sorry for the delay in responding. You pose a very good question. You are correct about the Stateful job, that will take care of a job overrunning into the next schedule time. As to the main question, I did some digging and didn’t find any real answer so I’m going to have guess that the next fire time would be 5 pm.

    I dug very quickly through the source and didn’t find what I was looking for but I can tell you that the usual way this type of thing is implemented is that the next_fire_time is estimated based on when the job is fired. So if it is fired at 11am it will make a call to determine the next_fire_time which should be 5pm.

  18. sirine says:

    Thank you for you post!
    i am having a problem with the property :
    I get an error that says :
    SchedulerFactoryBean needs to be set up in an ApplicationContext to be able to handle an ‘applicationContextSchedulerContextKey’
    Plz , do you have any idea how to fix it?
    i did many researches but i didn’t find any sloution!
    thank you!

    • sseaman says:

      The error is coming from line 691 in the SchedulerFactoryBean (here.

      It means that the application context was not set in the bean itself. This is very odd as it should be done for you because the SchedulerFactoryBean implements ApplicationContextAware so Spring should be setting it for you.

      I think something else might be wrong with your Spring implementation because it is not setting the ApplicationContext as it should be. Are you running Spring 3.0?

  19. Deryl Spielman says:

    From the examples here the cron expressions are stored in the spring config. I am wondering how to keep the cron schedule in a database and have a user add/update schedules for a particular job. Do you think this is possible?

  20. Vikram says:

    Sloan,

    I am using quartz scheudler(without spring or hibernate).

    I have configured using

    quartz.properties file

    org.quartz.scheduler.skipUpdateCheck = true 
    org.quartz.scheduler.instanceName = DatabaseScheduler 
    org.quartz.scheduler.instanceId = NON_CLUSTERED 
    org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX 
    org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate 
    org.quartz.jobStore.dataSource = quartzDataSource 
    #org.quartz.jobStore.tablePrefix = QRTZ_ 
    org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool 
    org.quartz.threadPool.threadCount = 5  
    
    
    org.quartz.jobStore.tablePrefix = Q_
    # Optional JNDI datasource 
    org.quartz.dataSource.quartzDataSource.jndiURL=jdbc/BatchSMSDS
    org.quartz.dataSource.quartzDataSource.java.naming.factory.initial=com.ibm.websphere.naming.WsnInitialContextFactory
    org.quartz.dataSource.quartzDataSource.java.naming.provider.url=corbaloc:iiop:localhost:2809
    
    #org.quartz.dataSource.quartzDataSource.java.naming.security.principal=admin 
    #org.quartz.dataSource.quartzDataSource.java.naming.security.credentials=123  
    # MySchedule scheduler service parameters 
    
    
    myschedule.schedulerService.autoStart = true 
    myschedule.schedulerService.waitForJobsToComplete = true 
    

    quartz_jobs.xml

    BillReadyEmailNotificationProcess
    SAMPLE
    BillReadyEmailNotification
    com.vzw.cc.capo.billready.BillReadyEmailNotificationProcesser
    &lt;!-- false --&gt;
    true
    false 
    			
    BillReadyEmailNotificationTrigger
    SAMPLE
    BillReadyEmailNotification
    SAMPLE
    0 7 8 30 MAR ?  
    &lt;!--0 15 10 * * ? 2005  --&gt;
    

    The application starts without any error. It is on Websphere application Server 7.

    I am expecting it to create triggers in the database(Q_triggers). But I don’t see any data inside quartz tables .

    Any idea why?

    Thanks.

    • sseaman says:

      Don’t know how much help I’m going to be able to provide on this one, sorry. I can suggest that you turn on any JDBC logging that you can (perhaps using log4jdbc) to see if it is even attempting to get to the db.

      Does the job at least fire or does it do nothing at all?

  21. Rips says:

    Hi Seaman,

    I am facing one issue for quite long now.I hope you can point me in the right direction.
    I have a quartz Job which gets fired correctly at the nextFireTime.

    But it fails because of the below.

    In my job I am calling DAO method which create a record in the database.
    Soon after that due to some business logic,I have to retrieve the above created record.
    But whenever it tries to
    find the record with the key that was generated some time before,it is not able to find the record.
    Also I debugged and found that that when control hits the find operation,the record is not yet commited into the database

    I have configured the persistent job as below:

    applicationContext
    

    Same thing is carried out through NonQuartz environment and it works fine.
    I have been trying differnt things but couldnt figure it ou till now.

    Please let me know if you require further information.
    Regards,
    Rips

    • sseaman says:

      Rips,
      It could be a few things:
      1) If you are using the same object that you wrote to and expect it to have the key you may have to flush the object. .flush(Object) from your DAO provider should do it. In case you don’t know, a flush will ensure that the object has the latest info from the DB.
      2) If you are retrieving the object but it is simply not finding it then this would suggest that your DAO transaction is not being commited. In this case you would have told the DAO provider to save the object but since it wasn’t committed it’s not in the db. To check this I would put a break point right before where you retrieve the object and when you hit the break point see if the data is in the DB

      Hope tha helps!

      • Rips says:

        Thanks for your reply.
        I am trying to retrieve the data soon after it has been saved through JPA.
        I have debugged it.when it hit that place,data is not saved in the database.

        But the same things work through NonQuartz process(Screen).
        It looks like some transaction commiting issues in the quartz process.
        Do you have any idea about this ?

        • sseaman says:

          Where do you have your @Transactional placed? Is it on the method that saves the bean and if so is that method a high level method (i.e. is it called from an external entitiy)?

          Perhaps that has something to do with it…

          • Rips says:

            I have transactional advices on the service layer.

            QuartzJob calls one Service.method1 which in turn calls Service1.create and soon after Service1.find(of the just created record).
            Now this find fails to find the record.

            Now same above process works fine when Service.method1 is called from Web(UI).
            Can be some transaction/session related problem ?

            • Rips says:

              Hi Sloan,

              Finally I have been able to fix the problem with the below bit of code

               EntityManagerFactory emf = null;
                  EntityManager em = null;
                  try {
                    ApplicationContext appContext = (ApplicationContext) jobExecutionContext.getScheduler().getContext().get("applicationContext");
                    EntityManagerFactory emf = (EntityManagerFactory) appContext.getBean("entityManagerFactory", EntityManagerFactory.class);
                    em = emf.createEntityManager();
                    TransactionSynchronizationManager.bindResource(emf, new EntityManagerHolder(em));
               
                    /* Some action */
               
                  } catch (Exception e) {
                    //Exception handling..
                    e.printStackTrace();
                  }
                  finally{
                    if (em!=null) em.close();
                  }
                }
              

              Details are in the below site
              Quartz, Spring & Hibernate/JPA

              Hope it helps someone else.

              • sseaman says:

                Interesting approach. You might want to wire in the EntitiyManager as a property to the bean so you don’t have to hard code as much.

                Thanks for posting the solution!

                • Rips says:

                  Sloan,
                  One another thing that I observed is that I just pass the data source as the parameter in the scheduler in the spring xml.and doesnt set any delegate as Quartz manual asks us to do .But it still works and stores the job information in the database.Even in your example you are setting the driver delegate class.
                  Is it not not mandatory ?

                  • sseaman says:

                    I would think it is manditory as it would need to know what type of DB it is against but it sounds like there might be a default or auto-discovery from what you are seeing

  22. shri says:

    Thanks for sharing the info.
    I tried to implement just by following as you mentioned, also executed the DB script for creating default tables required for quartz, still i am seeing the below error :


    Caused by: org.quartz.JobPersistenceException: Couldn't retrieve trigger: ORA-00942: table or view does not exist
    [See nested exception: java.sql.SQLSyntaxErrorException: ORA-00942: table or view does not exist
    ]
    at org.quartz.impl.jdbcjobstore.JobStoreSupport.retrieveTrigger(JobStoreSupport.java:1576)
    at org.quartz.impl.jdbcjobstore.JobStoreSupport.retrieveTrigger(JobStoreSupport.java:1552)
    at org.quartz.impl.jdbcjobstore.JobStoreSupport$10.execute(JobStoreSupport.java:1544)
    at org.quartz.impl.jdbcjobstore.JobStoreCMT.executeInLock(JobStoreCMT.java:242)
    at org.quartz.impl.jdbcjobstore.JobStoreSupport.executeWithoutLock(JobStoreSupport.java:3673)
    at org.quartz.impl.jdbcjobstore.JobStoreSupport.retrieveTrigger(JobStoreSupport.java:1541)
    at org.quartz.core.QuartzScheduler.getTrigger(QuartzScheduler.java:1429)
    at org.quartz.impl.StdScheduler.getTrigger(StdScheduler.java:551)
    at org.springframework.scheduling.quartz.SchedulerAccessor.addTriggerToScheduler(SchedulerAccessor.java:328)
    at org.springframework.scheduling.quartz.SchedulerAccessor.registerJobsAndTriggers(SchedulerAccessor.java:271)
    at org.springframework.scheduling.quartz.SchedulerFactoryBean.afterPropertiesSet(SchedulerFactoryBean.java:513)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1477)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1417)
    ... 36 more

    Please help.

    Thanks

    • sseaman says:

      If you take a look at the source of StdJDBCConstants (found at http://kickjava.com/src/org/quartz/impl/jdbcjobstore/StdJDBCConstants.java.htm) you can see the list of tables that it will use

      Make sure each table used exists in your database.

      Good luck!

      • shri says:

        Thanks for the quick reply sloan.
        I was able to fix that issue by recreating all the tables after adding some prefix, not really sure what is the problem earlier.

        Now i am worried about the number of tables required, is there anyway we can optimize the number of tables to be created ?

        “how quartz is making use of these different tables for which purpose”. do i need to spend more time debugging into quartz source to find this out ?

        Thanks

        • sseaman says:

          There is no way to optimize the # of tables without modifying the actual Spring code itself. You could put a ticket in requesting this but I honestly think it wouldn’t get much response, sorry.

          • shri says:

            Thanks for all the help.

            One more thing i wanted to know is how can i turn this scheduler OFF / ON ?

            I am looking at setting autoStartup property to false and scheduler stays disabled i guess.
            Is this the right way to do or any other approach ??

            Thanks
            Shri

            • sseaman says:

              Well, autoStartup will not start the schedule until you do it manually.

              i think what you might be referring to though is the one of the pause*() methods off of the scheduler object. That would disable the jobs.

              • shri says:

                Using pause* methods to disable would not be a good idea for our requirement.
                It would be great if we can enable / disable this just by changing a property (like autoStartup – had to restart the server though) or changing a value in DB table (this would not require server restart).

                Anyways, thanks again for the response.

  23. Arvinder says:

    Hi

    Nicely written and quite informative article.
    I have a scenario wherein I am looking to run my job/task in couple of ways:
    1. As a scheduled job to be run on hourly basis.
    2. Triggered by exposing a http service call. Http Service call can pass additional info which should alter job’s behavior (say the amount of data it has to process can be controlled by service call).

    The job should always run in a sequence i.e. if a rest call has triggered the process, then scheduled job should wait till it completes n vice versa.
    Currently I am sequencing rest calls separately through blocking queues & persisting rest calls in db to ensure they are not lost if my app goes down.

    I was thinking if I can make use of quartz for both rest calls & scheduled trigger. I have couple of questions in this regard:
    – Can i execute the trigger “now”(in case of rest calls) without affecting it’s next run time ?
    – Does quartz provide any inbuilt queuing (in case n rest calls are made in parallel) ?

    Thanks,
    Arvinder

    • sseaman says:

      My suggestion would be that when a REST call is made you programmatically schedule a job for one execution. That way you are not just running the code the job would run but you are instead making Quartz aware that a job is to be run.

      For instance: get the instance of the hourly scheduled job from Quartz, make a copy of it, alter your job behaviors, and then schedule the execution time for one minute from when the code is run. That way you should leave the original job alone and still execute your new job.

      That’s off the top of my head so the theory could be a little off…

  24. San says:

    Hi Sseaman,

    We are using Quartz 1.8.6 We have very weird issue that the job gets added in db but is never triggered or executed and also the PREV_FIRED_TIME is set to -1. Could you tell me what might be the reason? And also possible solution if any…

    • sseaman says:

      PREV_FIRED_TIME = 1 makes sense as the job has not run before so Quartz uses the -1 as the default.

      Why the job never fired could be a lot of things:
      1) Did you see any errors in your logs (you may want to make your logs go to TRACE level)
      2) When do you have the job set to run? I’ve seen people programmatically set the time based on when the app starts but by the time the Quartz code fires it is past the start time. If you are doing something like this, add 1-2 minutes to the scheduled time
      3) Does your job work correctly when run manually (outside of Quartz execution)?

  25. Rips says:

    Hi Sseaman
    I have been successfully using quartz in my application the way you have suggessed in this blog. Basically I have quartz bundled inside the webapp1 which is running inside the Jboss.

    But we have got another webapp2 running in the jboss which needs to have quartz job as well

    Now what I need to do is to have quartz scheduler running in the jboss as some kind of service and both the webapps should be able to register their jobs on the single quartz scheduler.

    I dont want to define the same scheduler again in webapp2 which will cause 2 quartz scheduler running.
    Do you have any idea how to achieve this with quartz ?

    • sseaman says:

      You could do a few things:
      1) Run Quartz in ‘Clustered’ mode: Documentation
      2) Try writing your own interface to their JMX beans
      3) Write your own custom REST based wrapper for Quartz and have both of your apps interact with it

      I’d look into them in that order

      • Rips says:

        Thanks Sseaman for your suggestions.I was looking at the other option as well which I found out having quartz run as jboss service.Link is below.please let me know your suggestion on this.
        http://zunkthoughts.blogspot.co.uk/2008/12/quick-howto-quartz-with-jboss.html

        • sseaman says:

          I’m not a big fan of using a version of something like Quartz that is built into the app. You can’t easily upgrade your version and you normally have to do some type of work-around to get all the functionality you want. I would follow the authors suggestion of using your own libraries and configure it how you want.

          • Rips says:

            As suggested I had a look at the clustering option but I think it wont work as Clustering is more suited for homogeneous applications running on several machines, not heterogeneous ones on single node.

            I finally got quartz service running from jboss.Also I was able to schedule jobs from the webapp1.But when the job was getting fired,it was giving class(job class) not found exception.So I pulled the class into a separate jar and put it in jboss lib and successfully got quartz running.But right now the sample job just have a system.out but when tomorow I will require beans injected into it and doing some transaction how it will be able to look up beans and other stuff.

            • Rips says:

              Finally got it working using quartz-jboss service.I have put all details in the answer in the below link in case anyone else is looking for the same:

  26. Steve says:

    Brilliant article. Thanks very much.

  27. Wenjun says:

    Hello Sseaman,

    Thank you for the article. It was very helpful when I was learning the stuff.

    My setup is similar to your article, except we are using HibernateTransactionManager instead of JpaTransactionManager. It has been working fine. But recently we upgraded MySQL from 5.1 to 5.5.20. We started having issues since the upgrade. I posted the details at http://stackoverflow.com/questions/12042782/saving-quartz-triggers-in-mysql.

    I also posted the same question at Springframwork forum. So far there have been no responses for both postings. We’d greatly appreciate it if you can take a look and provide some feedback.

    Thanks

  28. Balasubramanian J says:

    Good One Seamen. I have been searching like this.

    I have one question on how to implement the crontrigger dynamically without specifying the cron trigger options in the XML configurations.

    The requisite is given below.
    I have a requirement, where the crontrigger parameters can be changed from GUI and based on the configured values, I should schedule the job. Can you help me in this regard? How to configure the trigger parameters dynamically without configuring in the XML ?

  29. nicus says:

    If you use AspectJ in your Spring project (putting <context:spring-configured/> in your conifiguration, as Spring Roo do), annotating your jobClass (mycode.job.CaseArchiveSuspendedJob in your example) with @Configurable should allow autowiring in it, without extending SpringBeanJobFactory

  30. Raja says:

    Hi Sseaman, great stuff, really helpful.

    In the project I’m working for we are building some kind of job scheduling mechanism, based on Quartz 1.8.x inside a Spring web app.
    We kinda followed your guidelines for setting up the Scheduler, all looks pretty much the same beside the appContext wiring into jobs. however, I don’t see it as an issue.

    The problems start when we run our application in a cluster, in particular we see duplicate jobs and also deadlocks in database (we support Oracle, MSSQL, MySQL and PostgreSQL).

    I may be completely off but I suspect we have an issue with the instanceName value, we set it up to a fixed value and the instanceId is set to AUTO. However, selecting for the instance_name from the SCHEDULER_STATE table, reveals that the value we set for “org.quartz.scheduler.instanceName” is completely ignored, the value I see there is something line .

    Any expect advice about what to look for?
    thanks in advance.

  31. sam das says:

    hi seseaman,
    Great article , helped me get around the Issue of un serializable jobDataMap , with Spring.
    Thanks a bunch

  32. What’s up, I log on to your blogs on a regular basis. Your humoristic style is witty, keep up the good work!

  33. John Burgoon says:

    Thank you very much for this article. Can you possibly do a page about how you set up to test such changes? Do you use a clustered test environment before moving this to production? I’d appreciate your acumen on how to test changes before moving to Prod.

    Cheery regards from central Indiana,
    John

    • sseaman says:

      Hi John,

      Sorry, but I haven’t really touch this in some time. Off the top of my head, I’d follow a typical software development lifecycle pattern with your integration test (which is what this would be) occurring on your build servers in your beta or gamma pipeline.

  34. kusuma says:

    Hi Sseaman,

    I have deployed wars in JBOSS. I had quartz cron jobs running and when i do Ctrl+C in command Prompt which is trying to unschedule job.

    http://stackoverflow.com/questions/24776554/error-occurred-while-unscheduling-jobs.
    12:39:50,020 ERROR [com.kony.sync.console.job.scheduler.ConsoleJobScheduler] (ServerService Thread Pool – 20) Error occurred while unscheduling jobs for application ID:PaaSJobs: org.qu
    artz.JobPersistenceException: Failed to obtain DB connection from data source ‘syncQuartzDS’: java.sql.SQLException: Could not retrieve datasource via JNDI url ‘java:jboss/datasources/C
    onsoleDB’ java.sql.SQLException: javax.resource.ResourceException: IJ000451: The connection manager is shutdown: java:jboss/datasources/ConsoleDB [See nested exception: java.sql.SQLExce
    ption: Could not retrieve datasource via JNDI url ‘java:jboss/datasources/ConsoleDB’ java.sql.SQLException: javax.resource.ResourceException: IJ000451: The connection manager is shutdow
    n: java:jboss/datasources/ConsoleDB]
    at org.quartz.impl.jdbcjobstore.JobStoreSupport.getConnection(JobStoreSupport.java:715) [quartz-all-1.8.6.jar:]
    at org.quartz.impl.jdbcjobstore.JobStoreTX.getNonManagedTXConnection(JobStoreTX.java:69) [quartz-all-1.8.6.jar:]
    at org.quartz.impl.jdbcjobstore.JobStoreSupport.executeInNonManagedTXLock(JobStoreSupport.java:3785) [quartz-all-1.8.6.jar:]
    at org.quartz.impl.jdbcjobstore.JobStoreTX.executeInLock(JobStoreTX.java:90) [quartz-all-1.8.6.jar:]
    at org.quartz.impl.jdbcjobstore.JobStoreSupport.removeTrigger(JobStoreSupport.java:1458) [quartz-all-1.8.6.jar:]
    at org.quartz.core.QuartzScheduler.unscheduleJob(QuartzScheduler.java:965) [quartz-all-1.8.6.jar:]
    at org.quartz.impl.StdScheduler.unscheduleJob(StdScheduler.java:290) [quartz-all-1.8.6.jar:]
    at com.kony.sync.console.job.scheduler.ConsoleJobScheduler.unscheduleJob(ConsoleJobScheduler.java:225) [syncconsole.jar:]
    at com.kony.sync.services.context.SyncContextListener.contextDestroyed(SyncContextListener.java:221) [classes:]
    at org.apache.catalina.core.StandardContext.listenerStop(StandardContext.java:3427) [jbossweb-7.2.2.Final-redhat-1.jar:7.2.2.Final-redhat-1]
    at org.apache.catalina.core.StandardContext.stop(StandardContext.java:3920) [jbossweb-7.2.2.Final-redhat-1.jar:7.2.2.Final-redhat-1]
    at org.jboss.as.web.deployment.WebDeploymentService.doStop(WebDeploymentService.java:171) [jboss-as-web-7.3.0.Final-redhat-14.jar:7.3.0.Final-redhat-14]
    at org.jboss.as.web.deployment.WebDeploymentService.access$100(WebDeploymentService.java:60) [jboss-as-web-7.3.0.Final-redhat-14.jar:7.3.0.Final-redhat-14]
    at org.jboss.as.web.deployment.WebDeploymentService$2.run(WebDeploymentService.java:113) [jboss-as-web-7.3.0.Final-redhat-14.jar:7.3.0.Final-redhat-14]
    at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source) [rt.jar:1.6.0_26]
    at java.util.concurrent.FutureTask$Sync.innerRun(Unknown Source) [rt.jar:1.6.0_26]
    at java.util.concurrent.FutureTask.run(Unknown Source) [rt.jar:1.6.0_26]
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(Unknown Source) [rt.jar:1.6.0_26]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) [rt.jar:1.6.0_26]
    at java.lang.Thread.run(Unknown Source) [rt.jar:1.6.0_26]
    at org.jboss.threads.JBossThread.run(JBossThread.java:122)
    Caused by: java.sql.SQLException: Could not retrieve datasource via JNDI url ‘java:jboss/datasources/ConsoleDB’ java.sql.SQLException: javax.resource.ResourceException: IJ000451: The co
    nnection manager is shutdown: java:jboss/datasources/ConsoleDB
    at org.quartz.utils.JNDIConnectionProvider.getConnection(JNDIConnectionProvider.java:163) [quartz-all-1.8.6.jar:]
    at org.quartz.utils.DBConnectionManager.getConnection(DBConnectionManager.java:109) [quartz-all-1.8.6.jar:]
    at org.quartz.impl.jdbcjobstore.JobStoreSupport.getConnection(JobStoreSupport.java:712) [quartz-all-1.8.6.jar:]
    … 20 more

    Terminate batch job (Y/N)?

    What should i do to unschedule job on shutdown the server

    Thanks,
    Kusuma

    • sseaman says:

      From the error it looks like your DB connection is shut down before you can unschedule the jobs. You’ll need to have the unschedule task occur prior to db shutdown.

  35. Pingback: How to use Quartz 2.2 with Spring 3.2.x on WebSphere AS | Monobit Solutions

  36. Sarvi says:

    We have created schema and tables for JobStore and using it for a Spring Quartz implementation for a particular application.

    Now we are planning use the same schema and tables for a new application to use Spring Quartz implementation.

    Can we re-use the schema/tables, Does it break the functionality?

    • sseaman says:

      PK’s (Primary Keys) will be derived from the DB, not the application, so you should be OK for that. The issue will be configuration pollution as you’ll have two different configurations in the same table.

      If this is a brand new application you should but the tables in a different table space under a different owner if possible

  37. Sarvi says:

    Same jobs are getting added in the Job Store each we deploy the code, even though the overwriteExistingJobs property value is true in SchedulerFactory. Using Job Stores to persist the job details.
    Using Quartz 1.8.4 version and spring 3.2.4 version.

    Please let me know how can we overwrite the jobs in Data Store.

    • sseaman says:

      Might be something with the sequencer and the ability to determine if the job already exists. Worst case you could clear the jobs in your own code via JDBC, but that shouldn’t be necessary.

      Check the names of the jobs and ensure you don’t have duplicates. That can sometimes cause this

  38. shubam kumar awasthi says:

    Hello sir , i want to trigger some event after a month in my application. the problem is i am using hibernate for persistence ,when i am implementing the trigger ,it is triggered but SessionFactory is null. and i hav to persist that data which comes in my triggered method but Sessionfactory is getting null. i am searching the solution since two days, but all in vain…please suggest how i can implement Quartz scheduler with spring and hibernate. So far i have found somewhere that quartz scheduler creates a seperate table and thread to perform the Job. so session is null when we use hibernate . please help me out …i need solution for it. thanx in advance

  39. manju says:

    good tutorial..

  40. Hi, Many thanks for your article.

    I have a question, when we want to make a refresh of the Quartz Schedulers, then we need to the delete all the data from the Quartz Tables also right, bcoz the earlier data may lead to problems and my database need to be fresh when a new deployment is done? If we directly delete the data from all the tables from SQL Query browser which are related to Quartz, does it makes a problem for next Initialisations of Triggers.

    • sseaman says:

      If you just want to refresh (in terms of updating some data), it would be better to just do an update and have quartz reset what it has in memory.

      If you are really changing things, then a delete would work, but you still may require a bounce of quartz. I’m not sure what it keeps in memory these days, but there is a chance it would keep state in memory which might need reset.

      My suggestion is to take a beta stage and do a test to see what happens. Good luck!

  41. diwam says:

    Hi, thanks for the detailed explanation.

    I have a question, when we are running the schedulers in a cluster, is it possible to run a job on a specific node than the cluster to make a decision on which node the job needs to be executed (not for all the jobs, can i control a specific job which creating to run on a specific node of a cluster).

    Thanks in advance.

    • sseaman says:

      Not without adding some custom logic to the library. Right now it just has basic locking for “did the job run”. You could always have a specific instance of quartz running just for that node but it would decentralize things.

      If you want to be really scalable I would suggest against though this as the node could go down and the job might not run.

      • diwam says:

        Thanks for your reply. I understand that, there is no direct mechanism available at this point of time to run a job on a specific node.

        I have another point to clarify. Could you please check.

        # If I have 4 nodes; can i made 2 nodes into one cluster and other 2 nodes into another cluster; If there are two distinct functional jobs are running, i don’t want them to share across mt cluster groups. Is this possible?

        Thanks in advance.

        • sseaman says:

          If you have the jobs in different DB’s or again, hack the system a bit and mark jobs for specific clusters. You just then have each cluster have an ID that is used when checking for jobs to run.

  42. diwam says:

    Thanks. I could able to make use of instance names to have different cluster instances running on different nodes which will make the jobs run on desired cluster instance based on my configuration.

    During my verification, i found the following.

    # For example, I have a cluster with 2 nodes (with same property file on each node). I configured a test job which runs for every 30 seconds (just to display a console message for testing purpose). I could see that, one of the node (lets say node-1) picking the job and only that node is picking for each trigger time and executing the job. I expect quartz cluster to balance with other node as well i.e. with node-2 when trigger time comes. But node-2 picked the job only when i made node-1 shutdown. am i missing some thing? Please let me know.

    Thanks in advance.

  43. sseaman says:

    Not sure to be honest. I would think that it would distribute it as well. Sorry I don’t have a better answer for you.

  44. Harry Yuan says:

    Are there any tables need to be created in the database? Looks like so from reading your post. If so, where can I find DDL script?

  45. JoAnn Brereton says:

    I liked this article and its insight into clustering with Quartz.

    I have something which I hope is a pretty simple question that is related to clustering.

    One use case we’re trying to attack is to have the ability to standby/start all our instances across the cluster on demand.

    In order to do that, I need something that gives me all the Scheduler instances so that I can run Scheduler.standby() or Scheduler.start() on each of them.

    Is there a way to do this at all? We’re using 1.8.6 here. Getting a single scheduler and setting it to standby only seemed to affect it, not other cluster members. Is there a way to communicate standby/start to all the instance members?

Leave a Reply

Your email address will not be published. Required fields are marked *