I'm hoping one of you lovely people will be able to help me with this, as I've spent a number of fruitless hours already trying to make everything play nice!
I've traced the issue down to Classloading, and been able to see that when Quartz tries to de-serialise jobDetail's from a jobStore (jobStoreCMT), the Classloader used does not contain any of my applications classes, and only the libraries defined in the EARs lib folder.
So... I'm obviously using an application server, and in this case tried against Glassfish 3.1.1/3.1.2
tried against Quartz 1.8.6/2.1.5 using Spring 3.1.0.RELEASE
Spring/Quartz config:
and the corresponding trigger reference:Code:<bean id="schedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="overwriteExistingJobs" value="true" /> <property name="triggers"> <list> <ref bean="notificationEmailsSimpleTrigger" /> </list> </property> <property name="quartzProperties"> <props> <prop key="org.quartz.scheduler.instanceName">QuartzScheduler</prop> <prop key="org.quartz.scheduler.instanceId">AUTO</prop> <prop key="org.quartz.threadPool.class">org.quartz.simpl.SimpleThreadPool</prop> <prop key="org.quartz.threadPool.threadCount">25</prop> <prop key="org.quartz.threadPool.threadPriority">5</prop> <prop key="org.quartz.jobStore.class">org.quartz.impl.jdbcjobstore.JobStoreCMT</prop> <prop key="org.quartz.jobStore.driverDelegateClass">org.quartz.impl.jdbcjobstore.StdJDBCDelegate</prop> <prop key="org.quartz.jobStore.misfireThreshold">60000</prop> <prop key="org.quartz.jobStore.tablePrefix">QRTZ_</prop> <!-- <prop key="org.quartz.jobStore.isClustered">true</prop> --> <!-- <prop key="org.quartz.jobStore.clusterCheckinInterval">20000</prop> --> <prop key="org.quartz.scheduler.classLoadHelper.class">org.quartz.simpl.CascadingClassLoadHelper</prop> <prop key="org.quartz.scheduler.threadsInheritContextClassLoaderOfInitializer">true</prop> <prop key="org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread">true</prop> <prop key="org.quartz.scheduler.skipUpdateCheck">true</prop> </props> </property> </bean>
So the problem I'm having is this: any combination of the below doesn't seem to effect the classloader being used.Code:<bean id="notificationEmailsSimpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean"> <property name="jobDetail" ref="notificationJobDetail" /> <property name="repeatInterval" value="60000" /> </bean> <bean id="notificationJobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean"> <property name="jobClass" value="com.mcboom.social.notifications.NotificationQuartzJobBean" /> </bean>
Or more specifically dont help when trying to retrieve a previously persisted trigger, resulting in the following stracktrace:Code:<prop key="org.quartz.scheduler.classLoadHelper.class">org.quartz.simpl.CascadingClassLoadHelper</prop> <prop key="org.quartz.scheduler.threadsInheritContextClassLoaderOfInitializer">true</prop> <prop key="org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread">true</prop>
I can see the org.quartz.simpl.CascadingClassLoadHelper being used on-load, and correctly selecting the right classloader.Code:INFO: ERROR - ErrorLogger.schedulerError(schedulerFactoryBean_QuartzSchedulerThread)(2358) | An error occured while scanning for the next trigger to fire. org.quartz.JobPersistenceException: Couldn't acquire next trigger: Couldn't retrieve trigger: com.mcboom.social.notifications.NotificationQuartzJobBean [See nested exception: org.quartz.JobPersistenceException: Couldn't retrieve trigger: com.mcboom.social.notifications.NotificationQuartzJobBean [See nested exception: java.lang.ClassNotFoundException: com.mcboom.social.notifications.NotificationQuartzJobBean]] at org.quartz.impl.jdbcjobstore.JobStoreSupport.acquireNextTrigger(JobStoreSupport.java:2814) at org.quartz.impl.jdbcjobstore.JobStoreSupport$36.execute(JobStoreSupport.java:2757) at org.quartz.impl.jdbcjobstore.JobStoreSupport.executeInNonManagedTXLock(JobStoreSupport.java:3788) at org.quartz.impl.jdbcjobstore.JobStoreSupport.acquireNextTrigger(JobStoreSupport.java:2753) at org.quartz.core.QuartzSchedulerThread.run(QuartzSchedulerThread.java:263) Caused by: org.quartz.JobPersistenceException: Couldn't retrieve trigger: com.mcboom.social.notifications.NotificationQuartzJobBean [See nested exception: java.lang.ClassNotFoundException: com.mcboom.social.notifications.NotificationQuartzJobBean] at org.quartz.impl.jdbcjobstore.JobStoreSupport.retrieveTrigger(JobStoreSupport.java:1596) at org.quartz.impl.jdbcjobstore.JobStoreSupport.retrieveTrigger(JobStoreSupport.java:1572) at org.quartz.impl.jdbcjobstore.JobStoreSupport.acquireNextTrigger(JobStoreSupport.java:2792) ... 4 more
Problem is that when the QuartzSchedulerThread tries to retrieve a trigger it uses JobStoreSupport.retrieveTrigger(), which in turn falls-back to ObjectInputsStream.resolveClass(), and the following line of code:
Where latestUserDefinedLoader() always returns the wrong classloader...resulting in the ClassNotFoundException and leaving me pretty flummoxed!Code:Class.forName(name, false, latestUserDefinedLoader())
Can anyone from either the Quartz/ Spring community shed some light on this, I'm pulling my hair out at the moment.
Thanks Steve.
If anyones interested I posted a solution here


Reply With Quote
