Results 1 to 7 of 7

Thread: ServletContext injection on Quartz scheduler job not calling setter method

  1. #1
    Join Date
    Sep 2010
    Posts
    25

    Default ServletContext injection on Quartz scheduler job not calling setter method

    I've searched these forums along with google and cannot find the proper way to achieve setter injection on a Quartz scheduler bean. We are using Spring 3.0 and I've confirmed that the setServletContext(ServletContext ctx) is never called prior to the executeInternal() method and therefore the ServletContext is always null.

    My Goal:
    I need a singleton shared cache which this scheduler will load/refresh twice a day (right now I have the scheduler configured for every 2 minutes for testing). Then in a Spring MVC controller web service resource I want to get the shared object out of the ServletContext and use it to service the request. I thought the ServletContext would be a good place for storing this 'cache'.

    Here is what I have:

    Code:
    public class MoreResourcesProcessor extends QuartzJobBean implements StatefulJob, ServletContextAware {
    
        @Override
        protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
            logger.info("Executing MoreResourceProcessor..."); // I DO see this log statement!
            String name = "Billy";
            servletContext.setAttribute("name", name);
        }
    
        @Override
        public void setServletContext(ServletContext servletContext) {
            logger.info("Setting servletContext: ["+servletContext+"]"); // I never see this log statement!
            this.servletContext = servletContext;
        }
    
    }
    The 'Setting servletContext' log statement is never executed.

    Here is my spring configuration:

    Code:
    <bean id="MoreResourcesProcessor" class="org.springframework.scheduling.quartz.JobDetailBean">
            <property name="jobClass" value="com.soundstrue.mobile.processor.MoreResourcesProcessor"/>
            <property name="jobDataAsMap">
                <map>
                    <entry key="enabled" value="true"/>
                </map>
            </property>
        </bean>
    
        <!-- http://www.quartz-scheduler.org/documentation/quartz-1.x/tutorials/crontrigger -->
        <bean id="moreResourcesTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
            <property name="jobDetail" ref="MoreResourcesProcessor"/>
            <property name="cronExpression" value="0 0/2 * * * ?"/>
        </bean>
    
        <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
            <property name="applicationContextSchedulerContextKey" value="applicationContext"/>
            <property name="triggers">
                <list>
                    <ref bean="moreResourcesTrigger"/>
                </list>
            </property>
            <property name="quartzProperties">
                <util:properties>
                    <!-- http://forums.terracotta.org/forums/posts/list/3395.page -->
                    <prop key="org.quartz.scheduler.skipUpdateCheck">true</prop>
                </util:properties>
            </property>
        </bean>
    Everything that I have found while searching indicates to implement the ServletContextAware interface to achieve my goal but as you can see, this method isn't working.

    What is the recommended way?

    Thank you for any help!

    - Billy -

  2. #2
    Join Date
    Jun 2006
    Location
    The Netherlands
    Posts
    13,695

    Default

    Let me guess you are using the ContextLoaderListener to load this configuration file? Next to that the bean you are using isn't a spring bean, so spring doesn't know about the bean and thus will not inject dependencies into it.
    Marten Deinum
    Java Consultant / Pragmatist / Open Source Enthousiast / Author


    Pro Spring MVC: With Web Flow
    Conspect

    Have you read the reference guide.
    Use the [ code ] tags, young padawan

  3. #3
    Join Date
    Sep 2010
    Posts
    25

    Default

    Yes, you are absolutely correct. Apologies for not adding the web.xml snippet to the original post! This is what I have in my web.xml which loads the spring config.
    Code:
    <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>
                classpath*:/spring-context.xml
            </param-value>
        </context-param>
        <listener>
            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        </listener>

  4. #4
    Join Date
    Sep 2010
    Posts
    25

    Default

    Sorry, I didn't read the last sentence in my original reply. What do you mean by the bean I am using isn't a spring bean. The quartz job is a spring bean and configured in my spring config file, right? Or are you referring to the ServletContext isn't a spring bean? How would I configure that in my spring config file? Or am I completely missing what you are indicating?

  5. #5
    Join Date
    Jun 2006
    Location
    The Netherlands
    Posts
    13,695

    Default

    The class implementing ServletContextAware is NOT a spring bean. It is NOT instantiated by spring so it isn't a spring bean. You pass the classname into the org.springframework.scheduling.quartz.JobDetailBea n but as stated it is NOT a spring bean. An instance is created by the JobDetailBean and not by spring and thus spring doesn't know about it and thus will not inject anything into your class.
    Marten Deinum
    Java Consultant / Pragmatist / Open Source Enthousiast / Author


    Pro Spring MVC: With Web Flow
    Conspect

    Have you read the reference guide.
    Use the [ code ] tags, young padawan

  6. #6
    Join Date
    Sep 2010
    Posts
    25

    Default

    I see what you are saying now, I never create the MoreResourcesProcessor as it's own bean. I went ahead and tried what I think needed to be done and it didn't work because the jobClass property on the JobDetailBean expects a string representing the class to instantiate, not a reference bean.

    Here is what I tried which did not work:
    Code:
    <bean id="MoreResourcesProcessor" class="com.soundstrue.mobile.processor.MoreResourcesProcessor">
            <property name="s3Svc" ref="s3Svc"/>
            <property name="xStreamMarshaller" ref="xStreamMarshaller"/>
    </bean>
    
    <bean id="MoreResourcesProcessorJob" class="org.springframework.scheduling.quartz.JobDetailBean">
            <property name="jobClass" ref="MoreResourcesProcessor"/>
    </bean>
    When I attempt to deploy the app, I get the following exception:

    org.springframework.beans.factory.BeanCreationExce ption: Error creating bean with name 'MoreResourcesProcessorJob' defined in URL [file:/Users/bbacon/Development/workspace/st/mobile/ws/target/mobile-ws-1.0.1-SNAPSHOT/WEB-INF/classes/spring-context.xml]: Initialization of bean failed; nested exception is org.springframework.beans.TypeMismatchException: Failed to convert property value of type 'com.soundstrue.mobile.processor.MoreResourcesProc essor' to required type 'java.lang.Class' for property 'jobClass'; nested exception is java.lang.IllegalArgumentException: Cannot convert value of type [com.soundstrue.mobile.processor.MoreResourcesProce ssor] to required type [java.lang.Class] for property 'jobClass': PropertyEditor [org.springframework.beans.propertyeditors.ClassEdi tor] returned inappropriate value of type [com.soundstrue.mobile.processor.MoreResourcesProce ssor]

    Am I going about this the wrong way? Any chance you can provide a sample of what could work?

    Thanks for your help/time Marten.

    - Billy -

  7. #7
    Join Date
    Sep 2010
    Posts
    25

    Default

    I finally figured out a way to achieve injecting a ServletContext object onto a quartz scheduler. I had to go about it in a slight different fashion but the end result is the same.

    As Marten indicated above, my first problem was that I never established the MoreResourcesProcessor as a spring bean so this was the first requirement:

    Code:
    <bean id="MoreResourcesProcessor" class="com.soundstrue.mobile.processor.MoreResourcesProcessor">
        <property name="s3Svc" ref="s3Svc"/>
        <property name="xStreamMarshaller" ref="xStreamMarshaller"/>
    </bean>
    The MoreResourcesProcessor is now a simple java class which just implements ServletContextAware (no longer extending QuartzJobBean or implementing StatefulJob). The work is now done in the process() method which is completely an arbitrary name. Here is a short snippet of the class for reference:

    Code:
    public class MoreResourcesProcessor implements ServletContextAware {
    
         .... // members omitted 
    
         protected void process() {
              // performs all business logic and sets an attribute on the ServletContext which is injected
              servletContext.setAttribute("foo", "foo");
         }
    
        public void setxStreamMarshaller(XStreamMarshaller xStreamMarshaller) {
            this.xStreamMarshaller = xStreamMarshaller;
        }
    
        public void setS3Svc(STS3Service s3Svc) {
            this.s3Svc = s3Svc;
        }
    
        @Override
        public void setServletContext(ServletContext servletContext) {
            this.servletContext = servletContext;
        }
    }
    Then instead of using a JobDetailBean object to declare/configure the jobDetail, I simply use a MethodInvokingJobDetailFactoryBean.

    Code:
        <bean id="moreResourcesJobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
            <property name="targetObject" ref="MoreResourcesProcessor"/>
            <property name="targetMethod" value="process"/>
            <property name="concurrent" value="false"/>
        </bean>
    
        <!--http://www.quartz-scheduler.org/documentation/quartz-1.x/tutorials/crontrigger-->
        <bean id="moreResourcesCronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
            <property name="jobDetail" ref="moreResourcesJobDetail"/>
            <property name="cronExpression" value="0 0/2 * * * ?"/><!-- will trigger every 2 minutes -->
        </bean>
    
        <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
            <property name="triggers">
                <list>
                    <ref bean="moreResourcesCronTrigger"/>
                </list>
            </property>
            <property name="quartzProperties">
                <util:properties>
                    <!-- http://forums.terracotta.org/forums/posts/list/3395.page -->
                    <prop key="org.quartz.scheduler.skipUpdateCheck">true</prop>
                </util:properties>
            </property>
        </bean>
    Hope this helps the next person that runs into a similar issue.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •