/** * Copyright 2010 CosmoCode GmbH * * 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 de.cosmocode.palava.concurrent; import java.util.Collection; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Preconditions; import com.google.inject.Inject; import com.google.inject.name.Named; import de.cosmocode.palava.core.lifecycle.Disposable; import de.cosmocode.palava.core.lifecycle.Initializable; import de.cosmocode.palava.core.lifecycle.LifecycleException; import de.cosmocode.palava.jmx.MBeanService; /** * A {@link ScheduledExecutorService} which can be configured easily configured * using the constructor. * * @author Willi Schoenborn */ final class ConfigurableScheduledExecutorService implements ScheduledExecutorService, Initializable, Disposable, ConfigurableScheduledExecutorServiceMBean { private static final Logger LOG = LoggerFactory.getLogger(ConfigurableScheduledExecutorService.class); private final String name; private final int minPoolSize; private ThreadFactory factory; private RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy(); private final long shutdownTimeout; private final TimeUnit shutdownTimeoutUnit; private ScheduledThreadPoolExecutor executor; private final MBeanService mBeanService; @Inject public ConfigurableScheduledExecutorService( @Named(ExecutorConfig.NAME) String name, @Named(ExecutorConfig.MIN_POOL_SIZE) int minPoolSize, @Named(ExecutorConfig.SHUTDOWN_TIMEOUT) long shutdownTimeout, @Named(ExecutorConfig.SHUTDOWN_TIMEOUT_UNIT) TimeUnit shutdownTimeoutUnit, ThreadFactory defaultFactory, MBeanService mBeanService) { this.name = name; this.minPoolSize = minPoolSize; this.shutdownTimeout = shutdownTimeout; this.shutdownTimeoutUnit = Preconditions.checkNotNull(shutdownTimeoutUnit, "ShutdownTimeoutUnit"); this.factory = Preconditions.checkNotNull(defaultFactory, "Factory"); this.mBeanService = Preconditions.checkNotNull(mBeanService, "MBeanService"); } @Inject(optional = true) void setFactory(@Named(ExecutorConfig.THREAD_FACTORY) ThreadFactory factory) { this.factory = Preconditions.checkNotNull(factory, "Factory"); } @Inject(optional = true) void setHandler(@Named(ExecutorConfig.REJECTION_HANDLER) RejectedExecutionHandler handler) { this.handler = Preconditions.checkNotNull(handler, "Handler"); } @Override public void initialize() throws LifecycleException { this.executor = new ScheduledThreadPoolExecutor( minPoolSize, factory, handler ); mBeanService.register(this, "name", name); } @Override public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { return executor.awaitTermination(timeout, unit); } @Override public void execute(Runnable command) { executor.execute(command); } @Override public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException { return executor.invokeAll(tasks, timeout, unit); } @Override public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException { return executor.invokeAll(tasks); } @Override public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { return executor.invokeAny(tasks, timeout, unit); } @Override public <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException { return executor.invokeAny(tasks); } @Override public boolean isShutdown() { return executor.isShutdown(); } @Override public boolean isTerminated() { return executor.isTerminated(); } @Override public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) { return executor.schedule(callable, delay, unit); } @Override public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) { return executor.schedule(command, delay, unit); } @Override public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) { return executor.scheduleAtFixedRate(command, initialDelay, period, unit); } @Override public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) { return executor.scheduleWithFixedDelay(command, initialDelay, delay, unit); } @Override public void shutdown() { executor.shutdown(); } @Override public List<Runnable> shutdownNow() { return executor.shutdownNow(); } @Override public <T> Future<T> submit(Callable<T> task) { return executor.submit(task); } @Override public <T> Future<T> submit(Runnable task, T result) { return executor.submit(task, result); } @Override public Future<?> submit(Runnable task) { return executor.submit(task); } @Override public String getName() { return name; } @Override public int getActiveCount() { return executor.getActiveCount(); } @Override public long getCompletedTaskCount() { return executor.getCompletedTaskCount(); } @Override public int getCorePoolSize() { return executor.getCorePoolSize(); } @Override public int getLargestPoolSize() { return executor.getLargestPoolSize(); } @Override public int getMaximumPoolSize() { return executor.getMaximumPoolSize(); } @Override public int getPoolSize() { return executor.getPoolSize(); } @Override public long getTaskCount() { return executor.getTaskCount(); } @Override public void dispose() throws LifecycleException { try { mBeanService.unregister(this, "name", name); } finally { try { LOG.info("Shutting down {}", this); executor.shutdown(); LOG.info("Waiting {} {} for {} to shut down", new Object[] { shutdownTimeout, shutdownTimeoutUnit.name().toLowerCase(), this }); final boolean terminated = executor.awaitTermination(shutdownTimeout, shutdownTimeoutUnit); if (terminated) { LOG.info("{} terminated successfully", this); } else { LOG.warn("{} was forced to shutdown before finish", this); } } catch (InterruptedException e) { LOG.error("Interrupted while awaiting termination", e); } } } @Override public String toString() { return String.format("ScheduledExecutorService [%s]", name); } }