multiple jobs are triggered from quartz scheduler even if the job is a StatefuJob
Hi All,
First I couldn't find a Spring-Quartz-Integration forum so putting it here, sorry if putting in wrong one.
Scenario:
I have a spring-batch project where the scheduler is Quartz API and am using a cron trigger for it. I have few jobs which are scheduled in 30 seconds, and am running in clustered environments so most of the time the jobs are completed within 30 seconds, but in some cases (depending on the data that my reader reads and processor processes) it executes more than 30 seconds and I don't want a concurrent job to be fired when another one is still running.
I am using spring 3.0.5.RELEASE, spring-batch-2.1.7.RELEASE and quartz-1.8.6.
Below are my configuration:
pom.xml
All other related dependencies (spring & others) are excluded for brevity.
configuration for spring applicationContext.xml
Code:
<!-- quartz scheduler factory which creates all job triggers -->
<bean id="quartzScheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="dataSource" ref="quartzDataSource"/>
<property name="transactionManager" ref="quartzTransactionManager"/>
<property name="overwriteExistingJobs" value="true"/>
<property name="autoStartup" value="true" />
<property name="waitForJobsToCompleteOnShutdown" value="true"/>
<property name="applicationContextSchedulerContextKey" value="applicationContext"/>
<property name="schedulerContextAsMap">
<map>
<entry key="jobLocator" value-ref="jobRegistry" />
<entry key="jobLauncher" value-ref="jobLauncher" />
</map>
</property>
<property name="triggers">
<list>
<ref bean="testJobTrigger" />
<ref bean="testJobOneTrigger" />
</list>
</property>
<property name="quartzProperties">
<props>
<!-- below values can also be externalized from property file loaded via spring if needed, instead of hard coding -->
<prop key="org.quartz.scheduler.instanceName">TestBatchScheduler</prop>
<prop key="org.quartz.scheduler.instanceId">AUTO</prop>
<prop key="org.quartz.jobStore.misfireThreshold">100</prop>
<prop key="org.quartz.jobStore.class">org.quartz.impl.jdbcjobstore.JobStoreTX</prop>
<prop key="org.quartz.jobStore.driverDelegateClass">org.quartz.impl.jdbcjobstore.oracle.OracleDelegate</prop>
<prop key="org.quartz.jobStore.tablePrefix">QRTZ_</prop>
<prop key="org.quartz.jobStore.isClustered">true</prop>
<!-- <prop key="org.quartz.threadPool.class">org.quartz.simpl.SimpleThreadPool</prop> -->
<prop key="org.quartz.threadPool.class">org.springframework.scheduling.quartz.SimpleThreadPoolTaskExecutor</prop>
<prop key="org.quartz.threadPool.threadCount">30</prop>
<prop key="org.quartz.threadPool.threadPriority">5</prop>
</props>
</property>
</bean>
<bean id="testJobTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="testJobDetail" />
<property name="misfireInstructionName" value="MISFIRE_INSTRUCTION_DO_NOTHING"/>
<property name="cronExpression" value="20/30 * * * * ?" />
</bean>
<bean id="testJobDetail" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass" value="mycompany.spring.batch.test.quartz.QuartzJobLauncher" />
<property name="group" value="test-batch" />
<property name="jobDataAsMap">
<map>
<entry key="jobName" value="testJob"/>
</map>
</property>
</bean>
<bean id="testJobOneTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="testJobOneDetail" />
<property name="misfireInstructionName" value="MISFIRE_INSTRUCTION_DO_NOTHING"/>
<property name="cronExpression" value="25/30 * * * * ?" />
</bean>
<bean id="testJobOneDetail" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass" value="mycompany.spring.batch.test.quartz.QuartzJobLauncher" />
<property name="group" value="test-batch" />
<property name="jobDataAsMap">
<map>
<entry key="jobName" value="testJobOne"/>
</map>
</property>
</bean>
<bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository" />
<property name="taskExecutor">
<bean class="org.springframework.core.task.SimpleAsyncTaskExecutor" />
</property>
</bean>
<batch:job id="testJobOne" job-repository="jobRepository">
<batch:step id="testJobOneStep">
<batch:tasklet transaction-manager="quartzTransactionManager" ref="testTaskletOne"/>
</batch:step>
</batch:job>
<bean id="testTaskletOne" class="mycompany.spring.batch.test.TestTaskletOne"/>
<batch:job id="testJob" job-repository="jobRepository">
<batch:step id="testJobStep">
<batch:tasklet transaction-manager="quartzTransactionManager" ref="testTasklet"/>
</batch:step>
</batch:job>
<bean id="testTasklet" class="mycompany.spring.batch.test.TestTasklet"/>
<bean id="quartzDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" lazy-init="true" destroy-method="close">
<property name="driverClass" value="${batch.jdbc.driver}"/>
<property name="jdbcUrl" value="${batch.jdbc.url}"/>
<property name="user" value="${batch.jdbc.user}"/>
<property name="password" value="${batch.jdbc.password}"/>
<property name="maxPoolSize" value="${batch.jdbc.connection.pool.maxsize}"/>
<property name="minPoolSize" value="${batch.jdbc.connection.pool.minsize}"/>
</bean>
<bean id="quartzTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="quartzDataSource" />
</bean>
<bean id="jobRepository"
class="org.springframework.batch.core.repository.support.JobRepositoryFactoryBean">
<property name="dataSource" ref="quartzDataSource" />
<property name="transactionManager" ref="quartzTransactionManager" />
</bean>
<!-- define a job registry over here, we can define our own , here one in Map implementation -->
<bean id="jobRegistry" class="org.springframework.batch.core.configuration.support.MapJobRegistry" />
<bean class="org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor">
<property name="jobRegistry" ref="jobRegistry"/>
</bean>
<bean id="jobExplorer"
class="org.springframework.batch.core.explore.support.JobExplorerFactoryBean">
<property name="dataSource" ref="quartzDataSource"/>
</bean>
<bean id="lobHandler" class="org.springframework.jdbc.support.lob.DefaultLobHandler" />
<bean id="incrementerParent" class="org.springframework.jdbc.support.incrementer.OracleSequenceMaxValueIncrementer">
<property name="dataSource" ref="quartzDataSource" />
<property name="incrementerName" value="ID" />
</bean>
Other configurations or codes are put in below as it crosses max limit.
In my console I can see multiple jobs are fired, and quartz tables has the next firetime is updated by adding schduled rate to current execution time. I couldn't try new quartz API (2.X) with respective annotations of StatefulJob as I have to upgrade to spring 3.1 as 3.0.5 doesn't support it , and it will be very costly for me to test again my whole applications.
I am not sure where I am going wrong, appreciate for any pointers.
Thanks in advance
Ajit Das