package org.stagemonitor.core.util; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.stagemonitor.core.CorePlugin; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; public final class ExecutorUtils { private static final Logger logger = LoggerFactory.getLogger(ExecutorUtils.class); private ExecutorUtils() { // don't instantiate } public static ThreadPoolExecutor createSingleThreadDeamonPool(final String threadName, int queueCapacity) { final ThreadFactory daemonThreadFactory = new NamedThreadFactory(threadName); return new MyThreadPoolExecutor(queueCapacity, daemonThreadFactory, threadName); } public static void logRejectionWarning(RejectedExecutionException e) { logger.warn("The limit of pending tasks for the executor is reached. " + "This could be due to a unreachable service such as elasticsearch or due to a spike in incoming requests. " + "Consider increasing the default capacity limit with the configuration key '" + CorePlugin.POOLS_QUEUE_CAPACITY_LIMIT_KEY + "'\n" + e.getMessage()); } public static class NamedThreadFactory implements ThreadFactory { private final String threadName; public NamedThreadFactory(String threadName) { this.threadName = threadName; } @Override public Thread newThread(Runnable r) { Thread thread = new Thread(r); thread.setDaemon(true); thread.setName(threadName); return thread; } } private static class MyThreadPoolExecutor extends ThreadPoolExecutor { private final String threadName; public MyThreadPoolExecutor(int queueCapacity, ThreadFactory daemonThreadFactory, String threadName) { super(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(queueCapacity), daemonThreadFactory); this.threadName = threadName; } @Override public String toString() { return super.toString() + "(thread name = " + threadName + ")"; } /** * Overriding this method makes sure that exceptions thrown by a task are not silently swallowed. * <p/> * Thanks to nos for this solution: http://stackoverflow.com/a/2248203/1125055 */ @Override protected void afterExecute(Runnable r, Throwable t) { super.afterExecute(r, t); if (t == null && r instanceof Future<?>) { try { Future<?> future = (Future<?>) r; if (future.isDone()) { future.get(); } } catch (CancellationException ce) { t = ce; } catch (ExecutionException ee) { t = ee.getCause(); } catch (InterruptedException ie) { Thread.currentThread().interrupt(); // ignore/reset } } if (t != null) { logger.warn("Error while executing task in " + this, t); } } } }