2

我希望你們中的一個可愛的人將能夠幫助我,因爲我已經花了許多沒有結果的時間已經試圖讓一切都很好玩!石英JobStore:JobPersistenceException:無法檢索觸發器:ClassNotFoundException

我追蹤的問題到類加載,並且能夠看到,當石英試圖從一個作業存儲(jobStoreCMT),使用不含有任何我的應用程序類的類加載器去連載的JobDetail的,只有在EAR的lib文件夾中定義的庫。

所以...我明明使用應用程序服務器,在這種情況下試圖對Glassfish的3.1.1/3.1.2

使用Spring 3.1對石英1.8.6/2.1.5 嘗試。 0.RELEASE

彈簧/石英配置:

<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> 

和相應的觸發參考:

<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> 

所以我遇到的問題是這樣的:下面的任何組合似乎並沒有影響正在使用的類加載器。

<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> 

或者試圖檢索以前堅持觸發器時更具體地說不幫助,從而產生以下stracktrace:

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 

我可以看到org.quartz.simpl.CascadingClassLoadHelper使用上負載,並正確選擇正確的類加載器。

問題是,當QuartzSchedulerThread嘗試檢索一個觸發器,它使用JobStoreSupport.retrieveTrigger(),這反過來又下降回至ObjectInputsStream.resolveClass(),和下面的代碼行:

Class.forName(name, false, latestUserDefinedLoader()) 

latestUserDefinedLoader()總是返回錯誤的類加載器......導致ClassNotFoundException的和留下我非常狼狽!

我應該指出latestUserDefinedLoader()是ObjectInputsStream的本地方法,並且我在OSX上使用jdk 1.6。

任何來自Quartz/Spring或更有可能的Glassfish社區的人都會對此有所瞭解,此刻我正在拉我的頭髮。

謝謝Steve。

回答

4

如果任何人有興趣我想出了一個解決辦法,雖然不理想是擴展默認StdJDBCDelegate,在我來說,我的MySQL,在那裏我可以匿名覆蓋ObjectInputStream.resolveClass()很像下方。

/** 
* <p> 
* This method should be overridden by any delegate subclasses that need 
* special handling for BLOBs. The default implementation uses standard JDBC 
* <code>java.sql.Blob</code> operations. 
* </p> 
* 
* <p> 
* This implementation overcomes the incorrect classloader being used in 
* ObjectInputStream, overriding it with the current threads classloader. 
* </p> 
* 
* @param rs 
*   the result set, already queued to the correct row 
* @param colName 
*   the column name for the BLOB 
* @return the deserialized Object from the ResultSet BLOB 
* @throws ClassNotFoundException 
*    if a class found during deserialization cannot be found 
* @throws IOException 
*    if deserialization causes an error 
*/ 
@Override 
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) { 
     InputStream binaryInput = blobLocator.getBinaryStream(); 

     if (null != binaryInput) { 
      if (binaryInput instanceof ByteArrayInputStream && ((ByteArrayInputStream) binaryInput).available() == 0) { 
       // do nothing 
      } else { 
       ObjectInputStream in = new ObjectInputStream(binaryInput) { 

        @Override 
        protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException { 
         String name = desc.getName(); 
         try { 
          return Class.forName(name, false, Thread.currentThread().getContextClassLoader()); 
         } catch (ClassNotFoundException ex) { 
          return super.resolveClass(desc); 
         } 
        } 
       }; 

       try { 
        obj = in.readObject(); 
       } finally { 
        in.close(); 
       } 
      } 
     } 

    } 
    return obj; 
} 

使用當前線程類加載器,如果不成功,則回退到缺省處理。

如果任何人有更好的解決方案,甚至解釋爲什麼問題首先發生,我會更感興趣的是聽力。

S.

+0

更優雅的解決方案是重新打包我的EAR,將所有非EJB類打包到core.jar lib中,並將其放在EAR的lib文件夾中,以便與所有其他應用程序共享相同的類加載器。解決了這個問題,但卻帶來了大量重構的不必要開銷。 –

2

使用org.springframework.scheduling.quartz.JobDetailBean代替org.springframework.scheduling.quartz.JobDetailFactoryBean

+0

'org.springframework.scheduling.quartz.JobDetailFactoryBean' bean是一個下拉更換爲'org.springframework.scheduling.quartz.JobDetailBean'支持Quartx 2.x中,所以沒有幫助我害怕。感謝您的建議。 –