Open Source Repository

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



org/springframework/scheduling/concurrent/ScheduledExecutorFactoryBean.java
/*
 * Copyright 2002-2009 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.concurrent;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;

import org.springframework.beans.factory.FactoryBean;
import org.springframework.scheduling.support.DelegatingErrorHandlingRunnable;
import org.springframework.scheduling.support.TaskUtils;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;

/**
 {@link org.springframework.beans.factory.FactoryBean} that sets up
 * a JDK 1.5 {@link java.util.concurrent.ScheduledExecutorService}
 * (by default: a {@link java.util.concurrent.ScheduledThreadPoolExecutor})
 * and exposes it for bean references.
 *
 <p>Allows for registration of {@link ScheduledExecutorTask ScheduledExecutorTasks},
 * automatically starting the {@link ScheduledExecutorService} on initialization and
 * cancelling it on destruction of the context. In scenarios that just require static
 * registration of tasks at startup, there is no need to access the
 {@link ScheduledExecutorService} instance itself in application code.
 *
 <p>Note that {@link java.util.concurrent.ScheduledExecutorService}
 * uses a {@link Runnable} instance that is shared between repeated executions,
 * in contrast to Quartz which instantiates a new Job for each execution.
 *
 <p><b>WARNING:</b> {@link Runnable Runnables} submitted via a native
 {@link java.util.concurrent.ScheduledExecutorService} are removed from
 * the execution schedule once they throw an exception. If you would prefer
 * to continue execution after such an exception, switch this FactoryBean's
 {@link #setContinueScheduledExecutionAfterException "continueScheduledExecutionAfterException"}
 * property to "true".
 *
 @author Juergen Hoeller
 @since 2.0
 @see #setPoolSize
 @see #setThreadFactory
 @see ScheduledExecutorTask
 @see java.util.concurrent.ScheduledExecutorService
 @see java.util.concurrent.ScheduledThreadPoolExecutor
 */
