/** * 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.ExecutorService; import java.util.concurrent.Future; import java.util.concurrent.RejectedExecutionHandler; 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; /** * An {@link ExecutorService} which can be easily configured using * the constructor. * * @author Willi Schoenborn */ final class ConfigurableExecutorService implements ExecutorService, Initializable, Disposable, ConfigurableExecutorServiceMBean { private static final Logger LOG = LoggerFactory.getLogger(ConfigurableExecutorService.class); private String name; private final int minPoolSize; private final int maxPoolSize; private final long keepAliveTime; private final TimeUnit keepAliveTimeUnit; private final QueueMode queueMode; private final int queueCapacity; private ThreadFactory factory; private RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy(); private final long shutdownTimeout; private final TimeUnit shutdownTimeoutUnit; private ThreadPoolExecutor executor; private final MBeanService mBeanService; @Inject public ConfigurableExecutorService( @Named(ExecutorConfig.NAME) String name, @Named(ExecutorConfig.MIN_POOL_SIZE) int minPoolSize, @Named(ExecutorConfig.MAX_POOL_SIZE) int maxPoolSize, @Named(ExecutorConfig.KEEP_ALIVE_TIME) long keepAliveTime, @Named(ExecutorConfig.KEEP_ALIVE_TIME_UNIT) TimeUnit keepAliveTimeUnit, @Named(ExecutorConfig.QUEUE_MODE) QueueMode queueMode, @Named(ExecutorConfig.QUEUE_CAPACITY) int queueCapacity, ThreadFactory defaultFactory, @Named(ExecutorConfig.SHUTDOWN_TIMEOUT) long shutdownTimeout, @Named(ExecutorConfig.SHUTDOWN_TIMEOUT_UNIT) TimeUnit shutdownTimeoutUnit, MBeanService mBeanService) { this.name = name; this.minPoolSize = minPoolSize; this.maxPoolSize = maxPoolSize == -1 ? Integer.MAX_VALUE : maxPoolSize; this.keepAliveTime = keepAliveTime; this.keepAliveTimeUnit = Preconditions.checkNotNull(keepAliveTimeUnit, "KeepAliveTimeUnit"); this.queueMode = Preconditions.checkNotNull(queueMode, "QueueMode"); this.queueCapacity = queueCapacity; this.factory = Preconditions.checkNotNull(defaultFactory, "Factory"); this.shutdownTimeout = shutdownTimeout; this.shutdownTimeoutUnit = Preconditions.checkNotNull(shutdownTimeoutUnit, "ShutdownTimeoutUnit"); 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 { executor = new ThreadPoolExecutor( minPoolSize, maxPoolSize, keepAliveTime, keepAliveTimeUnit, queueCapacity == -1 ? queueMode.create() : queueMode.create(queueCapacity), 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 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("ExecutorService [%s]", name); } }