001/* 002 * Copyright 2021 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.runtime.plugins.admin.jvmstatus; 017 018import java.lang.management.ManagementFactory; 019import java.lang.management.ThreadMXBean; 020import java.util.ArrayList; 021import java.util.Arrays; 022import java.util.HashMap; 023import java.util.List; 024import java.util.Map; 025import java.util.regex.Matcher; 026import java.util.regex.Pattern; 027import java.util.stream.Collectors; 028 029import org.apache.avalon.framework.component.Component; 030 031import org.ametys.core.ui.Callable; 032 033/** 034 * Component used by the admin thread tool 035 */ 036public class ThreadInfo implements Component 037{ 038 /** Avalon role */ 039 public static final String ROLE = ThreadInfo.class.getName(); 040 041 private Pattern _groupPattern = Pattern.compile("^(.*)-\\d+$"); 042 043 /** 044 * Dump all JVM threads. 045 * @return a JSON-oriented thread dump 046 */ 047 @Callable 048 public List<Map<String, Object>> dumpAllThreads() 049 { 050 List<Map<String, Object>> result = new ArrayList<>(); 051 052 ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); 053 java.lang.management.ThreadInfo[] threads = threadMXBean.dumpAllThreads(true, true); 054 055 for (java.lang.management.ThreadInfo thread : threads) 056 { 057 Map<String, Object> info = new HashMap<>(); 058 059 String name = thread.getThreadName(); 060 String group = "-"; 061 Matcher matcher = _groupPattern.matcher(name); 062 if (matcher.matches()) 063 { 064 group = matcher.group(1); 065 } 066 067 info.put("name", name); 068 info.put("group", group); 069 info.put("state", thread.getThreadState()); 070 info.put("cpu", threadMXBean.getThreadCpuTime(thread.getThreadId())); 071 info.put("blocked-time", thread.getBlockedTime()); 072 info.put("waited-time", thread.getWaitedTime()); 073 info.put("lock-owner", thread.getLockOwnerName()); 074 075 String trace = Arrays.stream(thread.getStackTrace()) 076 .map(el -> _printStackTraceElement(el)) 077 .collect(Collectors.joining("\n")); 078 info.put("stack-trace", trace); 079 info.put("stack-ametys", Arrays.stream(thread.getStackTrace()).anyMatch(el -> el.getClassName().contains("org.ametys"))); 080 info.put("stack-size", thread.getStackTrace().length); 081 082 result.add(info); 083 } 084 085 return result; 086 } 087 088 private String _printStackTraceElement(StackTraceElement el) 089 { 090 return el.getClassName() + "." + el.getMethodName() + "(" + (el.isNativeMethod() ? "Native Method)" : (el.getFileName() != null && el.getLineNumber() >= 0 ? el.getFileName() + ":" + el.getLineNumber() + ")" : (el.getFileName() != null ? "" + el.getFileName() + ")" : "Unknown Source)"))); 091 } 092}