blob: 8ad566baebeff10acae2924fbd454b8513f9be20 [file] [log] [blame]
//
// ========================================================================
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// 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;
import org.eclipse.jetty.util.annotation.ManagedObject;
import org.eclipse.jetty.util.annotation.ManagedOperation;
/**
* 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 <a href="mailto:kjkoster@gmail.com">Kees Jan Koster</a>
*/
@ManagedObject("Java Monitoring Tools")
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;
}
@ManagedOperation(value="Detailed report on the deadlocked threads.", impact="ACTION_INFO")
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>();
@ManagedOperation(value="Number of Blocked Threads")
public int getThreadsBlocked()
{
sampleThreads();
return states.get(Thread.State.BLOCKED);
}
@ManagedOperation(value="Number of New Threads", impact="ACTION_INFO")
public int getThreadsNew()
{
sampleThreads();
return states.get(Thread.State.NEW);
}
@ManagedOperation(value="Number of Terminated Threads", impact="ACTION_INFO")
public int getThreadsTerminated()
{
sampleThreads();
return states.get(Thread.State.TERMINATED);
}
@ManagedOperation(value="Number of Sleeping and Waiting threads")
public int getThreadsTimedWaiting()
{
sampleThreads();
return states.get(Thread.State.TIMED_WAITING);
}
@ManagedOperation(value="Number of Waiting Threads", impact="ACTION_INFO")
public int getThreadsWaiting()
{
sampleThreads();
return states.get(Thread.State.WAITING);
}
@ManagedOperation(value="Number of Runnable Threads", impact="ACTION_INFO")
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";
@ManagedOperation(value="Amount of time successful DNS queries are cached for.")
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();
}
@ManagedOperation(value="Amount of time failed DNS queries are cached for")
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";
@ManagedOperation(value="Cache policy for successful DNS lookups was changed from the hard-coded default")
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;
}
@ManagedOperation(value="Cache policy for failed DNS lookups was changed from the hard-coded 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;
}
}