Question

I'm trying to get a clustered, Spring-managed Quartz environment going and I'm running into problems with the transaction features:

2014-02-06 13:59:00,015 ERROR org.quartz.core.ErrorLogger  - Error executing Job (DEFAULT.idleDeviceJob: couldn't begin execution.
org.quartz.SchedulerException: UserTransactionHelper could not lookup/create UserTransaction. [See nested exception: javax.naming.NamingException: Cannot create resource instance]
    at org.quartz.ee.jta.UserTransactionHelper$UserTransactionWithContext.<init>(UserTransactionHelper.java:148)
    at org.quartz.ee.jta.UserTransactionHelper.lookupUserTransaction(UserTransactionHelper.java:108)
    at org.quartz.ee.jta.JTAJobRunShell.begin(JTAJobRunShell.java:101)
    at org.quartz.core.JobRunShell.run(JobRunShell.java:164)
    at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:560)
Caused by: javax.naming.NamingException: Cannot create resource instance
    at org.apache.naming.factory.TransactionFactory.getObjectInstance(TransactionFactory.java:116)
    at javax.naming.spi.NamingManager.getObjectInstance(NamingManager.java:321)
    at org.apache.naming.NamingContext.lookup(NamingContext.java:843)
    at org.apache.naming.NamingContext.lookup(NamingContext.java:154)
    at org.apache.naming.NamingContext.lookup(NamingContext.java:831)
    at org.apache.naming.NamingContext.lookup(NamingContext.java:168)
    at org.apache.naming.SelectorContext.lookup(SelectorContext.java:158)
    at javax.naming.InitialContext.lookup(InitialContext.java:411)
    at org.quartz.ee.jta.UserTransactionHelper$UserTransactionWithContext.<init>(UserTransactionHelper.java:145)
    ... 4 more

Obviously as I'm running in Tomcat, there is no JNDI registration for the UserTransaction. That said, I can't figure out how to get Quartz to use the existing transactions I've provided in my application context via AOP (or just ignore the transaction stuff as AOP handles it):

<!--  Quartz Scheduler Transaction Propagation -->
    <tx:advice id="quartzSchedulerAdvice" transaction-manager="txManager">
        <tx:attributes>      
            <tx:method name="get*" read-only="true" propagation="SUPPORTS" />
            <tx:method name="set*" read-only="true" propagation="SUPPORTS" />
            <tx:method name="is*" read-only="true" propagation="SUPPORTS" />
            <tx:method name="insert*" read-only="false" propagation="REQUIRED"/>
            <tx:method name="update*" read-only="false" propagation="REQUIRED"/>
            <tx:method name="delete*" read-only="false" propagation="REQUIRED"/>
            <tx:method name="schedule*" read-only="false" propagation="REQUIRED" rollback-for="org.quartz.SchedulerException"/>
            <tx:method name="pause*" read-only="false" propagation="REQUIRED"/>
            <tx:method name="resume*" read-only="false" propagation="REQUIRED"/>
            <tx:method name="run*" read-only="false" propagation="REQUIRED"/>
            <tx:method name="update*" read-only="false" propagation="REQUIRED"/>
            <tx:method name="delete*" read-only="false" propagation="REQUIRED"/>
            <tx:method name="toggle*" read-only="false" propagation="REQUIRED"/>
            <tx:method name="clone*" read-only="false" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>

    <!-- ensure that the above transactional advice runs for any execution of 
        an operation defined by the FooService interface -->
    <aop:config>
        <aop:pointcut id="jdbcDaoPC"
            expression="execution(* com.project.repository.*.*(..))" />
        <aop:advisor advice-ref="txAdvice" pointcut-ref="jdbcDaoPC" />
    </aop:config>
    <aop:config>
          <aop:pointcut id="quartzSchedulerPointcut" 
          expression="execution(* org.quartz.Scheduler.*(..))" />
         <aop:advisor advice-ref="quartzSchedulerAdvice" 
            pointcut-ref="quartzSchedulerPointcut" />
    </aop:config>

I'm using BoneCP as my datasource:

<!-- Row Mapper Beans -->
<bean id="dataSource" class="com.jolbox.bonecp.BoneCPDataSource"
    destroy-method="close">
    <property name="driverClass" value="${jdbc.driverClassName}" />
    <property name="jdbcUrl" value="${jdbc.url}" />
    <property name="username" value="${jdbc.username}" />
    <property name="password" value="${jdbc.password}" />
    <property name="idleConnectionTestPeriod" value="60" />
    <property name="idleMaxAge" value="240" />
    <property name="maxConnectionsPerPartition" value="5" />
    <property name="minConnectionsPerPartition" value="2" />
    <property name="partitionCount" value="3" />
    <property name="acquireIncrement" value="5" />
    <property name="statementsCacheSize" value="100" />
    <property name="releaseHelperThreads" value="3" />
</bean>

And my Spring scheduler factory configuration is here:

<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <property name="configLocation" value="classpath:quartz.properties"/>
        <property name="dataSource" ref="dataSource"/>
        <property name="nonTransactionalDataSource">
            <bean class="org.apache.commons.dbcp.BasicDataSource">
                <property name="driverClassName" value="com.mysql.jdbc.Driver" />
                <property name="url" value="${jdbc.url}" />
                <property name="username" value="${jdbc.username}" />
                <property name="password" value="${jdbc.password}" />
            </bean>
        </property>
        <property name="transactionManager" ref="txManager"/>


        <property name="schedulerName" value="ClusteredScheduler"/>


         <property name="overwriteExistingJobs" value="true"/>

        <property name="autoStartup" value="true"/>
        <property name="applicationContextSchedulerContextKey" value="applicationContext"/>
        <property name="jobFactory">
          <bean class="com.project.scheduling.persistence.AutowiringSpringBeanJobFactory"/>
        </property>
        <property name="schedulerContextAsMap">
            <map>
                <entry key="dataSource" value-ref="dataSource" />
                <entry key="transactionManager" value-ref="txManager" />
            </map>
        </property>
        <property name="jobDetails">
            <list>
                <ref bean="shipNoticeJob" />
                <ref bean="idleDeviceJob" />
                <ref bean="distanceJob" />
                <ref bean="deviceMaintenanceJob" />
            </list>
        </property>

        <property name="triggers">
            <list>
                <ref bean="shipNoticeCronTrigger" />
                <ref bean="idleDeviceTrigger" />
                <ref bean="distanceTrigger" />
                <ref bean="deviceMaintenanceTrigger" />

            </list>
        </property>
    </bean>

None of the tutorials etc I've been able to find online reference this issue in Tomcat so I'm left assuming theres a configuration step I'm missing...

Was it helpful?

Solution

Well, this one was staring me in the face:

org.quartz.scheduler.wrapJobExecutionInUserTransaction=true

Should have been:

org.quartz.scheduler.wrapJobExecutionInUserTransaction=false

Duh.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top