001    /* ThreadInfo.java - Information on a thread
002       Copyright (C) 2006 Free Software Foundation
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    package java.lang.management;
039    
040    import java.util.Arrays;
041    
042    import javax.management.openmbean.ArrayType;
043    import javax.management.openmbean.CompositeData;
044    import javax.management.openmbean.CompositeType;
045    import javax.management.openmbean.OpenDataException;
046    import javax.management.openmbean.OpenType;
047    import javax.management.openmbean.SimpleType;
048    
049    /**
050     * <p>
051     * A class which maintains information about a particular
052     * thread.  This information includes:
053     * </p>
054     * <ul>
055     * <li><strong>General Thread Information:</strong>
056     * <ul>
057     * <li>The identifier of the thread.</li>
058     * <li>The name of the thread.</li>
059     * </ul>
060     * </li>
061     * <li><strong>Execution Information:</strong>
062     * <ul>
063     * <li>The current state of the thread (e.g. blocked, runnable)</li>
064     * <li>The object upon which the thread is blocked, either because
065     * the thread is waiting to obtain the monitor of that object to enter
066     * one of its synchronized monitor, or because
067     * {@link java.lang.Object#wait()} has been called while the thread
068     * was within a method of that object.</li>
069     * <li>The thread identifier of the current thread holding an object's
070     * monitor, upon which the thread described here is blocked.</li>
071     * <li>The stack trace of the thread (if requested on creation
072     * of this object</li>
073     * <li>The current locks held on object monitors by the thread.</li>
074     * <li>The current locks held on ownable synchronizers by the thread.</li>
075     * </ul>
076     * <li><strong>Synchronization Statistics</strong>
077     * <ul>
078     * <li>The number of times the thread has been blocked waiting for
079     * an object's monitor or in a {@link java.lang.Object#wait()} call.</li>
080     * <li>The accumulated time the thread has been blocked waiting for
081     * an object's monitor on in a {@link java.lang.Object#wait()} call.
082     * The availability of these statistics depends on the virtual machine's
083     * support for thread contention monitoring (see
084     * {@link ThreadMXBean#isThreadContentionMonitoringSupported()}.</li>
085     * </ul>
086     * </li>
087     * </ul>
088     *
089     * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
090     * @since 1.5
091     * @see ThreadMXBean#isThreadContentionMonitoringSupported()
092     */
093    public class ThreadInfo
094    {
095    
096      /**
097       * The id of the thread which this instance concerns.
098       */
099      private long threadId;
100    
101      /**
102       * The name of the thread which this instance concerns.
103       */
104      private String threadName;
105    
106      /**
107       * The state of the thread which this instance concerns.
108       */
109      private Thread.State threadState;
110    
111      /**
112       * The number of times the thread has been blocked.
113       */
114      private long blockedCount;
115    
116      /**
117       * The accumulated number of milliseconds the thread has
118       * been blocked (used only with thread contention monitoring
119       * support).
120       */
121      private long blockedTime;
122    
123      /**
124       * The name of the monitor lock on which this thread
125       * is blocked (if any).
126       */
127      private String lockName;
128    
129      /**
130       * The id of the thread which owns the monitor lock on
131       * which this thread is blocked, or <code>-1</code>
132       * if there is no owner.
133       */
134      private long lockOwnerId;
135    
136      /**
137       * The name of the thread which owns the monitor lock on
138       * which this thread is blocked, or <code>null</code>
139       * if there is no owner.
140       */
141      private String lockOwnerName;
142    
143      /**
144       * The number of times the thread has been in a waiting
145       * state.
146       */
147      private long waitedCount;
148    
149      /**
150       * The accumulated number of milliseconds the thread has
151       * been waiting (used only with thread contention monitoring
152       * support).
153       */
154      private long waitedTime;
155    
156      /**
157       * True if the thread is in a native method.
158       */
159      private boolean isInNative;
160    
161      /**
162       * True if the thread is suspended.
163       */
164      private boolean isSuspended;
165    
166      /**
167       * The stack trace of the thread.
168       */
169      private StackTraceElement[] trace;
170    
171      /**
172       * The array of information on monitors locked by the thread.
173       */
174      private MonitorInfo[] lockedMonitors;
175    
176      /**
177       * The array of information on ownable synchronizers locked
178       * by the thread.
179       */
180      private LockInfo[] lockedSynchronizers;
181    
182      /**
183       * Cache a local reference to the thread management bean.
184       */
185      private static ThreadMXBean bean = null;
186    
187      /**
188       * Cache the {@link javax.management.openmbean.CompositeType}
189       * for the {@link StackTraceElement}.
190       */
191      private static CompositeType seType;
192    
193      /**
194       * Constructs a new {@link ThreadInfo} corresponding
195       * to the thread specified.
196       *
197       * @param thread the thread on which the new instance
198       *               will be based.
199       * @param blockedCount the number of times the thread
200       *                     has been blocked.
201       * @param blockedTime the accumulated number of milliseconds
202       *                    the specified thread has been blocked
203       *                    (only used with contention monitoring enabled)
204       * @param lock the monitor lock the thread is waiting for
205       *             (only used if blocked)
206       * @param lockOwner the thread which owns the monitor lock, or
207       *                  <code>null</code> if it doesn't have an owner
208       *                  (only used if blocked)
209       * @param waitedCount the number of times the thread has been in a
210       *                    waiting state.
211       * @param waitedTime the accumulated number of milliseconds the
212       *                   specified thread has been waiting
213       *                   (only used with contention monitoring enabled)
214       * @param isInNative true if the thread is in a native method.
215       * @param isSuspended true if the thread is suspended.
216       * @param trace the stack trace of the thread to a pre-determined
217       *              depth (see VMThreadMXBeanImpl)
218       */
219      private ThreadInfo(Thread thread, long blockedCount, long blockedTime,
220                         Object lock, Thread lockOwner, long waitedCount,
221                         long waitedTime, boolean isInNative, boolean isSuspended,
222                         StackTraceElement[] trace)
223      {
224        this(thread, blockedCount, blockedTime, lock, lockOwner, waitedCount,
225             waitedTime, isInNative, isSuspended, trace, new MonitorInfo[]{},
226             new LockInfo[]{});
227      }
228    
229      /**
230       * Constructs a new {@link ThreadInfo} corresponding
231       * to the thread specified.
232       *
233       * @param thread the thread on which the new instance
234       *               will be based.
235       * @param blockedCount the number of times the thread
236       *                     has been blocked.
237       * @param blockedTime the accumulated number of milliseconds
238       *                    the specified thread has been blocked
239       *                    (only used with contention monitoring enabled)
240       * @param lock the monitor lock the thread is waiting for
241       *             (only used if blocked)
242       * @param lockOwner the thread which owns the monitor lock, or
243       *                  <code>null</code> if it doesn't have an owner
244       *                  (only used if blocked)
245       * @param waitedCount the number of times the thread has been in a
246       *                    waiting state.
247       * @param waitedTime the accumulated number of milliseconds the
248       *                   specified thread has been waiting
249       *                   (only used with contention monitoring enabled)
250       * @param isInNative true if the thread is in a native method.
251       * @param isSuspended true if the thread is suspended.
252       * @param trace the stack trace of the thread to a pre-determined
253       *              depth (see VMThreadMXBeanImpl)
254       * @param lockedMonitors an array of {@link MonitorInfo} objects
255       *                       representing locks held on object monitors
256       *                       by the thread.
257       * @param lockedSynchronizers an array of {@link LockInfo} objects
258       *                            representing locks held on ownable
259       *                            synchronizers by the thread. 
260       * @since 1.6
261       */
262      private ThreadInfo(Thread thread, long blockedCount, long blockedTime,
263                         Object lock, Thread lockOwner, long waitedCount,
264                         long waitedTime, boolean isInNative, boolean isSuspended,
265                         StackTraceElement[] trace, MonitorInfo[] lockedMonitors,
266                         LockInfo[] lockedSynchronizers)
267      {
268        this(thread.getId(), thread.getName(), thread.getState(), blockedCount, blockedTime,
269             lock == null ? null : lock.getClass().getName() + "@" + 
270                   Integer.toHexString(System.identityHashCode(lock)),
271             lockOwner == null ? -1 : lockOwner.getId(),
272             lockOwner == null ? null : lockOwner.getName(),
273             waitedCount, waitedTime, isInNative, isSuspended,
274             trace, lockedMonitors, lockedSynchronizers);
275      }
276    
277      /**
278       * Constructs a new {@link ThreadInfo} corresponding
279       * to the thread details specified.
280       *
281       * @param threadId the id of the thread on which this
282       *                 new instance will be based.
283       * @param threadName the name of the thread on which
284       *                 this new instance will be based.
285       * @param threadState the state of the thread on which
286       *                 this new instance will be based.
287       * @param blockedCount the number of times the thread
288       *                     has been blocked.
289       * @param blockedTime the accumulated number of milliseconds
290       *                    the specified thread has been blocked
291       *                    (only used with contention monitoring enabled)
292       * @param lockName the name of the monitor lock the thread is waiting for
293       *                 (only used if blocked)
294       * @param lockOwnerId the id of the thread which owns the monitor
295       *                  lock, or <code>-1</code> if it doesn't have an owner
296       *                  (only used if blocked)
297       * @param lockOwnerName the name of the thread which owns the monitor
298       *                  lock, or <code>null</code> if it doesn't have an 
299       *                  owner (only used if blocked)
300       * @param waitedCount the number of times the thread has been in a
301       *                    waiting state.
302       * @param waitedTime the accumulated number of milliseconds the
303       *                   specified thread has been waiting
304       *                   (only used with contention monitoring enabled)
305       * @param isInNative true if the thread is in a native method.
306       * @param isSuspended true if the thread is suspended.
307       * @param trace the stack trace of the thread to a pre-determined
308       *              depth (see VMThreadMXBeanImpl)
309       */
310      private ThreadInfo(long threadId, String threadName, Thread.State threadState,
311                         long blockedCount, long blockedTime, String lockName, 
312                         long lockOwnerId, String lockOwnerName, long waitedCount,
313                         long waitedTime, boolean isInNative, boolean isSuspended,
314                         StackTraceElement[] trace)
315      {
316        this(threadId, threadName, threadState, blockedCount, blockedTime,
317             lockName, lockOwnerId, lockOwnerName, waitedCount, waitedTime,
318             isInNative, isSuspended, trace, new MonitorInfo[]{}, new LockInfo[]{});
319      }
320    
321      /**
322       * Constructs a new {@link ThreadInfo} corresponding
323       * to the thread details specified.
324       *
325       * @param threadId the id of the thread on which this
326       *                 new instance will be based.
327       * @param threadName the name of the thread on which
328       *                 this new instance will be based.
329       * @param threadState the state of the thread on which
330       *                 this new instance will be based.
331       * @param blockedCount the number of times the thread
332       *                     has been blocked.
333       * @param blockedTime the accumulated number of milliseconds
334       *                    the specified thread has been blocked
335       *                    (only used with contention monitoring enabled)
336       * @param lockName the name of the monitor lock the thread is waiting for
337       *                 (only used if blocked)
338       * @param lockOwnerId the id of the thread which owns the monitor
339       *                  lock, or <code>-1</code> if it doesn't have an owner
340       *                  (only used if blocked)
341       * @param lockOwnerName the name of the thread which owns the monitor
342       *                  lock, or <code>null</code> if it doesn't have an 
343       *                  owner (only used if blocked)
344       * @param waitedCount the number of times the thread has been in a
345       *                    waiting state.
346       * @param waitedTime the accumulated number of milliseconds the
347       *                   specified thread has been waiting
348       *                   (only used with contention monitoring enabled)
349       * @param isInNative true if the thread is in a native method.
350       * @param isSuspended true if the thread is suspended.
351       * @param trace the stack trace of the thread to a pre-determined
352       *              depth (see VMThreadMXBeanImpl)
353       * @param lockedMonitors an array of {@link MonitorInfo} objects
354       *                       representing locks held on object monitors
355       *                       by the thread.
356       * @param lockedSynchronizers an array of {@link LockInfo} objects
357       *                            representing locks held on ownable
358       *                            synchronizers by the thread. 
359       *
360       * @since 1.6
361       */
362      private ThreadInfo(long threadId, String threadName, Thread.State threadState,
363                         long blockedCount, long blockedTime, String lockName, 
364                         long lockOwnerId, String lockOwnerName, long waitedCount,
365                         long waitedTime, boolean isInNative, boolean isSuspended,
366                         StackTraceElement[] trace, MonitorInfo[] lockedMonitors,
367                         LockInfo[] lockedSynchronizers)
368      {
369        this.threadId = threadId;
370        this.threadName = threadName;
371        this.threadState = threadState;
372        this.blockedCount = blockedCount;
373        this.blockedTime = blockedTime;
374        this.lockName = lockName;
375        this.lockOwnerId = lockOwnerId;
376        this.lockOwnerName = lockOwnerName;
377        this.waitedCount = waitedCount;
378        this.waitedTime = waitedTime;
379        this.isInNative = isInNative;
380        this.isSuspended = isSuspended;
381        this.trace = trace;
382        this.lockedMonitors = lockedMonitors;
383        this.lockedSynchronizers = lockedSynchronizers;
384      }
385    
386      /**
387       * Checks for an attribute in a {@link CompositeData} structure
388       * with the correct type.
389       *
390       * @param ctype the composite data type to check.
391       * @param name the name of the attribute.
392       * @param type the type to check for.
393       * @throws IllegalArgumentException if the attribute is absent
394       *                                  or of the wrong type.
395       */
396      static void checkAttribute(CompositeType ctype, String name,
397                                 OpenType type)
398        throws IllegalArgumentException
399      {
400        OpenType foundType = ctype.getType(name);
401        if (foundType == null)
402          throw new IllegalArgumentException("Could not find a field named " +
403                                             name);
404        if (!(foundType.equals(type)))
405          throw new IllegalArgumentException("Field " + name + " is not of " +
406                                             "type " + type.getClassName());
407      }
408    
409      /**
410       * Returns the {@link javax.management.openmbean.CompositeType} for
411       * a {@link StackTraceElement}.
412       *
413       * @return the type for the stack trace element.
414       */
415      static CompositeType getStackTraceType()
416      {
417        if (seType == null)
418          try
419            {
420              seType = new CompositeType(StackTraceElement.class.getName(),
421                                         "An element of a stack trace",
422                                         new String[] { "className", "methodName",
423                                                        "fileName", "lineNumber",
424                                                        "nativeMethod" 
425                                         },
426                                         new String[] { "Name of the class",
427                                                        "Name of the method",
428                                                        "Name of the source code file",
429                                                        "Line number",
430                                                        "True if this is a native method" 
431                                         },
432                                         new OpenType[] {
433                                           SimpleType.STRING, SimpleType.STRING,
434                                           SimpleType.STRING, SimpleType.INTEGER,
435                                           SimpleType.BOOLEAN 
436                                         });
437            }
438          catch (OpenDataException e)
439            {
440              throw new IllegalStateException("Something went wrong in creating " +
441                                              "the composite data type for the " +
442                                              "stack trace element.", e);
443            }
444        return seType;
445      }
446    
447      /**
448       * <p>
449       * Returns a {@link ThreadInfo} instance using the values
450       * given in the supplied
451       * {@link javax.management.openmbean.CompositeData} object.
452       * The composite data instance should contain the following
453       * attributes with the specified types:
454       * </p>
455       * <table>
456       * <th><td>Name</td><td>Type</td></th>
457       * <tr><td>threadId</td><td>java.lang.Long</td></tr>
458       * <tr><td>threadName</td><td>java.lang.String</td></tr>
459       * <tr><td>threadState</td><td>java.lang.String</td></tr>
460       * <tr><td>suspended</td><td>java.lang.Boolean</td></tr>
461       * <tr><td>inNative</td><td>java.lang.Boolean</td></tr>
462       * <tr><td>blockedCount</td><td>java.lang.Long</td></tr>
463       * <tr><td>blockedTime</td><td>java.lang.Long</td></tr>
464       * <tr><td>waitedCount</td><td>java.lang.Long</td></tr>
465       * <tr><td>waitedTime</td><td>java.lang.Long</td></tr>
466       * <tr><td>lockName</td><td>java.lang.String</td></tr>
467       * <tr><td>lockOwnerId</td><td>java.lang.Long</td></tr>
468       * <tr><td>lockOwnerName</td><td>java.lang.String</td></tr>
469       * <tr><td>stackTrace</td><td>javax.management.openmbean.CompositeData[]
470       * </td></tr>
471       * </table>
472       * <p>
473       * The stack trace is further described as:
474       * </p>
475       * <table>
476       * <th><td>Name</td><td>Type</td></th>
477       * <tr><td>className</td><td>java.lang.String</td></tr>
478       * <tr><td>methodName</td><td>java.lang.String</td></tr>
479       * <tr><td>fileName</td><td>java.lang.String</td></tr>
480       * <tr><td>lineNumber</td><td>java.lang.Integer</td></tr>
481       * <tr><td>nativeMethod</td><td>java.lang.Boolean</td></tr>
482       * </table>
483       * 
484       * @param data the composite data structure to take values from.
485       * @return a new instance containing the values from the 
486       *         composite data structure, or <code>null</code>
487       *         if the data structure was also <code>null</code>.
488       * @throws IllegalArgumentException if the composite data structure
489       *                                  does not match the structure
490       *                                  outlined above.
491       */
492      public static ThreadInfo from(CompositeData data)
493      {
494        if (data == null)
495          return null;
496        CompositeType type = data.getCompositeType();
497        checkAttribute(type, "ThreadId", SimpleType.LONG);
498        checkAttribute(type, "ThreadName", SimpleType.STRING);
499        checkAttribute(type, "ThreadState", SimpleType.STRING);
500        checkAttribute(type, "Suspended", SimpleType.BOOLEAN);
501        checkAttribute(type, "InNative", SimpleType.BOOLEAN);
502        checkAttribute(type, "BlockedCount", SimpleType.LONG);
503        checkAttribute(type, "BlockedTime", SimpleType.LONG);
504        checkAttribute(type, "WaitedCount", SimpleType.LONG);
505        checkAttribute(type, "WaitedTime", SimpleType.LONG);
506        checkAttribute(type, "LockName", SimpleType.STRING);
507        checkAttribute(type, "LockOwnerId", SimpleType.LONG);
508        checkAttribute(type, "LockOwnerName", SimpleType.STRING);
509        try
510          {
511            checkAttribute(type, "StackTrace",
512                           new ArrayType(1, getStackTraceType()));
513          }
514        catch (OpenDataException e)
515          {
516            throw new IllegalStateException("Something went wrong in creating " +
517                                            "the array for the stack trace element.",
518                                            e);
519          }
520        OpenType foundType = type.getType("LockedMonitors");
521        if (foundType != null)
522          try
523            {
524              CompositeType mType = new CompositeType(MonitorInfo.class.getName(),
525                                                      "Information on a object monitor lock",
526                                                      new String[] { "ClassName",
527                                                                     "IdentityHashCode",
528                                                                     "LockedStackDepth",
529                                                                     "LockedStackFrame"
530                                                      },
531                                                      new String[] { "Name of the class",
532                                                                     "Identity hash code " +
533                                                                     "of the class",
534                                                                     "Stack depth at time " +
535                                                                     "of lock",
536                                                                     "Stack frame at time " +
537                                                                     "of lock",
538                                                      },
539                                                      new OpenType[] {
540                                                        SimpleType.STRING, SimpleType.INTEGER,
541                                                        SimpleType.INTEGER, getStackTraceType()
542                                                      });
543              if (!(foundType.equals(new ArrayType(1, mType))))
544                throw new IllegalArgumentException("Field LockedMonitors is not of " +
545                                                   "type " + mType.getClassName());
546            }
547        catch (OpenDataException e)
548          {
549            throw new IllegalStateException("Something went wrong in creating " +
550                                            "the composite data type for the " +
551                                            "object monitor information array.", e);
552          }
553        foundType = type.getType("LockedSynchronizers");
554        if (foundType != null)
555          try
556            {
557              CompositeType lType = new CompositeType(LockInfo.class.getName(),
558                                                      "Information on a lock",
559                                                      new String[] { "ClassName",
560                                                                     "IdentityHashCode"
561                                                      },
562                                                      new String[] { "Name of the class",
563                                                                     "Identity hash code " +
564                                                                     "of the class"
565                                                      },
566                                                      new OpenType[] {
567                                                        SimpleType.STRING, SimpleType.INTEGER
568                                                      });
569              if (!(foundType.equals(new ArrayType(1, lType))))
570                throw new IllegalArgumentException("Field LockedSynchronizers is not of " +
571                                                   "type " + lType.getClassName());
572            }
573        catch (OpenDataException e)
574          {
575            throw new IllegalStateException("Something went wrong in creating " +
576                                            "the composite data type for the " +
577                                            "ownable synchronizerinformation array.", e);
578          }
579        CompositeData[] dTraces = (CompositeData[]) data.get("StackTrace");
580        StackTraceElement[] traces = new StackTraceElement[dTraces.length];
581        for (int a = 0; a < dTraces.length; ++a)
582            /* FIXME: We can't use the boolean as there is no available
583               constructor. */
584          traces[a] = 
585            new StackTraceElement((String) dTraces[a].get("ClassName"),
586                                  (String) dTraces[a].get("MethodName"),
587                                  (String) dTraces[a].get("FileName"),
588                                  ((Integer) 
589                                   dTraces[a].get("LineNumber")).intValue());
590        MonitorInfo[] mInfo;
591        if (data.containsKey("LockedMonitors"))
592          {
593            CompositeData[] dmInfos = (CompositeData[]) data.get("LockedMonitors");
594            mInfo = new MonitorInfo[dmInfos.length];
595            for (int a = 0; a < dmInfos.length; ++a)
596              mInfo[a] = MonitorInfo.from(dmInfos[a]);
597          }
598        else
599          mInfo = new MonitorInfo[]{};
600        LockInfo[] lInfo;
601        if (data.containsKey("LockedSynchronizers"))
602          {
603            CompositeData[] dlInfos = (CompositeData[]) data.get("LockedSynchronizers");
604            lInfo = new LockInfo[dlInfos.length];
605            for (int a = 0; a < dlInfos.length; ++a)
606              lInfo[a] = new LockInfo((String) dlInfos[a].get("ClassName"),
607                                      (Integer) dlInfos[a].get("IdentityHashCode"));
608          }
609        else
610          lInfo = new LockInfo[]{};
611        return new ThreadInfo(((Long) data.get("ThreadId")).longValue(),
612                              (String) data.get("ThreadName"),
613                              Thread.State.valueOf((String) data.get("ThreadState")),
614                              ((Long) data.get("BlockedCount")).longValue(),
615                              ((Long) data.get("BlockedTime")).longValue(),
616                              (String) data.get("LockName"),
617                              ((Long) data.get("LockOwnerId")).longValue(),
618                              (String) data.get("LockOwnerName"),  
619                              ((Long) data.get("WaitedCount")).longValue(),
620                              ((Long) data.get("WaitedTime")).longValue(),
621                              ((Boolean) data.get("InNative")).booleanValue(),
622                              ((Boolean) data.get("Suspended")).booleanValue(),
623                              traces, mInfo, lInfo);
624      }
625    
626      /**
627       * Returns the number of times this thread has been
628       * in the {@link java.lang.Thread.State#BLOCKED} state.
629       * A thread enters this state when it is waiting to
630       * obtain an object's monitor.  This may occur either
631       * on entering a synchronized method for the first time,
632       * or on re-entering it following a call to
633       * {@link java.lang.Object#wait()}.
634       *
635       * @return the number of times this thread has been blocked.
636       */
637      public long getBlockedCount()
638      {
639        return blockedCount;
640      }
641    
642      /**
643       * <p>
644       * Returns the accumulated number of milliseconds this
645       * thread has been in the
646       * {@link java.lang.Thread.State#BLOCKED} state
647       * since thread contention monitoring was last enabled.
648       * A thread enters this state when it is waiting to
649       * obtain an object's monitor.  This may occur either
650       * on entering a synchronized method for the first time,
651       * or on re-entering it following a call to
652       * {@link java.lang.Object#wait()}.
653       * </p>
654       * <p>
655       * Use of this method requires virtual machine support
656       * for thread contention monitoring and for this support
657       * to be enabled.
658       * </p>
659       * 
660       * @return the accumulated time (in milliseconds) that this
661       *         thread has spent in the blocked state, since
662       *         thread contention monitoring was enabled, or -1
663       *         if thread contention monitoring is disabled.
664       * @throws UnsupportedOperationException if the virtual
665       *                                       machine does not
666       *                                       support contention
667       *                                       monitoring.
668       * @see ThreadMXBean#isThreadContentionMonitoringEnabled()
669       * @see ThreadMXBean#isThreadContentionMonitoringSupported()
670       */
671      public long getBlockedTime()
672      {
673        if (bean == null)
674          bean = ManagementFactory.getThreadMXBean();
675        // Will throw UnsupportedOperationException for us
676        if (bean.isThreadContentionMonitoringEnabled())
677          return blockedTime;
678        else
679          return -1;
680      }
681    
682      /**
683       * Returns an array of {@link MonitorInfo} objects representing
684       * information on the locks on object monitors held by the thread.
685       * If no locks are held, or such information was not requested
686       * on creating this {@link ThreadInfo} object, a zero-length
687       * array will be returned.
688       *
689       * @return information on object monitors locked by this thread.
690       */
691      public MonitorInfo[] getLockedMonitors()
692      {
693        return lockedMonitors;
694      }
695    
696      /**
697       * Returns an array of {@link LockInfo} objects representing
698       * information on the locks on ownable synchronizers held by the thread.
699       * If no locks are held, or such information was not requested
700       * on creating this {@link ThreadInfo} object, a zero-length
701       * array will be returned.
702       *
703       * @return information on ownable synchronizers locked by this thread.
704       */
705      public LockInfo[] getLockedSynchronizers()
706      {
707        return lockedSynchronizers;
708      }
709    
710      /**
711       * <p>
712       * Returns a {@link LockInfo} object representing the
713       * lock on which this thread is blocked.  If the thread
714       * is not blocked, this method returns <code>null</code>.
715       * </p>
716       * <p>
717       * The thread may be blocked due to one of three reasons:
718       * </p>
719       * <ol>
720       * <li>The thread is in the <code>BLOCKED</code> state
721       * waiting to acquire an object monitor in order to enter
722       * a synchronized method or block.</li>
723       * <li>The thread is in the <code>WAITING</code> or
724       * <code>TIMED_WAITING</code> state due to a call to
725       * {@link java.lang.Object#wait()}.</li>
726       * <li>The thread is in the <code>WAITING</code> or
727       * <code>TIMED_WAITING</code> state due to a call
728       * to {@link java.util.concurrent.locks.LockSupport#park()}.
729       * The lock is the return value of
730       * {@link java.util.concurrent.locks.LockSupport#getBlocker()}.</li>
731       * </ol>
732       * 
733       * @return a {@link LockInfo} object representing the lock on
734       *         which the thread is blocked, or <code>null</code> if
735       *         the thread isn't blocked.
736       * @since 1.6
737       * @see #getLockName()
738       */
739      public LockInfo getLockInfo()
740      {
741        String lockName = getLockName();
742        int at = lockName.indexOf('@');
743        return new LockInfo(lockName.substring(0, at),
744                            Integer.decode(lockName.substring(at + 1)));
745      }
746    
747      /**
748       * <p>
749       * Returns a {@link java.lang.String} representation of
750       * the lock on which this thread is blocked.  If
751       * the thread is not blocked, this method returns
752       * <code>null</code>.
753       * </p>
754       * <p>
755       * The returned {@link java.lang.String} is constructed
756       * using the class name and identity hashcode (usually
757       * the memory address of the object) of the lock.  The
758       * two are separated by the '@' character, and the identity
759       * hashcode is represented in hexadecimal.  Thus, for a
760       * lock, <code>l</code>, the returned value is
761       * the result of concatenating
762       * <code>l.getClass().getName()</code>, <code>"@"</code>
763       * and
764       * <code>Integer.toHexString(System.identityHashCode(l))</code>.
765       * The value is only unique to the extent that the identity
766       * hash code is also unique.  The value is the same as would
767       * be returned by <code>getLockInfo().toString()</code>
768       * </p>
769       *
770       * @return a string representing the lock on which this
771       *         thread is blocked, or <code>null</code> if
772       *         the thread is not blocked.
773       */
774      public String getLockName()
775      {
776        if (!isThreadBlocked())
777          return null;
778        return lockName;
779      }
780    
781      /**
782       * Returns the identifier of the thread which owns the
783       * monitor lock this thread is waiting for.  -1 is returned
784       * if either this thread is not blocked, or the lock is
785       * not held by any other thread.
786       * 
787       * @return the thread identifier of thread holding the lock
788       *         this thread is waiting for, or -1 if the thread
789       *         is not blocked or the lock is not held by another
790       *         thread.
791       */
792      public long getLockOwnerId()
793      {
794        if (!isThreadBlocked())
795          return -1;
796        return lockOwnerId;
797      }
798    
799      /**
800       * Returns the name of the thread which owns the
801       * monitor lock this thread is waiting for.  <code>null</code>
802       * is returned if either this thread is not blocked,
803       * or the lock is not held by any other thread.
804       * 
805       * @return the thread identifier of thread holding the lock
806       *         this thread is waiting for, or <code>null</code>
807       *         if the thread is not blocked or the lock is not
808       *         held by another thread.
809       */
810      public String getLockOwnerName()
811      {
812        if (!isThreadBlocked())
813          return null;
814        return lockOwnerName;
815      }
816    
817      /**
818       * <p>
819       * Returns the stack trace of this thread to the depth
820       * specified on creation of this {@link ThreadInfo}
821       * object.  If the depth is zero, an empty array will
822       * be returned.  For non-zero arrays, the elements
823       * start with the most recent trace at position zero.
824       * The bottom of the stack represents the oldest method
825       * invocation which meets the depth requirements.
826       * </p>
827       * <p>
828       * Some virtual machines may not be able to return
829       * stack trace information for a thread.  In these
830       * cases, an empty array will also be returned.
831       * </p>
832       * 
833       * @return an array of {@link java.lang.StackTraceElement}s
834       *         representing the trace of this thread.
835       */
836      public StackTraceElement[] getStackTrace()
837      {
838        return trace;
839      }
840    
841      /**
842       * Returns the identifier of the thread associated with
843       * this instance of {@link ThreadInfo}.
844       *
845       * @return the thread's identifier.
846       */
847      public long getThreadId()
848      {
849        return threadId;
850      }
851    
852      /**
853       * Returns the name of the thread associated with
854       * this instance of {@link ThreadInfo}.
855       *
856       * @return the thread's name.
857       */
858      public String getThreadName()
859      {
860        return threadName;
861      }
862    
863      /**
864       * Returns the state of the thread associated with
865       * this instance of {@link ThreadInfo}.
866       *
867       * @return the thread's state.
868       */
869      public Thread.State getThreadState()
870      {
871        return threadState;
872      }
873        
874      /**
875       * Returns the number of times this thread has been
876       * in the {@link java.lang.Thread.State#WAITING} 
877       * or {@link java.lang.Thread.State#TIMED_WAITING} state.
878       * A thread enters one of these states when it is waiting
879       * due to a call to {@link java.lang.Object.wait()},
880       * {@link java.lang.Object.join()} or
881       * {@link java.lang.concurrent.locks.LockSupport.park()},
882       * either with an infinite or timed delay, respectively. 
883       *
884       * @return the number of times this thread has been waiting.
885       */
886      public long getWaitedCount()
887      {
888        return waitedCount;
889      }
890    
891      /**
892       * <p>
893       * Returns the accumulated number of milliseconds this
894       * thread has been in the
895       * {@link java.lang.Thread.State#WAITING} or
896       * {@link java.lang.Thread.State#TIMED_WAITING} state,
897       * since thread contention monitoring was last enabled.
898       * A thread enters one of these states when it is waiting
899       * due to a call to {@link java.lang.Object.wait()},
900       * {@link java.lang.Object.join()} or
901       * {@link java.lang.concurrent.locks.LockSupport.park()},
902       * either with an infinite or timed delay, respectively. 
903       * </p>
904       * <p>
905       * Use of this method requires virtual machine support
906       * for thread contention monitoring and for this support
907       * to be enabled.
908       * </p>
909       * 
910       * @return the accumulated time (in milliseconds) that this
911       *         thread has spent in one of the waiting states, since
912       *         thread contention monitoring was enabled, or -1
913       *         if thread contention monitoring is disabled.
914       * @throws UnsupportedOperationException if the virtual
915       *                                       machine does not
916       *                                       support contention
917       *                                       monitoring.
918       * @see ThreadMXBean#isThreadContentionMonitoringEnabled()
919       * @see ThreadMXBean#isThreadContentionMonitoringSupported()
920       */
921      public long getWaitedTime()
922      {
923        if (bean == null)
924          bean = ManagementFactory.getThreadMXBean();
925        // Will throw UnsupportedOperationException for us
926        if (bean.isThreadContentionMonitoringEnabled())
927          return waitedTime;
928        else
929          return -1;
930      }
931    
932      /**
933       * Returns true if the thread is in a native method.  This
934       * excludes native code which forms part of the virtual
935       * machine itself, or which results from Just-In-Time
936       * compilation.
937       *
938       * @return true if the thread is in a native method, false
939       *         otherwise.
940       */
941      public boolean isInNative()
942      {
943        return isInNative;
944      }
945    
946      /**
947       * Returns true if the thread has been suspended using
948       * {@link java.lang.Thread#suspend()}.
949       *
950       * @return true if the thread is suspended, false otherwise.
951       */
952      public boolean isSuspended()
953      {
954        return isSuspended;
955      }
956    
957      /**
958       * Returns a {@link java.lang.String} representation of
959       * this {@link ThreadInfo} object.  This takes the form
960       * <code>java.lang.management.ThreadInfo[id=tid, name=n,
961       * state=s, blockedCount=bc, waitedCount=wc, isInNative=iin,
962       * isSuspended=is]</code>, where <code>tid</code> is
963       * the thread identifier, <code>n</code> is the
964       * thread name, <code>s</code> is the thread state,
965       * <code>bc</code> is the blocked state count,
966       * <code>wc</code> is the waiting state count and
967       * <code>iin</code> and <code>is</code> are boolean
968       * flags to indicate the thread is in native code or
969       * suspended respectively.  If the thread is blocked,
970       * <code>lock=l, lockOwner=lo</code> is also included,
971       * where <code>l</code> is the lock waited for, and
972       * <code>lo</code> is the thread which owns the lock
973       * (or null if there is no owner).
974       *
975       * @return the string specified above.
976       */
977      public String toString()
978      {
979        return getClass().getName() +
980          "[id=" + threadId + 
981          ", name=" + threadName +
982          ", state=" + threadState +
983          ", blockedCount=" + blockedCount +
984          ", waitedCount=" + waitedCount +
985          ", isInNative=" + isInNative + 
986          ", isSuspended=" + isSuspended +
987          (isThreadBlocked() ? 
988           ", lockOwnerId=" + lockOwnerId +
989           ", lockOwnerName=" + lockOwnerName : "") +
990          ", lockedMonitors=" + Arrays.toString(lockedMonitors) +
991          ", lockedSynchronizers=" + Arrays.toString(lockedSynchronizers) +
992          "]";
993      }
994    
995      /**
996       * <p>
997       * Returns true if the thread is in a blocked state.
998       * The thread is regarded as blocked if:
999       * </p>
1000       * <ol>
1001       * <li>The thread is in the <code>BLOCKED</code> state
1002       * waiting to acquire an object monitor in order to enter
1003       * a synchronized method or block.</li>
1004       * <li>The thread is in the <code>WAITING</code> or
1005       * <code>TIMED_WAITING</code> state due to a call to
1006       * {@link java.lang.Object#wait()}.</li>
1007       * <li>The thread is in the <code>WAITING</code> or
1008       * <code>TIMED_WAITING</code> state due to a call
1009       * to {@link java.util.concurrent.locks.LockSupport#park()}.
1010       * The lock is the return value of
1011       * {@link java.util.concurrent.locks.LockSupport#getBlocker()}.</li>
1012       * </ol>
1013       *
1014       * @return true if the thread is blocked.
1015       */
1016      private boolean isThreadBlocked()
1017      {
1018        return (threadState == Thread.State.BLOCKED ||
1019                threadState == Thread.State.WAITING ||
1020                threadState == Thread.State.TIMED_WAITING);
1021      }
1022      
1023    }