// ======================================================================== // Copyright (c) Webtide LLC // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // You may elect to redistribute this code under either of these licenses. // ======================================================================== package org.eclipse.jetty.monitor.integration; import java.lang.management.ManagementFactory; import java.lang.management.ThreadInfo; import java.lang.management.ThreadMXBean; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.security.Security; import java.util.HashMap; import java.util.Map; /* ------------------------------------------------------------ */ /** * Derived from the JMX bean classes created by Kees Jan Koster for the java-monitor * J2EE probe http://code.google.com/p/java-monitor-probes/source/browse/. * * @author kjkoster <kjkoster@gmail.com> */ public class JavaMonitorTools { private static final ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); private static Method findDeadlockMethod = null; static { try { findDeadlockMethod = ThreadMXBean.class.getMethod("findDeadlockedThreads"); } catch (Exception ignored) { // this is a 1.5 JVM try { findDeadlockMethod = ThreadMXBean.class.getMethod("findMonitorDeadlockedThreads"); } catch (SecurityException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } } } private ThreadInfo[] findDeadlock() throws IllegalAccessException, InvocationTargetException { final long[] threadIds = (long[])findDeadlockMethod.invoke(threadMXBean,(Object[])null); if (threadIds == null || threadIds.length < 1) { // no deadlock, we're done return null; } final ThreadInfo[] threads = threadMXBean.getThreadInfo(threadIds,Integer.MAX_VALUE); return threads; } public String getDeadlockStacktraces() { try { final ThreadInfo[] threads = findDeadlock(); if (threads == null) { // no deadlock, we're done return null; } return stacktraces(threads,0); } catch (Exception e) { return e.getMessage(); } } private static final int MAX_STACK = 10; private String stacktraces(final ThreadInfo[] threads, final int i) { if (i >= threads.length) { return ""; } final ThreadInfo thread = threads[i]; final StringBuilder trace = new StringBuilder(); for (int stack_i = 0; stack_i < Math.min(thread.getStackTrace().length,MAX_STACK); stack_i++) { if (stack_i == (MAX_STACK - 1)) { trace.append(" ..."); } else { trace.append(" at ").append(thread.getStackTrace()[stack_i]).append("\n"); } } return "\"" + thread.getThreadName() + "\", id " + thread.getThreadId() + " is " + thread.getThreadState() + " on " + thread.getLockName() + ", owned by " + thread.getLockOwnerName() + ", id " + thread.getLockOwnerId() + "\n" + trace + "\n\n" + stacktraces(threads,i + 1); } /** * We keep track of the last time we sampled the thread states. * It is a crude optimization to avoid having to query for the * threads states very often. */ private long lastSampled = 0L; private final Map<Thread.State, Integer> states = new HashMap<Thread.State, Integer>(); public int getThreadsBlocked() { sampleThreads(); return states.get(Thread.State.BLOCKED); } public int getThreadsNew() { sampleThreads(); return states.get(Thread.State.NEW); } public int getThreadsTerminated() { sampleThreads(); return states.get(Thread.State.TERMINATED); } public int getThreadsTimedWaiting() { sampleThreads(); return states.get(Thread.State.TIMED_WAITING); } public int getThreadsWaiting() { sampleThreads(); return states.get(Thread.State.WAITING); } public int getThreadsRunnable() { sampleThreads(); return states.get(Thread.State.RUNNABLE); } private synchronized void sampleThreads() { if ((lastSampled + 50L) < System.currentTimeMillis()) { lastSampled = System.currentTimeMillis(); for (final Thread.State state : Thread.State.values()) { states.put(state,0); } for (final ThreadInfo thread : threadMXBean.getThreadInfo(threadMXBean.getAllThreadIds())) { if (thread != null) { final Thread.State state = thread.getThreadState(); states.put(state,states.get(state) + 1); } else { states.put(Thread.State.TERMINATED,states.get(Thread.State.TERMINATED) + 1); } } } } private static final String POLICY = "sun.net.InetAddressCachePolicy"; public int getCacheSeconds() throws ClassNotFoundException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { final Class policy = Class.forName(POLICY); final Object returnValue = policy.getMethod("get", (Class[]) null) .invoke(null, (Object[]) null); Integer seconds = (Integer) returnValue; return seconds.intValue(); } public int getCacheNegativeSeconds() throws ClassNotFoundException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { final Class policy = Class.forName(POLICY); final Object returnValue = policy.getMethod("getNegative", (Class[]) null).invoke(null, (Object[]) null); Integer seconds = (Integer) returnValue; return seconds.intValue(); } private static final String DEFAULT = "default"; private static final String SECURITY = "security"; private static final String SYSTEM = "system"; private static final String BOTH = "both"; private static final String SECURITY_TTL = "networkaddress.cache.ttl"; private static final String SYSTEM_TTL = "sun.net.inetaddr.ttl"; private static final String SECURITY_NEGATIVE_TTL = "networkaddress.cache.negative.ttl"; private static final String SYSTEM_NEGATIVE_TTL = "sun.net.inetaddr.negative.ttl"; public String getCacheTweakedFrom() { if (Security.getProperty(SECURITY_TTL) != null) { if (System.getProperty(SYSTEM_TTL) != null) { return BOTH; } return SECURITY; } if (System.getProperty(SYSTEM_TTL) != null) { return SYSTEM; } return DEFAULT; } public String getCacheNegativeTweakedFrom() { if (Security.getProperty(SECURITY_NEGATIVE_TTL) != null) { if (System.getProperty(SYSTEM_NEGATIVE_TTL) != null) { return BOTH; } return SECURITY; } if (System.getProperty(SYSTEM_NEGATIVE_TTL) != null) { return SYSTEM; } return DEFAULT; } }