diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/ProcessorNode.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/ProcessorNode.java index 9601fe590d..77308cbbc8 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/ProcessorNode.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/ProcessorNode.java @@ -73,12 +73,12 @@ public abstract class ProcessorNode extends AbstractComponentNode implements Con public abstract Requirement getInputRequirement(); - public abstract List getActiveThreads(); + public abstract List getActiveThreads(ThreadDetails threadDetails); /** * Returns the number of threads that are still 'active' in this Processor but have been terminated * via {@link #terminate()}. To understand more about these threads, such as their stack traces and - * how long they have been active, one can use {@link #getActiveThreads()} and then filter the results + * how long they have been active, one can use {@link #getActiveThreads(ThreadDetails)} and then filter the results * to include only those {@link ActiveThreadInfo} objects for which the thread is terminated. For example: * {@code getActiveThreads().stream().filter(ActiveThreadInfo::isTerminated).collect(Collectors.toList());} * diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/ThreadDetails.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/ThreadDetails.java new file mode 100644 index 0000000000..9a6348c03e --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/ThreadDetails.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.nifi.controller; + +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; + +public class ThreadDetails { + private final ThreadInfo[] threadInfos; + private final long[] deadlockedThreadIds; + private final long[] monitorDeadlockThreadIds; + + private ThreadDetails(final ThreadMXBean mbean) { + threadInfos = mbean.dumpAllThreads(true, true); + deadlockedThreadIds = mbean.findDeadlockedThreads(); + monitorDeadlockThreadIds = mbean.findMonitorDeadlockedThreads(); + } + + public ThreadInfo[] getThreadInfos() { + return threadInfos; + } + + public long[] getDeadlockedThreadIds() { + return deadlockedThreadIds; + } + + public long[] getMonitorDeadlockThreadIds() { + return monitorDeadlockThreadIds; + } + + public static ThreadDetails capture() { + final ThreadMXBean mbean = ManagementFactory.getThreadMXBean(); + return new ThreadDetails(mbean); + } +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/StandardProcessorNode.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/StandardProcessorNode.java index 96402a4531..1cc36050d9 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/StandardProcessorNode.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/StandardProcessorNode.java @@ -76,9 +76,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.Assert; -import java.lang.management.ManagementFactory; import java.lang.management.ThreadInfo; -import java.lang.management.ThreadMXBean; import java.lang.reflect.InvocationTargetException; import java.net.URL; import java.util.ArrayList; @@ -1401,14 +1399,10 @@ public class StandardProcessorNode extends ProcessorNode implements Connectable } @Override - public synchronized List getActiveThreads() { + public synchronized List getActiveThreads(final ThreadDetails threadDetails) { final long now = System.currentTimeMillis(); - final ThreadMXBean mbean = ManagementFactory.getThreadMXBean(); - final ThreadInfo[] infos = mbean.dumpAllThreads(true, true); - final long[] deadlockedThreadIds = mbean.findDeadlockedThreads(); - final long[] monitorDeadlockThreadIds = mbean.findMonitorDeadlockedThreads(); - final Map threadInfoMap = Stream.of(infos) + final Map threadInfoMap = Stream.of(threadDetails.getThreadInfos()) .collect(Collectors.toMap(ThreadInfo::getThreadId, Function.identity(), (a, b) -> a)); final List threadList = new ArrayList<>(activeThreads.size()); @@ -1419,7 +1413,7 @@ public class StandardProcessorNode extends ProcessorNode implements Connectable final long activeMillis = now - timestamp; final ThreadInfo threadInfo = threadInfoMap.get(thread.getId()); - final String stackTrace = ThreadUtils.createStackTrace(thread, threadInfo, deadlockedThreadIds, monitorDeadlockThreadIds, activeMillis); + final String stackTrace = ThreadUtils.createStackTrace(thread, threadInfo, threadDetails.getDeadlockedThreadIds(), threadDetails.getMonitorDeadlockThreadIds(), activeMillis); final ActiveThreadInfo activeThreadInfo = new ActiveThreadInfo(thread.getName(), stackTrace, activeMillis, activeTask.isTerminated()); threadList.add(activeThreadInfo); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/diagnostics/bootstrap/tasks/LongRunningProcessorTask.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/diagnostics/bootstrap/tasks/LongRunningProcessorTask.java index 73b799096b..d90ef45bd1 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/diagnostics/bootstrap/tasks/LongRunningProcessorTask.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/diagnostics/bootstrap/tasks/LongRunningProcessorTask.java @@ -19,6 +19,7 @@ package org.apache.nifi.diagnostics.bootstrap.tasks; import org.apache.nifi.controller.ActiveThreadInfo; import org.apache.nifi.controller.FlowController; import org.apache.nifi.controller.ProcessorNode; +import org.apache.nifi.controller.ThreadDetails; import org.apache.nifi.diagnostics.DiagnosticTask; import org.apache.nifi.diagnostics.DiagnosticsDumpElement; import org.apache.nifi.diagnostics.StandardDiagnosticsDumpElement; @@ -40,9 +41,10 @@ public class LongRunningProcessorTask implements DiagnosticTask { @Override public DiagnosticsDumpElement captureDump(final boolean verbose) { final List details = new ArrayList<>(); + final ThreadDetails threadDetails = ThreadDetails.capture(); for (final ProcessorNode processorNode : flowController.getFlowManager().getRootGroup().findAllProcessors()) { - final List activeThreads = processorNode.getActiveThreads(); + final List activeThreads = processorNode.getActiveThreads(threadDetails); for (final ActiveThreadInfo activeThread : activeThreads) { if (activeThread.getActiveMillis() > MIN_ACTIVE_MILLIS) { diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java index 9d70f9e63a..2fcca8b6dc 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java @@ -82,6 +82,7 @@ import org.apache.nifi.controller.ProcessorNode; import org.apache.nifi.controller.ReportingTaskNode; import org.apache.nifi.controller.Snippet; import org.apache.nifi.controller.Template; +import org.apache.nifi.controller.ThreadDetails; import org.apache.nifi.controller.flow.FlowManager; import org.apache.nifi.controller.label.Label; import org.apache.nifi.controller.queue.DropFlowFileState; @@ -3873,7 +3874,7 @@ public final class DtoFactory { private List createThreadDumpDtos(final ProcessorNode procNode) { final List threadDumps = new ArrayList<>(); - final List activeThreads = procNode.getActiveThreads(); + final List activeThreads = procNode.getActiveThreads(ThreadDetails.capture()); for (final ActiveThreadInfo threadInfo : activeThreads) { final ThreadDumpDTO dto = new ThreadDumpDTO(); dto.setStackTrace(threadInfo.getStackTrace());