Open Source Repository

Home /spring/spring-context-3.0.5 | Repository Home



org/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.java
/*
 * Copyright 2002-2010 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.scheduling.annotation;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.MethodInvokingRunnable;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.ReflectionUtils.MethodCallback;
import org.springframework.util.StringValueResolver;

/**
 * Bean post-processor that registers methods annotated with {@link Scheduled @Scheduled}
 * to be invoked by a {@link org.springframework.scheduling.TaskScheduler} according
 * to the "fixedRate", "fixedDelay", or "cron" expression provided via the annotation.
 *
 @author Mark Fisher
 @author Juergen Hoeller
 @since 3.0
 @see Scheduled
 @see org.springframework.scheduling.TaskScheduler
 */
public class ScheduledAnnotationBeanPostProcessor
    implements BeanPostProcessor, Ordered, EmbeddedValueResolverAware, ApplicationContextAware,
    ApplicationListener<ContextRefreshedEvent>, DisposableBean {

  private Object scheduler;

  private StringValueResolver embeddedValueResolver;

  private ApplicationContext applicationContext;

  private final ScheduledTaskRegistrar registrar = new ScheduledTaskRegistrar();

  private final Map<Runnable, String> cronTasks = new HashMap<Runnable, String>();

  private final Map<Runnable, Long> fixedDelayTasks = new HashMap<Runnable, Long>();

  private final Map<Runnable, Long> fixedRateTasks = new HashMap<Runnable, Long>();


  /**
   * Set the {@link org.springframework.scheduling.TaskScheduler} that will invoke
   * the scheduled methods, or a {@link java.util.concurrent.ScheduledExecutorService}
   * to be wrapped as a TaskScheduler.
   */
  public void setScheduler(Object scheduler) {
    this.scheduler = scheduler;
  }

  public void setEmbeddedValueResolver(StringValueResolver resolver) {
    this.embeddedValueResolver = resolver;
  }

  public void setApplicationContext(ApplicationContext applicationContext) {
    this.applicationContext = applicationContext;
  }

  public int getOrder() {
    return LOWEST_PRECEDENCE;
  }


  public Object postProcessBeforeInitialization(Object bean, String beanName) {
    return bean;
  }

  public Object postProcessAfterInitialization(final Object bean, String beanName) {
    Class<?> targetClass = AopUtils.getTargetClass(bean);
    ReflectionUtils.doWithMethods(targetClass, new MethodCallback() {
      public void doWith(Method methodthrows IllegalArgumentException, IllegalAccessException {
        Scheduled annotation = AnnotationUtils.getAnnotation(method, Scheduled.class);
        if (annotation != null) {
          Assert.isTrue(void.class.equals(method.getReturnType()),
              "Only void-returning methods may be annotated with @Scheduled.");
          Assert.isTrue(method.getParameterTypes().length == 0,
              "Only no-arg methods may be annotated with @Scheduled.");
          MethodInvokingRunnable runnable = new MethodInvokingRunnable();
          runnable.setTargetObject(bean);
          runnable.setTargetMethod(method.getName());
          runnable.setArguments(new Object[0]);
          try {
            runnable.prepare();
          }
          catch (Exception ex) {
            throw new IllegalStateException("failed to prepare task", ex);
          }
          boolean processedSchedule = false;
          String errorMessage = "Exactly one of 'cron', 'fixedDelay', or 'fixedRate' is required.";
          String cron = annotation.cron();
          if (!"".equals(cron)) {
            processedSchedule = true;
            if (embeddedValueResolver != null) {
              cron = embeddedValueResolver.resolveStringValue(cron);
            }
            cronTasks.put(runnable, cron);
          }
          long fixedDelay = annotation.fixedDelay();
          if (fixedDelay >= 0) {
            Assert.isTrue(!processedSchedule, errorMessage);
            processedSchedule = true;
            fixedDelayTasks.put(runnable, fixedDelay);
          }
          long fixedRate = annotation.fixedRate();
          if (fixedRate >= 0) {
            Assert.isTrue(!processedSchedule, errorMessage);
            processedSchedule = true;
            fixedRateTasks.put(runnable, fixedRate);
          }
          Assert.isTrue(processedSchedule, errorMessage);
        }
      }
    });
    return bean;
  }

  public void onApplicationEvent(ContextRefreshedEvent event) {
    if (event.getApplicationContext() == this.applicationContext) {
      if (this.scheduler != null) {
        this.registrar.setScheduler(this.scheduler);
      }
      this.registrar.setCronTasks(this.cronTasks);
      this.registrar.setFixedDelayTasks(this.fixedDelayTasks);
      this.registrar.setFixedRateTasks(this.fixedRateTasks);
      this.registrar.afterPropertiesSet();
    }
  }

  public void destroy() throws Exception {
    if (this.registrar != null) {
      this.registrar.destroy();
    }
  }

}