Announcement Announcement Module
Collapse
No announcement yet.
Spring injection no longer works after exposing scheduler as MBean (JMX) Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Spring injection no longer works after exposing scheduler as MBean (JMX)

    In our web application we have one Quartz scheduler, containing one trigger which is linked to one job. Pretty straightfoward. Job data is injected by Spring using the org.springframework.scheduling.quartz.JobDetailBea n.

    Below is the configuration of the JobDetailBean. As you can see, a number of business beans (factories, etc...) are being injected into the job by means of the jobDataAsMap property.

    Code:
    <bean name="ftpProxyJobDetail" class="org.springframework.scheduling.quartz.JobDetailBean">
    		<property name="jobClass" value="be.admb.ftpproxy.scheduler.jobs.FtpProxyJob" />
    		<property name="jobDataAsMap">
    			<map>
    				<entry key="name" value="FtpProxyJob" />
    				<entry key="proxyConfiguration" value-ref="proxyConfiguration" />
    				<entry key="blackList" value-ref="blackList" />
    				<entry key="extensionFactory" value-ref="extensionFactory" />
    				<entry key="connectionHelper" value-ref="connectionHelper" />
    				<entry key="emailFactory" value-ref="emailFactory" />
    			</map>
    		</property>
    	</bean>
    Where emailFactory is just an ordinary factory, defined as:

    Code:
    <bean id="emailFactory" class="be.admb.ftpproxy.utils.SimpleEmailFactory" />
    This works fine as long as I do not expose the Scheduler as a JMX MBean. Note the org.quartz.scheduler.jmx.export entry that is set to false.

    Code:
    <bean id="schedulerFtpProxy"
    		class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    		<property name="triggers">
    			<list>
    				<ref bean="simpleTriggerFtpProxy" />
    			</list>
    		</property>
    		<property name="quartzProperties">
    			<props>
    				<!-- ThreadPool -->
    				<prop key="org.quartz.threadPool.class">org.quartz.simpl.SimpleThreadPool</prop>
    				<prop key="org.quartz.threadPool.threadCount">1</prop>
    				<prop key="org.quartz.threadPool.threadPriority">5</prop>
    				<prop key="org.quartz.scheduler.jmx.export">false</prop>
    			</props>
    		</property>
    	</bean>
    When I set org.quartz.scheduler.jmx.export to true, I get the following error when the application starts up:

    java.lang.ClassCastException: be.admb.ftpproxy.utils.SimpleEmailFactory cannot be cast to java.lang.String

    It appears as if no property conversion is done for the SimpeEmailFactory class. I get similar ClassCastExceptions for the other properties that are to be injected by the jobDataAsMap.

    The complete stack trace appears below:

    Code:
    java.lang.ClassCastException: be.admb.ftpproxy.utils.SimpleEmailFactory cannot be cast to java.lang.String
    	at org.quartz.core.jmx.JobDataMapSupport.toTabularData(JobDataMapSupport.java:72)
    	at org.quartz.core.jmx.JobDetailSupport.toCompositeData(JobDetailSupport.java:83)
    	at org.quartz.core.QuartzSchedulerMBeanImpl.jobAdded(QuartzSchedulerMBeanImpl.java:344)
    	at org.quartz.core.QuartzScheduler.notifySchedulerListenersJobAdded(QuartzScheduler.java:2173)
    	at org.quartz.core.QuartzScheduler.addJob(QuartzScheduler.java:866)
    	at org.quartz.impl.StdScheduler.addJob(StdScheduler.java:266)
    	at org.springframework.scheduling.quartz.SchedulerAccessor.addJobToScheduler(SchedulerAccessor.java:317)
    	at org.springframework.scheduling.quartz.SchedulerAccessor.addTriggerToScheduler(SchedulerAccessor.java:340)
    	at org.springframework.scheduling.quartz.SchedulerAccessor.registerJobsAndTriggers(SchedulerAccessor.java:276)
    	at org.springframework.scheduling.quartz.SchedulerFactoryBean.afterPropertiesSet(SchedulerFactoryBean.java:483)
    	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1369)
    	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1335)
    	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:473)
    	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory$1.run(AbstractAutowireCapableBeanFactory.java:409)
    	at java.security.AccessController.doPrivileged(Native Method)
    	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:380)
    	at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:264)
    	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
    	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:261)
    	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:185)
    	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:164)
    	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:423)
    	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:728)
    	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:380)
    	at org.springframework.web.context.ContextLoader.createWebApplicationContext(ContextLoader.java:255)
    	at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:199)
    	at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:45)
    	at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:3843)
    	at org.apache.catalina.core.StandardContext.start(StandardContext.java:4342)
    	at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1045)
    	at org.apache.catalina.core.StandardHost.start(StandardHost.java:719)
    	at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1045)
    	at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:443)
    	at org.apache.catalina.core.StandardService.start(StandardService.java:516)
    	at org.apache.catalina.core.StandardServer.start(StandardServer.java:710)
    	at org.apache.catalina.startup.Catalina.start(Catalina.java:578)
    	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    	at java.lang.reflect.Method.invoke(Method.java:597)
    	at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:288)
    	at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:413)
    Any idea on how to solve this?

    Regards,
    Rik

  • #2
    The compromising line of code is located in JobDataMapSupport, on line 72 (Quartz 1.8.0), in the method toTabularData().

    Code:
    list.add(toCompositeData(key, (String) jobDataMap.get(key)));
    Obviously the jobDataMap can contain other objects than simple Strings. Hence the ClassCastException.

    Methinks this qualifies as a bug...

    A solution could consist of changing the line in question to:
    Code:
    list.add(toCompositeData(key, jobDataMap.get(key).toString()));
    Right?

    Comment

    Working...
    X