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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10704509/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-21 09:11:36  来源:igfitidea点击:

Quartz JobStore: JobPersistenceException: Couldn't retrieve trigger: ClassNotFoundException

macosspringglassfish-3quartz-schedulerclassnotfoundexception

提问by stephen murphy

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.

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

So... I'm obviously using an application server, and in this case tried against Glassfish 3.1.1/3.1.2

所以......我显然正在使用应用程序服务器,在这种情况下尝试针对 Glassfish 3.1.1/3.1.2

tried against Quartz 1.8.6/2.1.5 using Spring 3.1.0.RELEASE

尝试使用 Spring 3.1.0.RELEASE 对抗 Quartz 1.8.6/2.1.5

Spring/Quartz config:

弹簧/石英配置:

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

and the corresponding trigger reference:

以及相应的触发器参考:

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

So the problem I'm having is this: any combination of the below doesn't seem to effect the classloader being used.

所以我遇到的问题是:下面的任何组合似乎都不会影响正在使用的类加载器。

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

Or more specifically dont help when trying to retrieve a previously persisted trigger, resulting in the following stracktrace:

或者更具体地说,在尝试检索以前持久化的触发器时无济于事,导致以下 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.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

I can see the org.quartz.simpl.CascadingClassLoadHelperbeing used on-load, and correctly selecting the right classloader.

我可以看到org.quartz.simpl.CascadingClassLoadHelper在加载时使用,并正确选择了正确的类加载器。

Problem is that when the QuartzSchedulerThreadtries to retrieve a trigger it uses JobStoreSupport.retrieveTrigger(), which in turn falls-back to ObjectInputsStream.resolveClass(), and the following line of code:

问题是,当QuartzSchedulerThread尝试检索触发器时,它使用JobStoreSupport.retrieveTrigger(),后者又回退到ObjectInputsStream.resolveClass()和以下代码行:

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

Where latestUserDefinedLoader()always returns the wrong classloader...resulting in the ClassNotFoundException and leaving me pretty flummoxed!

latestUserDefinedLoader()总是返回错误的类加载器......导致ClassNotFoundException的和留下我很狼狈不堪!

I should point out that latestUserDefinedLoader()is a native method of ObjectInputsStream, and i'm using jdk 1.6 on OSX.

我应该指出latestUserDefinedLoader()是 ObjectInputsStream 的本机方法,我在 OSX 上使用 jdk 1.6。

Can anyone from either the Quartz/ Spring or more likely Glassfish community shed some light on this, I'm pulling my hair out at the moment.

Quartz/Spring 或更可能的 Glassfish 社区的任何人都可以对此有所了解吗,我现在正在拔头发。

Thanks Steve.

谢谢史蒂夫。

回答by stephen murphy

If anyones interested the solution I came up with, although not ideal was to extend the default StdJDBCDelegate, in my case for MySQL, where I was able to anonymously override ObjectInputStream.resolveClass()much like the below.

如果有人对我提出的解决方案感兴趣,虽然不是理想的扩展默认值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;
}   

Using the current threads classloader, falling-back to the default handling if unsuccessful.

使用当前线程类加载器,如果不成功则回退到默认处理。

If anyone has a better solution or even an explanation as to why the issue occurred in the first place, I'd be more than interested in hearing.

如果有人有更好的解决方案,甚至可以解释为什么会首先出现问题,我会非常想听听。

S.

S。

回答by jddsantaella

Use org.springframework.scheduling.quartz.JobDetailBeaninstead org.springframework.scheduling.quartz.JobDetailFactoryBean

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