Announcement Announcement Module
Collapse
No announcement yet.
Feature: @Scheduled with @Value cron expression? Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Feature: @Scheduled with @Value cron expression?

    Hi.

    I am using Spring 3.0.0.RELEASE and tried to setup a scheduled job using the @Scheduled annotation with a configurable cron expression (stored in a properties file). My intuitive approach was to annotate my service method (in a @Service component) with @Scheduled and use Spring's Expression Language with the @Value annotation for retrieval of the properties value. Here's an example:

    Code:
    @Service public class SomeService .. {
    // ..
      @Scheduled(cron = @Value("#{ applicationProps['rates.refresh.cron']}"))
      public void refreshRates() throws ... { .. }
    // ...
    }
    ..with my applicationContext.xml containing these settings:

    Code:
    <context:component-scan base-package="com.example.some.package" />
    <task:annotation-driven />
    <util:properties id="applicationProps" location="/WEB-INF/classes/properties/application.properties" />
    Obviously this fails since @Scheduled's "cron" attribute expects a constant String value to be assigned.

    I finally implemented this behaviour using the XML-based task configuration:

    Code:
    <task:scheduled-tasks>
      <task:scheduled ref="someService" method="refreshRates" cron="#{applicationProps['rates.refresh.cron']}" />
    </task:scheduled-tasks>
    ..which forces me to ensure the proper service bean name using..

    Code:
    @Service(value="someService") public class SomeService..
    ..so that resolving my bean reference inside the "task:scheduled" section works.


    Wouldn't it be a neat feature to be able to setup task scheduling using annotations only with my first approach, or do I miss some point here?

    For your reference, here are the links to Spring's Expression Language and the @Scheduled Annotation documentation.

    Thanks for any feedback about this, digging into Spring 3 reveals many great improvements!

    Cheers,
    Axel

  • #2
    ScheduledAnnotationBeanPostProcessor uses ConfigurableApplicationContext's resolveEmbeddedValue to resolve cron attribute value. AbstractBeanFactory implements ConfigurableBeanFactory, and it's resolveEmbeddedValue method implementation uses StringValueResolver implementations to resolveStringValue. There is only one StringValueResolver implementation, PlaceholderResolvingStringValueResolver which makes use of PropertyPlaceholderHelper to resolveStringValue, ...

    Basically one should be able to reference properties using property placeholder expression ("${...}") in @Scheduled cron attribute value, just register ProperyPlaceholderConfigurer in your context XML, e.g. to point to properties file with configured cron expression property.
    Last edited by sslavic; Jan 13th, 2010, 06:00 PM. Reason: typo

    Comment


    • #3
      Hi sslavic.

      I gave your suggestion an try and added this section in my XML context:

      Code:
      <task:annotation-driven />
      <util:properties id="applicationProps"
        location="/WEB-INF/classes/properties/application.properties" />
      <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" p:properties-ref="applicationProps" />
      and my service implementation adjusted to:

      Code:
      @Scheduled(cron = "${rates.refresh.cron}")
      public void refreshRates() throws ... { .. }
      But nevertheless I get the following exception when deploying my application:

      Code:
      java.lang.IllegalArgumentException: cron expression must consist of 6 fields (found 1 in ${rates.refresh.cron})
      Obviously the property is not resolved and the placeholder string itself is being interpreted as cron expression. Honestly, I expected that since PropertyPlaceholderConfigurer only replaces tokens inside the XML context definition and not in Java code. That's what the new @Value annotation is for, which I mentioned in my initial posting.

      But nevertheless thanks for the suggestion, sslavic. I appreciate your feedback.

      Cheers,
      Axel

      Comment


      • #4
        When analyzing spring-framework sources I was using spring-framework trunk, where ScheduledAnnotationBeanPostProcessor has been changed since 3.0.0.RELEASE, to add support for property placeholders as cron attribute values (see related issue in spring-framework issue tracker), so this will be available in 3.0.1.RELEASE. If you can't wait for that to happen, you can build spring trunk yourself, in which case this SpringSource blog might be useful.

        Comment


        • #5
          Thanks sslavic for the detailed reply and the issue report you pointed me to.

          Since the 3.0.1 release is due already 25. January[0], I will be happy to wait until then. In the meantime, my workaround using the XML based configuration (described in my first posting) will be fine.

          Cheers,
          Axel


          --
          [0] Weird, but rich formatting is disabled for me ATM, so here's the link as footnote:
          - http://jira.springframework.org/brow...rversion/11331

          Comment


          • #6
            Spring 3.0.1 has just been released and I now readjusted my setup - here's my current, and working, state.

            In my applicationContext.xml I have a PropertiesPlaceholder, its backing Properties and the task scheduling set to use annotations:

            Code:
            <util:properties id="applicationProps" location="/WEB-INF/classes/properties/application.properties" />
            <context:property-placeholder properties-ref="applicationProps"  />
            ..
            <task:annotation-driven />
            And the service method which shall be called using the @Scheduled annotation now looks like this:

            Code:
            @Scheduled(cron = "${rates.refresh.cron}")
            public void refreshRates() throws .. { .. }
            ..with the service implementation class being annotated as @Service as mentioned in my initial post.

            With Spring 3.0.1 this work perfectly out of the box and I can drop my XML configuration for the task scheduling which I used as interim solution. Thanks for this great new release!

            Cheers,
            Axel

            Comment


            • #7
              Hi

              Hi Alex,

              This is working for me as well. However when I update the value through the JMX, the scheduler is not taking the latest?

              Comment


              • #8
                Hi luvfort.

                Originally posted by luvfort View Post
                This is working for me as well. However when I update the value through the JMX, the scheduler is not taking the latest?
                I guess the ScheduledAnnotationBeanPostProcessor only sets up the scheduling mechanism _once_ upon application context initialization. This is why a changed (cron) schedule may not be applied when modified during runtime (which is what I guess you do using JMX).

                Perhaps the chapter about task scheduling in the reference docs allows to find a solution (don't have the time to look myself right now).

                Cheers & HTH,
                Axel

                Comment


                • #9
                  can show me an example of application.properties that u included in you contextroperty-placeholder?

                  Comment


                  • #10
                    Hi pamlwong.

                    Originally posted by pamlwong View Post
                    can show me an example of application.properties that u included in you contextroperty-placeholder?
                    Of course My application.properties contains various settings, the one for the cron scheduled job was:

                    Code:
                    # run refresh job every day a 9am
                    rates.refresh.cron=0 0 9 * * *
                    The syntax how you can define schedules is described in the API for CronSquenceGenerator or in the reference documentation, here's an excerpt from the API:


                    Code:
                    Example patterns:
                    
                        * "0 0 * * * *" = the top of every hour of every day.
                        * "*/10 * * * * *" = every ten seconds.
                        * "0 0 8-10 * * *" = 8, 9 and 10 o'clock of every day.
                        * "0 0/30 8-10 * * *" = 8:00, 8:30, 9:00, 9:30 and 10 o'clock every day.
                        * "0 0 9-17 * * MON-FRI" = on the hour nine-to-five weekdays
                        * "0 0 0 25 12 ?" = every Christmas Day at midnight
                    I hope this helps.

                    Cheers,
                    Axel

                    Comment


                    • #11
                      Thanks for ur prompt reply.

                      exception:

                      org.springframework.beans.factory.xml.XmlBeanDefin itionStoreException: Line 60 in XML document from class path resource [applicationContext.xml] is invalid; nested exception is org.xml.sax.SAXParseException: The prefix "util" for element "utilroperties" is not bound.

                      My config file:
                      <task:annotation-driven executor="taskExecutor" scheduler="taskScheduler">
                      <utilroperties id="applicationProps" location="/WEB-INF/resources/crontab.properties" />
                      <contextroperty-placeholder properties-ref="applicationProps" />
                      </task:annotation-driven>


                      my Crontab.properties
                      rates.refresh.cron=1/3 * * * * *

                      im very new to this, xml and i have no idea whats goin on. please advise

                      Comment


                      • #12
                        i realise i missed out the
                        xmlns:util="http://www.springframework.org/schema/util"
                        &
                        xsi:schemaLocation="
                        http://www.springframework.org/schema/util
                        http://www.springframework.org/schema/util/spring-util-3.0.xsd"

                        and i have change my config to

                        <task:annotation-driven executor="taskExecutor" scheduler="taskScheduler"/>
                        <contextroperty-placeholder location="classpath:crontab.properties"/>

                        But there is another <contextroperty-placeholder... > at another xml file. ended up the next <contextroperty-placeholder... > seems like got problem when loading.

                        Comment


                        • #13
                          Originally posted by pamlwong View Post
                          i realise i missed out the
                          xmlns:util="http://www.springframework.org/schema/util"
                          &
                          xsi:schemaLocation="
                          http://www.springframework.org/schema/util
                          http://www.springframework.org/schema/util/spring-util-3.0.xsd"

                          and i have change my config to

                          <task:annotation-driven executor="taskExecutor" scheduler="taskScheduler"/>
                          <contextroperty-placeholder location="classpath:crontab.properties"/>

                          But there is another <contextroperty-placeholder... > at another xml file. ended up the next <contextroperty-placeholder... > seems like got problem when loading.
                          Lastest update:

                          i am using spring 3 + tomcat currently.
                          It seems like it will only load the 1st context: property-placeholder. so i have to put all the to be loaded properties file in same place just like this:
                          <context: property-placeholder location="classpath:jdbc.properties, classpath:crontab.properties"/>

                          Comment


                          • #14
                            thank you . it worked !

                            Comment


                            • #15
                              how to make this work for periodictriggers (i.e. not cron)

                              Originally posted by axel.knauf View Post
                              ..And the service method which shall be called using the @Scheduled annotation now looks like this:

                              Code:
                              @Scheduled(cron = "${rates.refresh.cron}")
                              public void refreshRates() throws .. { .. }
                              ..with the service implementation class being annotated as @Service as mentioned in my initial post.

                              With Spring 3.0.1 this work perfectly out of the box and I can drop my XML configuration for the task scheduling which I used as interim solution. Thanks for this great new release!

                              Cheers,
                              Axel
                              that's great. anyone know how to you make this work with a PeriodicTrigger?
                              i.e. @Scheduler(fixedRate=...).

                              the fixedRate attribute is type long, not string. with a cron expression, the types match.

                              so hard-coding the value works:
                              @Scheduler(fixedRate=5000)
                              but i cannot trick it into bringing in a placeholder:
                              @Scheduler(fixedRate="${someprop}")

                              / eitan

                              Comment

                              Working...
                              X