public class ScheduledExecutorFactoryBean extends ExecutorConfigurationSupport
    implements FactoryBean<ScheduledExecutorService> {

  private int poolSize = 1;

  private ScheduledExecutorTask[] scheduledExecutorTasks;

  private boolean continueScheduledExecutionAfterException = false;

  private boolean exposeUnconfigurableExecutor = false;

  private ScheduledExecutorService exposedExecutor;


  /**
   * Set the ScheduledExecutorService's pool size.
   * Default is 1.
   */
  public void setPoolSize(int poolSize) {
    Assert.isTrue(poolSize > 0"'poolSize' must be 1 or higher");
    this.poolSize = poolSize;
  }

  /**
   * Register a list of ScheduledExecutorTask objects with the ScheduledExecutorService
   * that this FactoryBean creates. Depending on each ScheduledExecutorTask's settings,
   * it will be registered via one of ScheduledExecutorService's schedule methods.
   @see java.util.concurrent.ScheduledExecutorService#schedule(java.lang.Runnable, long, java.util.concurrent.TimeUnit)
   @see java.util.concurrent.ScheduledExecutorService#scheduleWithFixedDelay(java.lang.Runnable, long, long, java.util.concurrent.TimeUnit)
   @see java.util.concurrent.ScheduledExecutorService#scheduleAtFixedRate(java.lang.Runnable, long, long, java.util.concurrent.TimeUnit)
   */
  public void setScheduledExecutorTasks(ScheduledExecutorTask[] scheduledExecutorTasks) {
    this.scheduledExecutorTasks = scheduledExecutorTasks;
  }

  /**
   * Specify whether to continue the execution of a scheduled task
   * after it threw an exception.
   <p>Default is "false", matching the native behavior of a
   {@link java.util.concurrent.ScheduledExecutorService}.
   * Switch this flag to "true" for exception-proof execution of each task,
   * continuing scheduled execution as in the case of successful execution.
   @see java.util.concurrent.ScheduledExecutorService#scheduleAtFixedRate
   */
  public void setContinueScheduledExecutionAfterException(boolean continueScheduledExecutionAfterException) {
    this.continueScheduledExecutionAfterException = continueScheduledExecutionAfterException;
  }

  /**
   * Specify whether this FactoryBean should expose an unconfigurable
   * decorator for the created executor.
   <p>Default is "false", exposing the raw executor as bean reference.
   * Switch this flag to "true" to strictly prevent clients from
   * modifying the executor's configuration.
   @see java.util.concurrent.Executors#unconfigurableScheduledExecutorService
   */
  public void setExposeUnconfigurableExecutor(boolean exposeUnconfigurableExecutor) {
    this.exposeUnconfigurableExecutor = exposeUnconfigurableExecutor;
  }


  protected ExecutorService initializeExecutor(
      ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) {

    ScheduledExecutorService executor =
        createExecutor(this.poolSize, threadFactory, rejectedExecutionHandler);

    // Register specified ScheduledExecutorTasks, if necessary.
    if (!ObjectUtils.isEmpty(this.scheduledExecutorTasks)) {
      registerTasks(this.scheduledExecutorTasks, executor);
    }

    // Wrap executor with an unconfigurable decorator.
    this.exposedExecutor = (this.exposeUnconfigurableExecutor ?
        Executors.unconfigurableScheduledExecutorService(executor: executor);

    return executor;
  }

  /**
   * Create a new {@link ScheduledExecutorService} instance.
   <p>The default implementation creates a {@link ScheduledThreadPoolExecutor}.
   * Can be overridden in subclasses to provide custom {@link ScheduledExecutorService} instances.
   @param poolSize the specified pool size
   @param threadFactory the ThreadFactory to use
   @param rejectedExecutionHandler the RejectedExecutionHandler to use
   @return a new ScheduledExecutorService instance
   @see #afterPropertiesSet()
   @see java.util.concurrent.ScheduledThreadPoolExecutor
   */
  protected ScheduledExecutorService createExecutor(
      int poolSize, ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) {

    return new ScheduledThreadPoolExecutor(poolSize, threadFactory, rejectedExecutionHandler);
  }

  /**
   * Register the specified {@link ScheduledExecutorTask ScheduledExecutorTasks}
   * on the given {@link ScheduledExecutorService}.
   @param tasks the specified ScheduledExecutorTasks (never empty)
   @param executor the ScheduledExecutorService to register the tasks on.
   */
  protected void registerTasks(ScheduledExecutorTask[] tasks, ScheduledExecutorService executor) {
    for (ScheduledExecutorTask task : tasks) {
      Runnable runnable = getRunnableToSchedule(task);
      if (task.isOneTimeTask()) {
        executor.schedule(runnable, task.getDelay(), task.getTimeUnit());
      }
      else {
        if (task.isFixedRate()) {
          executor.scheduleAtFixedRate(runnable, task.getDelay(), task.getPeriod(), task.getTimeUnit());
        }
        else {
          executor.scheduleWithFixedDelay(runnable, task.getDelay(), task.getPeriod(), task.getTimeUnit());
        }
      }
    }
  }

  /**
   * Determine the actual Runnable to schedule for the given task.
   <p>Wraps the task's Runnable in a
   {@link org.springframework.scheduling.support.DelegatingErrorHandlingRunnable}
   * that will catch and log the Exception. If necessary, it will suppress the
   * Exception according to the
   {@link #setContinueScheduledExecutionAfterException "continueScheduledExecutionAfterException"}
   * flag.
   @param task the ScheduledExecutorTask to schedule
   @return the actual Runnable to schedule (may be a decorator)
   */
  protected Runnable getRunnableToSchedule(ScheduledExecutorTask task) {
    return this.continueScheduledExecutionAfterException
        new DelegatingErrorHandlingRunnable(task.getRunnable(), TaskUtils.LOG_AND_SUPPRESS_ERROR_HANDLER)
        new DelegatingErrorHandlingRunnable(task.getRunnable(), TaskUtils.LOG_AND_PROPAGATE_ERROR_HANDLER);
  }


  public ScheduledExecutorService getObject() {
    return this.exposedExecutor;
  }

  public Class<? extends ScheduledExecutorService> getObjectType() {
    return (this.exposedExecutor != null this.exposedExecutor.getClass() : ScheduledExecutorService.class);
  }

  public boolean isSingleton() {
    return true;
  }

}