Announcement Announcement Module
Collapse
No announcement yet.
ServletContext injection on Quartz scheduler job not calling setter method Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

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

    Comment


    • #3
      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>

      Comment


      • #4
        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?

        Comment


        • #5
          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.

          Comment


          • #6
            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 -

            Comment


            • #7
              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.

              Comment

              Working...
              X