View Javadoc

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