Frames | No Frames |
1: /* ThreadInfo.java - Information on a thread 2: Copyright (C) 2006 Free Software Foundation 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: package java.lang.management; 39: 40: import javax.management.openmbean.ArrayType; 41: import javax.management.openmbean.CompositeData; 42: import javax.management.openmbean.CompositeType; 43: import javax.management.openmbean.OpenDataException; 44: import javax.management.openmbean.OpenType; 45: import javax.management.openmbean.SimpleType; 46: 47: /** 48: * <p> 49: * A class which maintains information about a particular 50: * thread. This information includes: 51: * </p> 52: * <ul> 53: * <li><strong>General Thread Information:</strong> 54: * <ul> 55: * <li>The identifier of the thread.</li> 56: * <li>The name of the thread.</li> 57: * </ul> 58: * </li> 59: * <li><strong>Execution Information:</strong> 60: * <ul> 61: * <li>The current state of the thread (e.g. blocked, runnable)</li> 62: * <li>The object upon which the thread is blocked, either because 63: * the thread is waiting to obtain the monitor of that object to enter 64: * one of its synchronized monitor, or because 65: * {@link java.lang.Object#wait()} has been called while the thread 66: * was within a method of that object.</li> 67: * <li>The thread identifier of the current thread holding an object's 68: * monitor, upon which the thread described here is blocked.</li> 69: * <li>The stack trace of the thread (if requested on creation 70: * of this object</li> 71: * </ul> 72: * <li><strong>Synchronization Statistics</strong> 73: * <ul> 74: * <li>The number of times the thread has been blocked waiting for 75: * an object's monitor or in a {@link java.lang.Object#wait()} call.</li> 76: * <li>The accumulated time the thread has been blocked waiting for 77: * an object's monitor on in a {@link java.lang.Object#wait()} call. 78: * The availability of these statistics depends on the virtual machine's 79: * support for thread contention monitoring (see 80: * {@link ThreadMXBean#isThreadContentionMonitoringSupported()}.</li> 81: * </ul> 82: * </li> 83: * </ul> 84: * 85: * @author Andrew John Hughes (gnu_andrew@member.fsf.org) 86: * @since 1.5 87: * @see ThreadMXBean#isThreadContentionMonitoringSupported() 88: */ 89: public class ThreadInfo 90: { 91: 92: /** 93: * The id of the thread which this instance concerns. 94: */ 95: private long threadId; 96: 97: /** 98: * The name of the thread which this instance concerns. 99: */ 100: private String threadName; 101: 102: /** 103: * The state of the thread which this instance concerns. 104: */ 105: private String threadState; 106: 107: /** 108: * The number of times the thread has been blocked. 109: */ 110: private long blockedCount; 111: 112: /** 113: * The accumulated number of milliseconds the thread has 114: * been blocked (used only with thread contention monitoring 115: * support). 116: */ 117: private long blockedTime; 118: 119: /** 120: * The name of the monitor lock on which this thread 121: * is blocked (if any). 122: */ 123: private String lockName; 124: 125: /** 126: * The id of the thread which owns the monitor lock on 127: * which this thread is blocked, or <code>-1</code> 128: * if there is no owner. 129: */ 130: private long lockOwnerId; 131: 132: /** 133: * The name of the thread which owns the monitor lock on 134: * which this thread is blocked, or <code>null</code> 135: * if there is no owner. 136: */ 137: private String lockOwnerName; 138: 139: /** 140: * The number of times the thread has been in a waiting 141: * state. 142: */ 143: private long waitedCount; 144: 145: /** 146: * The accumulated number of milliseconds the thread has 147: * been waiting (used only with thread contention monitoring 148: * support). 149: */ 150: private long waitedTime; 151: 152: /** 153: * True if the thread is in a native method. 154: */ 155: private boolean isInNative; 156: 157: /** 158: * True if the thread is suspended. 159: */ 160: private boolean isSuspended; 161: 162: /** 163: * The stack trace of the thread. 164: */ 165: private StackTraceElement[] trace; 166: 167: /** 168: * Cache a local reference to the thread management bean. 169: */ 170: private static ThreadMXBean bean = null; 171: 172: /** 173: * Constructs a new {@link ThreadInfo} corresponding 174: * to the thread specified. 175: * 176: * @param thread the thread on which the new instance 177: * will be based. 178: * @param blockedCount the number of times the thread 179: * has been blocked. 180: * @param blockedTime the accumulated number of milliseconds 181: * the specified thread has been blocked 182: * (only used with contention monitoring enabled) 183: * @param lock the monitor lock the thread is waiting for 184: * (only used if blocked) 185: * @param lockOwner the thread which owns the monitor lock, or 186: * <code>null</code> if it doesn't have an owner 187: * (only used if blocked) 188: * @param waitedCount the number of times the thread has been in a 189: * waiting state. 190: * @param waitedTime the accumulated number of milliseconds the 191: * specified thread has been waiting 192: * (only used with contention monitoring enabled) 193: * @param isInNative true if the thread is in a native method. 194: * @param isSuspended true if the thread is suspended. 195: * @param trace the stack trace of the thread to a pre-determined 196: * depth (see VMThreadMXBeanImpl) 197: */ 198: private ThreadInfo(Thread thread, long blockedCount, long blockedTime, 199: Object lock, Thread lockOwner, long waitedCount, 200: long waitedTime, boolean isInNative, boolean isSuspended, 201: StackTraceElement[] trace) 202: { 203: this(thread.getId(), thread.getName(), thread.getState(), blockedCount, 204: blockedTime, lock.getClass().getName() + "@" + 205: Integer.toHexString(System.identityHashCode(lock)), lockOwner.getId(), 206: lockOwner.getName(), waitedCount, waitedTime, isInNative, isSuspended, 207: trace); 208: } 209: 210: /** 211: * Constructs a new {@link ThreadInfo} corresponding 212: * to the thread details specified. 213: * 214: * @param threadId the id of the thread on which this 215: * new instance will be based. 216: * @param threadName the name of the thread on which 217: * this new instance will be based. 218: * @param threadState the state of the thread on which 219: * this new instance will be based. 220: * @param blockedCount the number of times the thread 221: * has been blocked. 222: * @param blockedTime the accumulated number of milliseconds 223: * the specified thread has been blocked 224: * (only used with contention monitoring enabled) 225: * @param lockName the name of the monitor lock the thread is waiting for 226: * (only used if blocked) 227: * @param lockOwnerId the id of the thread which owns the monitor 228: * lock, or <code>-1</code> if it doesn't have an owner 229: * (only used if blocked) 230: * @param lockOwnerName the name of the thread which owns the monitor 231: * lock, or <code>null</code> if it doesn't have an 232: * owner (only used if blocked) 233: * @param waitedCount the number of times the thread has been in a 234: * waiting state. 235: * @param waitedTime the accumulated number of milliseconds the 236: * specified thread has been waiting 237: * (only used with contention monitoring enabled) 238: * @param isInNative true if the thread is in a native method. 239: * @param isSuspended true if the thread is suspended. 240: * @param trace the stack trace of the thread to a pre-determined 241: * depth (see VMThreadMXBeanImpl) 242: */ 243: private ThreadInfo(long threadId, String threadName, String threadState, 244: long blockedCount, long blockedTime, String lockName, 245: long lockOwnerId, String lockOwnerName, long waitedCount, 246: long waitedTime, boolean isInNative, boolean isSuspended, 247: StackTraceElement[] trace) 248: { 249: this.threadId = threadId; 250: this.threadName = threadName; 251: this.threadState = threadState; 252: this.blockedCount = blockedCount; 253: this.blockedTime = blockedTime; 254: this.lockName = lockName; 255: this.lockOwnerId = lockOwnerId; 256: this.lockOwnerName = lockOwnerName; 257: this.waitedCount = waitedCount; 258: this.waitedTime = waitedTime; 259: this.isInNative = isInNative; 260: this.isSuspended = isSuspended; 261: this.trace = trace; 262: } 263: 264: /** 265: * Checks for an attribute in a {@link CompositeData} structure 266: * with the correct type. 267: * 268: * @param ctype the composite data type to check. 269: * @param name the name of the attribute. 270: * @param type the type to check for. 271: * @throws IllegalArgumentException if the attribute is absent 272: * or of the wrong type. 273: */ 274: static void checkAttribute(CompositeType ctype, String name, 275: OpenType type) 276: throws IllegalArgumentException 277: { 278: OpenType foundType = ctype.getType(name); 279: if (foundType == null) 280: throw new IllegalArgumentException("Could not find a field named " + 281: name); 282: if (!(foundType.equals(type))) 283: throw new IllegalArgumentException("Field " + name + " is not of " + 284: "type " + type.getClassName()); 285: } 286: 287: /** 288: * <p> 289: * Returns a {@link ThreadInfo} instance using the values 290: * given in the supplied 291: * {@link javax.management.openmbean.CompositeData} object. 292: * The composite data instance should contain the following 293: * attributes with the specified types: 294: * </p> 295: * <table> 296: * <th><td>Name</td><td>Type</td></th> 297: * <tr><td>threadId</td><td>java.lang.Long</td></tr> 298: * <tr><td>threadName</td><td>java.lang.String</td></tr> 299: * <tr><td>threadState</td><td>java.lang.String</td></tr> 300: * <tr><td>suspended</td><td>java.lang.Boolean</td></tr> 301: * <tr><td>inNative</td><td>java.lang.Boolean</td></tr> 302: * <tr><td>blockedCount</td><td>java.lang.Long</td></tr> 303: * <tr><td>blockedTime</td><td>java.lang.Long</td></tr> 304: * <tr><td>waitedCount</td><td>java.lang.Long</td></tr> 305: * <tr><td>waitedTime</td><td>java.lang.Long</td></tr> 306: * <tr><td>lockName</td><td>java.lang.String</td></tr> 307: * <tr><td>lockOwnerId</td><td>java.lang.Long</td></tr> 308: * <tr><td>lockOwnerName</td><td>java.lang.String</td></tr> 309: * <tr><td>stackTrace</td><td>javax.management.openmbean.CompositeData[] 310: * </td></tr> 311: * </table> 312: * <p> 313: * The stack trace is further described as: 314: * </p> 315: * <table> 316: * <th><td>Name</td><td>Type</td></th> 317: * <tr><td>className</td><td>java.lang.String</td></tr> 318: * <tr><td>methodName</td><td>java.lang.String</td></tr> 319: * <tr><td>fileName</td><td>java.lang.String</td></tr> 320: * <tr><td>lineNumber</td><td>java.lang.Integer</td></tr> 321: * <tr><td>nativeMethod</td><td>java.lang.Boolean</td></tr> 322: * </table> 323: * 324: * @param data the composite data structure to take values from. 325: * @return a new instance containing the values from the 326: * composite data structure, or <code>null</code> 327: * if the data structure was also <code>null</code>. 328: * @throws IllegalArgumentException if the composite data structure 329: * does not match the structure 330: * outlined above. 331: */ 332: public static ThreadInfo from(CompositeData data) 333: { 334: if (data == null) 335: return null; 336: CompositeType type = data.getCompositeType(); 337: checkAttribute(type, "threadId", SimpleType.LONG); 338: checkAttribute(type, "threadName", SimpleType.STRING); 339: checkAttribute(type, "threadState", SimpleType.STRING); 340: checkAttribute(type, "suspended", SimpleType.BOOLEAN); 341: checkAttribute(type, "inNative", SimpleType.BOOLEAN); 342: checkAttribute(type, "blockedCount", SimpleType.LONG); 343: checkAttribute(type, "blockedTime", SimpleType.LONG); 344: checkAttribute(type, "waitedCount", SimpleType.LONG); 345: checkAttribute(type, "waitedTime", SimpleType.LONG); 346: checkAttribute(type, "lockName", SimpleType.STRING); 347: checkAttribute(type, "lockOwnerId", SimpleType.LONG); 348: checkAttribute(type, "lockOwnerName", SimpleType.STRING); 349: try 350: { 351: CompositeType seType = 352: new CompositeType(StackTraceElement.class.getName(), 353: "An element of a stack trace", 354: new String[] { "className", "methodName", 355: "fileName", "lineNumber", 356: "nativeMethod" 357: }, 358: new String[] { "Name of the class", 359: "Name of the method", 360: "Name of the source code file", 361: "Line number", 362: "True if this is a native method" 363: }, 364: new OpenType[] { 365: SimpleType.STRING, SimpleType.STRING, 366: SimpleType.STRING, SimpleType.INTEGER, 367: SimpleType.BOOLEAN 368: }); 369: checkAttribute(type, "stackTrace", new ArrayType(1, seType)); 370: } 371: catch (OpenDataException e) 372: { 373: throw new IllegalStateException("Something went wrong in creating " + 374: "the composite data type for the " + 375: "stack trace element.", e); 376: } 377: CompositeData[] dTraces = (CompositeData[]) data.get("stackTrace"); 378: StackTraceElement[] traces = new StackTraceElement[dTraces.length]; 379: for (int a = 0; a < dTraces.length; ++a) 380: /* FIXME: We can't use the boolean as there is no available 381: constructor. */ 382: traces[a] = 383: new StackTraceElement((String) dTraces[a].get("className"), 384: (String) dTraces[a].get("methodName"), 385: (String) dTraces[a].get("fileName"), 386: ((Integer) 387: dTraces[a].get("lineNumber")).intValue()); 388: return new ThreadInfo(((Long) data.get("threadId")).longValue(), 389: (String) data.get("threadName"), 390: (String) data.get("threadState"), 391: ((Long) data.get("blockedCount")).longValue(), 392: ((Long) data.get("blockedTime")).longValue(), 393: (String) data.get("lockName"), 394: ((Long) data.get("lockOwnerId")).longValue(), 395: (String) data.get("lockOwnerName"), 396: ((Long) data.get("waitedCount")).longValue(), 397: ((Long) data.get("waitedTime")).longValue(), 398: ((Boolean) data.get("inNative")).booleanValue(), 399: ((Boolean) data.get("suspended")).booleanValue(), 400: traces); 401: } 402: 403: /** 404: * Returns the number of times this thread has been 405: * in the {@link java.lang.Thread.State#BLOCKED} state. 406: * A thread enters this state when it is waiting to 407: * obtain an object's monitor. This may occur either 408: * on entering a synchronized method for the first time, 409: * or on re-entering it following a call to 410: * {@link java.lang.Object#wait()}. 411: * 412: * @return the number of times this thread has been blocked. 413: */ 414: public long getBlockedCount() 415: { 416: return blockedCount; 417: } 418: 419: /** 420: * <p> 421: * Returns the accumulated number of milliseconds this 422: * thread has been in the 423: * {@link java.lang.Thread.State#BLOCKED} state 424: * since thread contention monitoring was last enabled. 425: * A thread enters this state when it is waiting to 426: * obtain an object's monitor. This may occur either 427: * on entering a synchronized method for the first time, 428: * or on re-entering it following a call to 429: * {@link java.lang.Object#wait()}. 430: * </p> 431: * <p> 432: * Use of this method requires virtual machine support 433: * for thread contention monitoring and for this support 434: * to be enabled. 435: * </p> 436: * 437: * @return the accumulated time (in milliseconds) that this 438: * thread has spent in the blocked state, since 439: * thread contention monitoring was enabled, or -1 440: * if thread contention monitoring is disabled. 441: * @throws UnsupportedOperationException if the virtual 442: * machine does not 443: * support contention 444: * monitoring. 445: * @see ThreadMXBean#isThreadContentionMonitoringEnabled() 446: * @see ThreadMXBean#isThreadContentionMonitoringSupported() 447: */ 448: public long getBlockedTime() 449: { 450: if (bean == null) 451: bean = ManagementFactory.getThreadMXBean(); 452: // Will throw UnsupportedOperationException for us 453: if (bean.isThreadContentionMonitoringEnabled()) 454: return blockedTime; 455: else 456: return -1; 457: } 458: 459: /** 460: * <p> 461: * Returns a {@link java.lang.String} representation of 462: * the monitor lock on which this thread is blocked. If 463: * the thread is not blocked, this method returns 464: * <code>null</code>. 465: * </p> 466: * <p> 467: * The returned {@link java.lang.String} is constructed 468: * using the class name and identity hashcode (usually 469: * the memory address of the object) of the lock. The 470: * two are separated by the '@' character, and the identity 471: * hashcode is represented in hexadecimal. Thus, for a 472: * lock, <code>l</code>, the returned value is 473: * the result of concatenating 474: * <code>l.getClass().getName()</code>, <code>"@"</code> 475: * and 476: * <code>Integer.toHexString(System.identityHashCode(l))</code>. 477: * The value is only unique to the extent that the identity 478: * hash code is also unique. 479: * </p> 480: * 481: * @return a string representing the lock on which this 482: * thread is blocked, or <code>null</code> if 483: * the thread is not blocked. 484: */ 485: public String getLockName() 486: { 487: if (threadState.equals("BLOCKED")) 488: return null; 489: return lockName; 490: } 491: 492: /** 493: * Returns the identifier of the thread which owns the 494: * monitor lock this thread is waiting for. -1 is returned 495: * if either this thread is not blocked, or the lock is 496: * not held by any other thread. 497: * 498: * @return the thread identifier of thread holding the lock 499: * this thread is waiting for, or -1 if the thread 500: * is not blocked or the lock is not held by another 501: * thread. 502: */ 503: public long getLockOwnerId() 504: { 505: if (threadState.equals("BLOCKED")) 506: return -1; 507: return lockOwnerId; 508: } 509: 510: /** 511: * Returns the name of the thread which owns the 512: * monitor lock this thread is waiting for. <code>null</code> 513: * is returned if either this thread is not blocked, 514: * or the lock is not held by any other thread. 515: * 516: * @return the thread identifier of thread holding the lock 517: * this thread is waiting for, or <code>null</code> 518: * if the thread is not blocked or the lock is not 519: * held by another thread. 520: */ 521: public String getLockOwnerName() 522: { 523: if (threadState.equals("BLOCKED")) 524: return null; 525: return lockOwnerName; 526: } 527: 528: /** 529: * <p> 530: * Returns the stack trace of this thread to the depth 531: * specified on creation of this {@link ThreadInfo} 532: * object. If the depth is zero, an empty array will 533: * be returned. For non-zero arrays, the elements 534: * start with the most recent trace at position zero. 535: * The bottom of the stack represents the oldest method 536: * invocation which meets the depth requirements. 537: * </p> 538: * <p> 539: * Some virtual machines may not be able to return 540: * stack trace information for a thread. In these 541: * cases, an empty array will also be returned. 542: * </p> 543: * 544: * @return an array of {@link java.lang.StackTraceElement}s 545: * representing the trace of this thread. 546: */ 547: public StackTraceElement[] getStackTrace() 548: { 549: return trace; 550: } 551: 552: /** 553: * Returns the identifier of the thread associated with 554: * this instance of {@link ThreadInfo}. 555: * 556: * @return the thread's identifier. 557: */ 558: public long getThreadId() 559: { 560: return threadId; 561: } 562: 563: /** 564: * Returns the name of the thread associated with 565: * this instance of {@link ThreadInfo}. 566: * 567: * @return the thread's name. 568: */ 569: public String getThreadName() 570: { 571: return threadName; 572: } 573: 574: /** 575: * Returns the state of the thread associated with 576: * this instance of {@link ThreadInfo}. 577: * 578: * @return the thread's state. 579: */ 580: public String getThreadState() 581: { 582: return threadState; 583: } 584: 585: /** 586: * Returns the number of times this thread has been 587: * in the {@link java.lang.Thread.State#WAITING} 588: * or {@link java.lang.Thread.State#TIMED_WAITING} state. 589: * A thread enters one of these states when it is waiting 590: * due to a call to {@link java.lang.Object.wait()}, 591: * {@link java.lang.Object.join()} or 592: * {@link java.lang.concurrent.locks.LockSupport.park()}, 593: * either with an infinite or timed delay, respectively. 594: * 595: * @return the number of times this thread has been waiting. 596: */ 597: public long getWaitedCount() 598: { 599: return waitedCount; 600: } 601: 602: /** 603: * <p> 604: * Returns the accumulated number of milliseconds this 605: * thread has been in the 606: * {@link java.lang.Thread.State#WAITING} or 607: * {@link java.lang.Thread.State#TIMED_WAITING} state, 608: * since thread contention monitoring was last enabled. 609: * A thread enters one of these states when it is waiting 610: * due to a call to {@link java.lang.Object.wait()}, 611: * {@link java.lang.Object.join()} or 612: * {@link java.lang.concurrent.locks.LockSupport.park()}, 613: * either with an infinite or timed delay, respectively. 614: * </p> 615: * <p> 616: * Use of this method requires virtual machine support 617: * for thread contention monitoring and for this support 618: * to be enabled. 619: * </p> 620: * 621: * @return the accumulated time (in milliseconds) that this 622: * thread has spent in one of the waiting states, since 623: * thread contention monitoring was enabled, or -1 624: * if thread contention monitoring is disabled. 625: * @throws UnsupportedOperationException if the virtual 626: * machine does not 627: * support contention 628: * monitoring. 629: * @see ThreadMXBean#isThreadContentionMonitoringEnabled() 630: * @see ThreadMXBean#isThreadContentionMonitoringSupported() 631: */ 632: public long getWaitedTime() 633: { 634: if (bean == null) 635: bean = ManagementFactory.getThreadMXBean(); 636: // Will throw UnsupportedOperationException for us 637: if (bean.isThreadContentionMonitoringEnabled()) 638: return waitedTime; 639: else 640: return -1; 641: } 642: 643: /** 644: * Returns true if the thread is in a native method. This 645: * excludes native code which forms part of the virtual 646: * machine itself, or which results from Just-In-Time 647: * compilation. 648: * 649: * @return true if the thread is in a native method, false 650: * otherwise. 651: */ 652: public boolean isInNative() 653: { 654: return isInNative; 655: } 656: 657: /** 658: * Returns true if the thread has been suspended using 659: * {@link java.lang.Thread#suspend()}. 660: * 661: * @return true if the thread is suspended, false otherwise. 662: */ 663: public boolean isSuspended() 664: { 665: return isSuspended; 666: } 667: 668: /** 669: * Returns a {@link java.lang.String} representation of 670: * this {@link ThreadInfo} object. This takes the form 671: * <code>java.lang.management.ThreadInfo[id=tid, name=n, 672: * state=s, blockedCount=bc, waitedCount=wc, isInNative=iin, 673: * isSuspended=is]</code>, where <code>tid</code> is 674: * the thread identifier, <code>n</code> is the 675: * thread name, <code>s</code> is the thread state, 676: * <code>bc</code> is the blocked state count, 677: * <code>wc</code> is the waiting state count and 678: * <code>iin</code> and <code>is</code> are boolean 679: * flags to indicate the thread is in native code or 680: * suspended respectively. If the thread is blocked, 681: * <code>lock=l, lockOwner=lo</code> is also included, 682: * where <code>l</code> is the lock waited for, and 683: * <code>lo</code> is the thread which owns the lock 684: * (or null if there is no owner). 685: * 686: * @return the string specified above. 687: */ 688: public String toString() 689: { 690: return getClass().getName() + 691: "[id=" + threadId + 692: ", name=" + threadName + 693: ", state=" + threadState + 694: ", blockedCount=" + blockedCount + 695: ", waitedCount=" + waitedCount + 696: ", isInNative=" + isInNative + 697: ", isSuspended=" + isSuspended + 698: (threadState.equals("BLOCKED") ? 699: ", lockOwnerId=" + lockOwnerId + 700: ", lockOwnerName=" + lockOwnerName : "") + 701: "]"; 702: } 703: 704: }