/** * Helios, OpenSource Monitoring * Brought to you by the Helios Development Group * * Copyright 2007, Helios Development Group and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. * */ package org.helios.apmrouter.util.thread; import java.lang.Thread.UncaughtExceptionHandler; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicLong; import javax.management.MalformedObjectNameException; import javax.management.ObjectName; import org.helios.apmrouter.jmx.JMXHelper; import org.helios.apmrouter.server.ServerComponentBean; import org.springframework.jmx.export.annotation.ManagedAttribute; import org.springframework.jmx.export.annotation.ManagedMetric; import org.springframework.jmx.export.annotation.ManagedOperation; import org.springframework.jmx.export.annotation.ManagedResource; import org.springframework.jmx.export.naming.ObjectNamingStrategy; /** * <p>Title: ManagedThreadPool</p> * <p>Description: A JMX exposed thread pool</p> * <p>Company: Helios Development Group LLC</p> * @author Whitehead (nwhitehead AT heliosdev DOT org) * <p><code>org.helios.apmrouter.util.thread.ManagedThreadPool</code></p> */ @ManagedResource public class ManagedThreadPool extends ServerComponentBean implements ExecutorService, ObjectNamingStrategy, ThreadFactory, RejectedExecutionHandler, UncaughtExceptionHandler { /** The delegate pool */ protected ThreadPoolExecutor inner; /** The thread group owning this pool's threads */ protected ThreadGroup threadGroup; /** The initial config for this pool */ protected final ThreadPoolConfig tpc; /** A serial number factory for created threads */ protected final AtomicLong threadNameSerial = new AtomicLong(0L); /** * Creates a new {@code ThreadPoolExecutor} with the parameters taken from a {@link ThreadPoolConfig} instance. * @param tpc the ThreadPoolConfig instance to configure this pool */ public ManagedThreadPool(ThreadPoolConfig tpc) { this.tpc = tpc; } /** * {@inheritDoc} * @see org.springframework.jmx.export.naming.ObjectNamingStrategy#getObjectName(java.lang.Object, java.lang.String) */ @Override public ObjectName getObjectName(Object managedBean, String beanKey) throws MalformedObjectNameException { objectName = JMXHelper.objectName(new StringBuilder(getClass().getPackage().getName()).append(":service=ThreadPool,name=").append(beanName)); return objectName; } /** * <p>Overrides the object name if not set already</p> * {@inheritDoc} * @see org.helios.apmrouter.server.ServerComponentBean#setObjectName(javax.management.ObjectName) */ public void setObjectName(ObjectName objectName) { if(this.objectName!=null && objectName!=null) { this.objectName = objectName; } } /** * <p>Configures and starts the thread pool. * {@inheritDoc} * @see org.helios.apmrouter.server.ServerComponentBean#doStart() */ @Override protected void doStart() throws Exception { threadGroup = new ThreadGroup(beanName); inner = new ThreadPoolExecutor( tpc.corePoolSize, tpc.maximumPoolSize, tpc.keepAliveTime, TimeUnit.MILLISECONDS, tpc.queueSize==1 ? new SynchronousQueue<Runnable>(false) : new ArrayBlockingQueue<Runnable>(tpc.queueSize, tpc.fairQueue), this, this); inner.allowCoreThreadTimeOut(tpc.coreThreadsTimeout); for(int i = 0; i < tpc.coreThreadsStarted; i++) { if(!inner.prestartCoreThread()) break; } setEventExecutor(this); } /** * <p>Stops the thread pool * {@inheritDoc} * @see org.helios.apmrouter.server.ServerComponentBean#doStop() */ @Override protected void doStop() { int i = inner.shutdownNow().size(); info("Stopped with [", i, "] tasks in flight"); } /** * Sets the spring event executor * @param eventExecutor the eventExecutor to set */ @Override public void setEventExecutor(Executor eventExecutor) { this.eventExecutor = eventExecutor; if(eventMulticaster!=null) { eventMulticaster.setTaskExecutor(eventExecutor); } } /** * {@inheritDoc} * @see java.util.concurrent.RejectedExecutionHandler#rejectedExecution(java.lang.Runnable, java.util.concurrent.ThreadPoolExecutor) */ @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor tpe) { error("Rejected Execution [", r + "]"); } /** * {@inheritDoc} * @see java.lang.Thread.UncaughtExceptionHandler#uncaughtException(java.lang.Thread, java.lang.Throwable) */ @Override public void uncaughtException(Thread thread, Throwable t) { error("Uncaught Exception on thread [", thread, "]", t); } /** * {@inheritDoc} * @see java.util.concurrent.ThreadFactory#newThread(java.lang.Runnable) */ @Override public Thread newThread(Runnable r) { Thread t = new Thread(threadGroup, r, beanName + "Thread#" + threadNameSerial.incrementAndGet()); t.setUncaughtExceptionHandler(this); t.setDaemon(tpc.daemonThreads); return t; } /** * @param value * @see java.util.concurrent.ThreadPoolExecutor#allowCoreThreadTimeOut(boolean) */ @ManagedOperation public void allowCoreThreadTimeOut(boolean value) { inner.allowCoreThreadTimeOut(value); } /** * @return boolean * @see java.util.concurrent.ThreadPoolExecutor#allowsCoreThreadTimeOut() */ @ManagedAttribute public boolean allowsCoreThreadTimeOut() { return inner.allowsCoreThreadTimeOut(); } /** * @param timeout * @param unit * @return boolean * @throws InterruptedException * @see java.util.concurrent.ThreadPoolExecutor#awaitTermination(long, java.util.concurrent.TimeUnit) */ @Override @ManagedOperation public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { return inner.awaitTermination(timeout, unit); } /** * @param command * @see java.util.concurrent.ThreadPoolExecutor#execute(java.lang.Runnable) */ @Override @ManagedOperation public void execute(Runnable command) { inner.execute(command); } /** * @return int * @see java.util.concurrent.ThreadPoolExecutor#getActiveCount() */ @ManagedMetric(category="") public int getActiveCount() { return inner.getActiveCount(); } /** * @return long * @see java.util.concurrent.ThreadPoolExecutor#getCompletedTaskCount() */ @ManagedAttribute public long getCompletedTaskCount() { return inner.getCompletedTaskCount(); } /** * @return int * @see java.util.concurrent.ThreadPoolExecutor#getCorePoolSize() */ @ManagedAttribute public int getCorePoolSize() { return inner.getCorePoolSize(); } /** * @param unit * @return long * @see java.util.concurrent.ThreadPoolExecutor#getKeepAliveTime(java.util.concurrent.TimeUnit) */ public long getKeepAliveTime(TimeUnit unit) { return inner.getKeepAliveTime(unit); } /** * @return int * @see java.util.concurrent.ThreadPoolExecutor#getLargestPoolSize() */ @ManagedAttribute public int getLargestPoolSize() { return inner.getLargestPoolSize(); } /** * @return int * @see java.util.concurrent.ThreadPoolExecutor#getMaximumPoolSize() */ @ManagedAttribute public int getMaximumPoolSize() { return inner.getMaximumPoolSize(); } /** * @return int * @see java.util.concurrent.ThreadPoolExecutor#getPoolSize() */ @ManagedAttribute public int getPoolSize() { return inner.getPoolSize(); } /** * @return BlockingQueue<Runnable> * @see java.util.concurrent.ThreadPoolExecutor#getQueue() */ public BlockingQueue<Runnable> getQueue() { return inner.getQueue(); } @ManagedAttribute public int getQueueSize() { return inner.getQueue().size(); } /** * @return RejectedExecutionHandler * @see java.util.concurrent.ThreadPoolExecutor#getRejectedExecutionHandler() */ public RejectedExecutionHandler getRejectedExecutionHandler() { return inner.getRejectedExecutionHandler(); } /** * @return long * @see java.util.concurrent.ThreadPoolExecutor#getTaskCount() */ @ManagedAttribute public long getTaskCount() { return inner.getTaskCount(); } /** * @return ThreadFactory * @see java.util.concurrent.ThreadPoolExecutor#getThreadFactory() */ public ThreadFactory getThreadFactory() { return inner.getThreadFactory(); } /** * @param arg0 * @param arg1 * @param arg2 * @return <T> List<Future<T>> * @throws InterruptedException * @see java.util.concurrent.AbstractExecutorService#invokeAll(java.util.Collection, long, java.util.concurrent.TimeUnit) */ @Override public <T> List<Future<T>> invokeAll( Collection<? extends Callable<T>> arg0, long arg1, TimeUnit arg2) throws InterruptedException { return inner.invokeAll(arg0, arg1, arg2); } /** * @param arg0 * @return <T> List<Future<T>> * @throws InterruptedException * @see java.util.concurrent.AbstractExecutorService#invokeAll(java.util.Collection) */ @Override public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> arg0) throws InterruptedException { return inner.invokeAll(arg0); } /** * @param tasks * @param timeout * @param unit * @return <T> T * @throws InterruptedException * @throws ExecutionException * @throws TimeoutException * @see java.util.concurrent.AbstractExecutorService#invokeAny(java.util.Collection, long, java.util.concurrent.TimeUnit) */ @Override public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { return inner.invokeAny(tasks, timeout, unit); } /** * @param arg0 * @return <T> T * @throws InterruptedException * @throws ExecutionException * @see java.util.concurrent.AbstractExecutorService#invokeAny(java.util.Collection) */ @Override public <T> T invokeAny(Collection<? extends Callable<T>> arg0) throws InterruptedException, ExecutionException { return inner.invokeAny(arg0); } /** * @return boolean * @see java.util.concurrent.ThreadPoolExecutor#isShutdown() */ @Override @ManagedAttribute public boolean isShutdown() { return inner.isShutdown(); } /** * @return boolean * @see java.util.concurrent.ThreadPoolExecutor#isTerminated() */ @Override @ManagedAttribute public boolean isTerminated() { return inner.isTerminated(); } /** * @return boolean * @see java.util.concurrent.ThreadPoolExecutor#isTerminating() */ @ManagedAttribute public boolean isTerminating() { return inner.isTerminating(); } /** * @return int * @see java.util.concurrent.ThreadPoolExecutor#prestartAllCoreThreads() */ @ManagedOperation public int prestartAllCoreThreads() { return inner.prestartAllCoreThreads(); } /** * @return boolean * @see java.util.concurrent.ThreadPoolExecutor#prestartCoreThread() */ @ManagedOperation public boolean prestartCoreThread() { return inner.prestartCoreThread(); } /** * * @see java.util.concurrent.ThreadPoolExecutor#purge() */ @ManagedOperation public void purge() { inner.purge(); } /** * @param task * @return boolean * @see java.util.concurrent.ThreadPoolExecutor#remove(java.lang.Runnable) */ public boolean remove(Runnable task) { return inner.remove(task); } /** * @param corePoolSize * @see java.util.concurrent.ThreadPoolExecutor#setCorePoolSize(int) */ @ManagedAttribute public void setCorePoolSize(int corePoolSize) { inner.setCorePoolSize(corePoolSize); } /** * @param time * @param unit * @see java.util.concurrent.ThreadPoolExecutor#setKeepAliveTime(long, java.util.concurrent.TimeUnit) */ public void setKeepAliveTime(long time, TimeUnit unit) { inner.setKeepAliveTime(time, unit); } /** * @param maximumPoolSize * @see java.util.concurrent.ThreadPoolExecutor#setMaximumPoolSize(int) */ @ManagedAttribute public void setMaximumPoolSize(int maximumPoolSize) { inner.setMaximumPoolSize(maximumPoolSize); } /** * @param handler * @see java.util.concurrent.ThreadPoolExecutor#setRejectedExecutionHandler(java.util.concurrent.RejectedExecutionHandler) */ public void setRejectedExecutionHandler(RejectedExecutionHandler handler) { inner.setRejectedExecutionHandler(handler); } /** * @param threadFactory * @see java.util.concurrent.ThreadPoolExecutor#setThreadFactory(java.util.concurrent.ThreadFactory) */ public void setThreadFactory(ThreadFactory threadFactory) { inner.setThreadFactory(threadFactory); } /** * * @see java.util.concurrent.ThreadPoolExecutor#shutdown() */ @Override @ManagedOperation public void shutdown() { inner.shutdown(); } /** * @return List<Runnable> * @see java.util.concurrent.ThreadPoolExecutor#shutdownNow() */ @Override public List<Runnable> shutdownNow() { return inner.shutdownNow(); } /** * @param task * @return <T> Future<T> * @see java.util.concurrent.AbstractExecutorService#submit(java.util.concurrent.Callable) */ @Override public <T> Future<T> submit(Callable<T> task) { return inner.submit(task); } /** * @param task * @param result * @return <T> Future<T> * @see java.util.concurrent.AbstractExecutorService#submit(java.lang.Runnable, java.lang.Object) */ @Override public <T> Future<T> submit(Runnable task, T result) { return inner.submit(task, result); } /** * @param task * @return Future<?> * @see java.util.concurrent.AbstractExecutorService#submit(java.lang.Runnable) */ @Override public Future<?> submit(Runnable task) { return inner.submit(task); } /** * @return String * @see java.lang.Object#toString() */ @Override public String toString() { return inner.toString(); } /** * {@inheritDoc} * @see org.helios.apmrouter.server.ServerComponent#getSupportedMetricNames() */ @Override public Set<String> getSupportedMetricNames() { return Collections.emptySet(); } /** * Returns an array of thread details for this pool * @return an array of strings */ @ManagedAttribute public String[] getThreadStats() { Set<String> threadNames = new HashSet<String>(); Thread[] threads = new Thread[inner.getMaximumPoolSize()]; int threadCount = threadGroup.enumerate(threads); for(int i = 0; i < threadCount; i++) { Thread t = threads[i]; threadNames.add(String.format("%s(%s) [%s]", t.getName(), t.getId(), t.getState().name())); } return threadNames.toArray(new String[threadNames.size()]); } }