Quartz JobStore:JobPersistenceException:无法检索触发器:ClassNotFoundException

问题描述 投票:0回答:3

我希望你们中的一个可爱的人能够帮助我解决这个问题,因为我已经花了很多时间毫无结果地试图让一切顺利进行!

我已经将问题追溯到类加载,并且能够看到当 Quartz 尝试从 jobStore (jobStoreCMT) 反序列化 jobDetail 时,使用的类加载器不包含我的任何应用程序类,并且仅包含EARs lib 文件夹。

所以...我显然正在使用应用程序服务器,在本例中尝试针对 Glassfish 3.1.1/3.1.2

尝试过 Quartz 1.8.6/2.1.5 使用Spring 3.1.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 社区的任何人都可以对此发表一些看法吗?我现在正在抓狂。

谢谢史蒂夫。

macos spring glassfish-3 quartz-scheduler classnotfoundexception
3个回答
5
投票

如果有人对我提出的解决方案感兴趣,尽管扩展默认值

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.


2
投票

使用

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


0
投票

在我的例子中,Quartz 数据库中存在一个错误的作业,而 Java 配置中不存在作为 bean 的作业,因此它抱怨找不到该类。当我从数据库中删除 3 个表的记录时:qrtz_cron_triggers、qrtz_triggers、qrtz_ob_details,它开始工作

© www.soinside.com 2019 - 2024. All rights reserved.