diff --git a/src/main/java/org/datadog/jmxfetch/App.java b/src/main/java/org/datadog/jmxfetch/App.java index 37df8e394..d386bb0b0 100644 --- a/src/main/java/org/datadog/jmxfetch/App.java +++ b/src/main/java/org/datadog/jmxfetch/App.java @@ -48,6 +48,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; +import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.regex.Matcher; @@ -89,12 +90,12 @@ public App(AppConfig appConfig) { this.appConfig = appConfig; ExecutorService collectionThreadPool = - Executors.newFixedThreadPool(appConfig.getThreadPoolSize()); + buildExecutorService(appConfig.getThreadPoolSize()); collectionProcessor = new TaskProcessor(collectionThreadPool, appConfig.getReporter()); ExecutorService recoveryThreadPool = - Executors.newFixedThreadPool(appConfig.getReconnectionThreadPoolSize()); + buildExecutorService(appConfig.getReconnectionThreadPoolSize()); recoveryProcessor = new TaskProcessor(recoveryThreadPool, appConfig.getReporter()); // setup client @@ -263,7 +264,7 @@ private void clearInstances(Collection instances) { + "previous one hogging threads"); recoveryProcessor.stop(); recoveryProcessor.setThreadPoolExecutor( - Executors.newFixedThreadPool(appConfig.getReconnectionThreadPoolSize())); + buildExecutorService(appConfig.getReconnectionThreadPoolSize())); } List statuses = @@ -290,6 +291,24 @@ public TaskStatusHandler invoke( } } + /** + * Builds an {@link ExecutorService} of the specified fixed size. Threads will be created + * and executed as daemons if {@link AppConfig#isDaemon()} is true. Defaults to false. + * + * @param size The thread pool size + * @return The create executor + */ + private ExecutorService buildExecutorService(int size) { + return Executors.newFixedThreadPool(size, new ThreadFactory() { + @Override + public Thread newThread(Runnable runnable) { + Thread thread = Executors.defaultThreadFactory().newThread(runnable); + thread.setDaemon(appConfig.isDaemon()); + return thread; + } + }); + } + private String getAutoDiscoveryName(String config) { String[] splitted = config.split(System.getProperty("line.separator"), 2); @@ -470,7 +489,7 @@ public void doIteration() { + "previous one hogging threads"); collectionProcessor.stop(); collectionProcessor.setThreadPoolExecutor( - Executors.newFixedThreadPool(appConfig.getThreadPoolSize())); + buildExecutorService(appConfig.getThreadPoolSize())); } List statuses = @@ -556,7 +575,7 @@ private void fixBrokenInstances(Reporter reporter) { + "previous one hogging threads"); recoveryProcessor.stop(); recoveryProcessor.setThreadPoolExecutor( - Executors.newFixedThreadPool(appConfig.getReconnectionThreadPoolSize())); + buildExecutorService(appConfig.getReconnectionThreadPoolSize())); } Collections.shuffle(fixInstanceTasks); @@ -885,7 +904,7 @@ public void init(boolean forceNewConnection) { + "previous one hogging threads"); recoveryProcessor.stop(); recoveryProcessor.setThreadPoolExecutor( - Executors.newFixedThreadPool(appConfig.getReconnectionThreadPoolSize())); + buildExecutorService(appConfig.getReconnectionThreadPoolSize())); } List statuses = diff --git a/src/main/java/org/datadog/jmxfetch/AppConfig.java b/src/main/java/org/datadog/jmxfetch/AppConfig.java index 9185fc547..7817d102a 100644 --- a/src/main/java/org/datadog/jmxfetch/AppConfig.java +++ b/src/main/java/org/datadog/jmxfetch/AppConfig.java @@ -207,6 +207,14 @@ public class AppConfig { @Builder.Default private boolean targetDirectInstances = false; + /** + * Boolean setting to determine whether internal executors are launched as daemons or not. + * This is useful when JMXFetch is embedded in a client app, e.g. for the java tracer, + * so that the client app's exit doesn't block on the termination of these internal threads. + */ + @Builder.Default + private boolean daemon = false; + // This is used by things like APM agent to provide configuration from resources private List instanceConfigResources; // This is used by things like APM agent to provide metric configuration from resources @@ -365,4 +373,11 @@ public Integer getRefreshBeansPeriod() { public Map getGlobalTags() { return globalTags; } + + /** + * @return Whether or not internal threads will be run as daemon. + */ + public boolean isDaemon() { + return daemon; + } } diff --git a/src/main/java/org/datadog/jmxfetch/tasks/TaskProcessor.java b/src/main/java/org/datadog/jmxfetch/tasks/TaskProcessor.java index ad57573ee..f3fc1fb48 100644 --- a/src/main/java/org/datadog/jmxfetch/tasks/TaskProcessor.java +++ b/src/main/java/org/datadog/jmxfetch/tasks/TaskProcessor.java @@ -37,7 +37,7 @@ public void setThreadPoolExecutor(ExecutorService executor) { * */ public boolean ready() { ThreadPoolExecutor tpe = (ThreadPoolExecutor) threadPoolExecutor; - return !(tpe.getMaximumPoolSize() == tpe.getActiveCount()); + return !tpe.isTerminated() && !(tpe.getMaximumPoolSize() == tpe.getActiveCount()); } /**