View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.monitor.integration;
20  
21  import java.lang.management.ManagementFactory;
22  import java.lang.management.ThreadInfo;
23  import java.lang.management.ThreadMXBean;
24  import java.lang.reflect.InvocationTargetException;
25  import java.lang.reflect.Method;
26  import java.security.Security;
27  import java.util.HashMap;
28  import java.util.Map;
29  
30  import org.eclipse.jetty.util.annotation.ManagedObject;
31  import org.eclipse.jetty.util.annotation.ManagedOperation;
32  
33  /**
34   * Derived from the JMX bean classes created by Kees Jan Koster for the java-monitor
35   * J2EE probe http://code.google.com/p/java-monitor-probes/source/browse/.
36   * 
37   * @author <a href="mailto:kjkoster@gmail.com">Kees Jan Koster</a>
38   */
39  @ManagedObject("Java Monitoring Tools")
40  public class JavaMonitorTools
41  {
42      private static final ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
43  
44      private static Method findDeadlockMethod = null;
45  
46      static
47      {
48          try
49          {
50              findDeadlockMethod = ThreadMXBean.class.getMethod("findDeadlockedThreads");
51          }
52          catch (Exception ignored)
53          {
54              // this is a 1.5 JVM
55              try
56              {
57                  findDeadlockMethod = ThreadMXBean.class.getMethod("findMonitorDeadlockedThreads");
58              }
59              catch (SecurityException e)
60              {
61                  e.printStackTrace();
62              }
63              catch (NoSuchMethodException e)
64              {
65                  e.printStackTrace();
66              }
67          }
68      }
69  
70      private ThreadInfo[] findDeadlock()
71          throws IllegalAccessException, InvocationTargetException
72      {
73          final long[] threadIds = (long[])findDeadlockMethod.invoke(threadMXBean,(Object[])null);
74  
75          if (threadIds == null || threadIds.length < 1)
76          {
77              // no deadlock, we're done
78              return null;
79          }
80  
81          final ThreadInfo[] threads = threadMXBean.getThreadInfo(threadIds,Integer.MAX_VALUE);
82          return threads;
83      }
84      @ManagedOperation(value="Detailed report on the deadlocked threads.", impact="ACTION_INFO")
85      public String getDeadlockStacktraces()
86      {
87          try
88          {
89              final ThreadInfo[] threads = findDeadlock();
90              if (threads == null)
91              {
92                  // no deadlock, we're done
93                  return null;
94              }
95  
96              return stacktraces(threads,0);
97          }
98          catch (Exception e)
99          {
100             return e.getMessage();
101         }
102     }
103 
104     private static final int MAX_STACK = 10;
105 
106     private String stacktraces(final ThreadInfo[] threads, final int i)
107     {
108         if (i >= threads.length)
109         {
110             return "";
111         }
112         final ThreadInfo thread = threads[i];
113 
114         final StringBuilder trace = new StringBuilder();
115         for (int stack_i = 0; stack_i < Math.min(thread.getStackTrace().length,MAX_STACK); stack_i++)
116         {
117             if (stack_i == (MAX_STACK - 1))
118             {
119                 trace.append("    ...");
120             }
121             else
122             {
123                 trace.append("    at ").append(thread.getStackTrace()[stack_i]).append("\n");
124             }
125         }
126 
127         return "\"" + thread.getThreadName() + "\", id " + thread.getThreadId() + " is " + thread.getThreadState() + " on " + thread.getLockName()
128                 + ", owned by " + thread.getLockOwnerName() + ", id " + thread.getLockOwnerId() + "\n" + trace + "\n\n" + stacktraces(threads,i + 1);
129     }
130 
131     /**
132      * We keep track of the last time we sampled the thread states.
133      * It is a crude optimization to avoid having to query for the
134      * threads states very often.
135      */
136     private long lastSampled = 0L;
137 
138     private final Map<Thread.State, Integer> states = new HashMap<Thread.State, Integer>();
139 
140     @ManagedOperation(value="Number of Blocked Threads")
141     public int getThreadsBlocked()
142     {
143         sampleThreads();
144 
145         return states.get(Thread.State.BLOCKED);
146     }
147 
148     @ManagedOperation(value="Number of New Threads", impact="ACTION_INFO")
149     public int getThreadsNew()
150     {
151         sampleThreads();
152 
153         return states.get(Thread.State.NEW);
154     }
155     
156     @ManagedOperation(value="Number of Terminated Threads", impact="ACTION_INFO")
157     public int getThreadsTerminated()
158     {
159         sampleThreads();
160 
161         return states.get(Thread.State.TERMINATED);
162     }
163 
164     @ManagedOperation(value="Number of Sleeping and Waiting threads")
165     public int getThreadsTimedWaiting()
166     {
167         sampleThreads();
168 
169         return states.get(Thread.State.TIMED_WAITING);
170     }
171 
172     @ManagedOperation(value="Number of Waiting Threads", impact="ACTION_INFO")
173     public int getThreadsWaiting()
174     {
175         sampleThreads();
176 
177         return states.get(Thread.State.WAITING);
178     }
179 
180     @ManagedOperation(value="Number of Runnable Threads", impact="ACTION_INFO")
181     public int getThreadsRunnable()
182     {
183         sampleThreads();
184 
185         return states.get(Thread.State.RUNNABLE);
186     }
187 
188     private synchronized void sampleThreads()
189     {
190         if ((lastSampled + 50L) < System.currentTimeMillis())
191         {
192             lastSampled = System.currentTimeMillis();
193             for (final Thread.State state : Thread.State.values())
194             {
195                 states.put(state,0);
196             }
197 
198             for (final ThreadInfo thread : threadMXBean.getThreadInfo(threadMXBean.getAllThreadIds()))
199             {
200                 if (thread != null)
201                 {
202                     final Thread.State state = thread.getThreadState();
203                     states.put(state,states.get(state) + 1);
204                 }
205                 else
206                 {
207                     states.put(Thread.State.TERMINATED,states.get(Thread.State.TERMINATED) + 1);
208                 }
209             }
210         }
211     }
212 
213     private static final String POLICY = "sun.net.InetAddressCachePolicy";
214 
215     @ManagedOperation(value="Amount of time successful DNS queries are cached for.")
216     public int getCacheSeconds() throws ClassNotFoundException,
217             IllegalAccessException, InvocationTargetException,
218             NoSuchMethodException {
219         final Class policy = Class.forName(POLICY);
220         final Object returnValue = policy.getMethod("get", (Class[]) null)
221                 .invoke(null, (Object[]) null);
222         Integer seconds = (Integer) returnValue;
223 
224         return seconds.intValue();
225     }
226 
227     @ManagedOperation(value="Amount of time failed DNS queries are cached for")
228     public int getCacheNegativeSeconds() throws ClassNotFoundException,
229             IllegalAccessException, InvocationTargetException,
230             NoSuchMethodException {
231         final Class policy = Class.forName(POLICY);
232         final Object returnValue = policy.getMethod("getNegative",
233                 (Class[]) null).invoke(null, (Object[]) null);
234         Integer seconds = (Integer) returnValue;
235 
236         return seconds.intValue();
237     }
238 
239     private static final String DEFAULT = "default";
240 
241     private static final String SECURITY = "security";
242 
243     private static final String SYSTEM = "system";
244 
245     private static final String BOTH = "both";
246 
247     private static final String SECURITY_TTL = "networkaddress.cache.ttl";
248 
249     private static final String SYSTEM_TTL = "sun.net.inetaddr.ttl";
250 
251     private static final String SECURITY_NEGATIVE_TTL = "networkaddress.cache.negative.ttl";
252 
253     private static final String SYSTEM_NEGATIVE_TTL = "sun.net.inetaddr.negative.ttl";
254 
255     @ManagedOperation(value="Cache policy for successful DNS lookups was changed from the hard-coded default")
256     public String getCacheTweakedFrom() {
257         if (Security.getProperty(SECURITY_TTL) != null) {
258             if (System.getProperty(SYSTEM_TTL) != null) {
259                 return BOTH;
260             }
261 
262             return SECURITY;
263         }
264 
265         if (System.getProperty(SYSTEM_TTL) != null) {
266             return SYSTEM;
267         }
268 
269         return DEFAULT;
270     }
271 
272     @ManagedOperation(value="Cache policy for failed DNS lookups was changed from the hard-coded default")
273     public String getCacheNegativeTweakedFrom() {
274         if (Security.getProperty(SECURITY_NEGATIVE_TTL) != null) {
275             if (System.getProperty(SYSTEM_NEGATIVE_TTL) != null) {
276                 return BOTH;
277             }
278 
279             return SECURITY;
280         }
281 
282         if (System.getProperty(SYSTEM_NEGATIVE_TTL) != null) {
283             return SYSTEM;
284         }
285 
286         return DEFAULT;
287     }
288 }