Path: blob/master/jcl/src/java.management/share/classes/java/lang/management/ThreadInfo.java
12511 views
/*[INCLUDE-IF JAVA_SPEC_VERSION >= 8]*/1/*2*******************************************************************************3* Copyright (c) 2007, 2022 IBM Corp. and others4*5* This program and the accompanying materials are made available under6* the terms of the Eclipse Public License 2.0 which accompanies this7* distribution and is available at https://www.eclipse.org/legal/epl-2.0/8* or the Apache License, Version 2.0 which accompanies this distribution and9* is available at https://www.apache.org/licenses/LICENSE-2.0.10*11* This Source Code may also be made available under the following12* Secondary Licenses when the conditions for such availability set13* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU14* General Public License, version 2 with the GNU Classpath15* Exception [1] and GNU General Public License, version 2 with the16* OpenJDK Assembly Exception [2].17*18* [1] https://www.gnu.org/software/classpath/license.html19* [2] http://openjdk.java.net/legal/assembly-exception.html20*21* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception22*******************************************************************************/23package java.lang.management;2425import java.util.Arrays;26import java.util.StringTokenizer;2728import javax.management.openmbean.CompositeData;29import javax.management.openmbean.InvalidKeyException;3031import com.ibm.java.lang.management.internal.LockInfoUtil;32import com.ibm.java.lang.management.internal.ManagementAccessControl;33import com.ibm.java.lang.management.internal.MonitorInfoUtil;34import com.ibm.java.lang.management.internal.StackTraceElementUtil;35import com.ibm.java.lang.management.internal.ThreadInfoUtil;3637import openj9.management.internal.LockInfoBase;38import openj9.management.internal.MonitorInfoBase;39import openj9.management.internal.ThreadInfoBase;4041/**42* Information about a snapshot of the state of a thread.43*44* @since 1.545*/46public class ThreadInfo {4748private static final MonitorInfo[] EMPTY_MONITORINFO_ARRAY = new MonitorInfo[0];4950/* Set up access to ThreadInfo shared secret.*/51static {52ManagementAccessControl.setThreadInfoAccess(new ThreadInfoAccessImpl());53}5455/**56* Container for the actual data.57*/58private final ThreadInfoBase baseInfo;59private static final LockInfo[] EMPTY_LOCKINFO_ARRAY = new LockInfo[0];606162/**63* Used by from().64* @param threadIdVal65* @param threadNameVal66* @param threadStateVal67* @param suspendedVal68* @param inNativeVal69* @param blockedCountVal70* @param blockedTimeVal71* @param waitedCountVal72* @param waitedTimeVal73* @param lockNameVal74* @param lockOwnerIdVal75* @param lockOwnerNameVal76* @param stackTraceVal77* @param lockInfo78* @param lockedMonitors79* @param lockedSynchronizers80/*[IF Sidecar19-SE]81* @param daemon82* @param priority83/*[ENDIF]84*/85private ThreadInfo(long threadIdVal, String threadNameVal,86Thread.State threadStateVal, boolean suspendedVal,87boolean inNativeVal, long blockedCountVal, long blockedTimeVal,88long waitedCountVal, long waitedTimeVal, String lockNameVal,89long lockOwnerIdVal, String lockOwnerNameVal,90StackTraceElement[] stackTraceVal, LockInfo lockInfo,91MonitorInfo[] lockedMonitors, LockInfo[] lockedSynchronizers92/*[IF Sidecar19-SE]*/93, boolean daemon, int priority94/*[ENDIF]*/95) {96MonitorInfoBase[] monInfoBases = null;97if (null != lockedMonitors) {98monInfoBases = Arrays.stream(lockedMonitors)99.map(MonitorInfo::getBaseInfo).toArray(MonitorInfoBase[]::new);100}101102LockInfoBase[] lockedSyncs = null;103if (null != lockedSynchronizers) {104lockedSyncs = Arrays.stream(lockedSynchronizers)105.map(LockInfo::getBaseInfo).toArray(LockInfoBase[]::new);106}107LockInfoBase lckInfo = null;108if (null != lockInfo) {109lckInfo = lockInfo.getBaseInfo();110}111baseInfo = new ThreadInfoBase(threadIdVal, threadNameVal, threadStateVal, suspendedVal, inNativeVal,112blockedCountVal, blockedTimeVal, waitedCountVal, waitedTimeVal, lockNameVal, lockOwnerIdVal,113lockOwnerNameVal, stackTraceVal, lckInfo, monInfoBases, lockedSyncs114/*[IF Sidecar19-SE]*/115, daemon, priority116/*[ENDIF]*/117);118}119120/**121* Constructor for use via reflection from ThreadMXBeanImpl.122* @param base data for the object123*/124private ThreadInfo(ThreadInfoBase base) {125baseInfo = base;126}127128/**129* Returns the number of times that the thread represented by this130* <code>ThreadInfo</code> has been blocked on any monitor objects. The131* count is from the start of the thread's life.132*133* @return the number of times the corresponding thread has been blocked on134* a monitor.135*/136public long getBlockedCount() {137return baseInfo.getBlockedCount();138}139140/**141* If thread contention monitoring is supported and enabled, returns the142* total amount of time that the thread represented by this143* <code>ThreadInfo</code> has spent blocked on any monitor objects. The144* time is measured in milliseconds and will be measured over the time period145* since thread contention was most recently enabled.146*147* @return if thread contention monitoring is currently enabled, the number148* of milliseconds that the thread associated with this149* <code>ThreadInfo</code> has spent blocked on any monitors. If150* thread contention monitoring is supported but currently disabled,151* <code>-1</code>.152* @throws UnsupportedOperationException153* if the virtual machine does not support thread contention154* monitoring.155* @see ThreadMXBean#isThreadContentionMonitoringSupported()156* @see ThreadMXBean#isThreadContentionMonitoringEnabled()157*/158public long getBlockedTime() {159return baseInfo.getBlockedTime();160}161162/**163* If the thread represented by this <code>ThreadInfo</code> is currently164* blocked on or waiting on a monitor object, returns a string165* representation of that monitor object.166* <p>167* The monitor's string representation is comprised of the following168* component parts:169* <ul>170* <li><code>monitor</code> class name171* <li><code>@</code>172* <li><code>Integer.toHexString(System.identityHashCode(monitor))</code>173* </ul>174* @return if blocked or waiting on a monitor, a string representation of175* the monitor object. Otherwise, <code>null</code>.176* @see Integer#toHexString(int)177* @see System#identityHashCode(java.lang.Object)178*/179public String getLockName() {180return baseInfo.getLockName();181}182183/**184* If the thread represented by this <code>ThreadInfo</code> is currently185* blocked on or waiting on a monitor object, returns the thread identifier186* of the thread which owns the monitor.187*188* @return the thread identifier of the other thread which holds the monitor189* that the thread associated with this <code>ThreadInfo</code> is190* blocked or waiting on. If this <code>ThreadInfo</code>'s191* associated thread is currently not blocked or waiting, or there192* is no other thread holding the monitor, returns a <code>-1</code>.193*/194public long getLockOwnerId() {195return baseInfo.getLockOwnerId();196}197198/**199* If the thread represented by this <code>ThreadInfo</code> is currently200* blocked on or waiting on a monitor object, returns the name of the thread201* which owns the monitor.202*203* @return the name of the other thread which holds the monitor that the204* thread associated with this <code>ThreadInfo</code> is blocked205* or waiting on. If this <code>ThreadInfo</code>'s associated206* thread is currently not blocked or waiting, or there is no other207* thread holding the monitor, returns a <code>null</code>208* reference.209*/210public String getLockOwnerName() {211return baseInfo.getLockOwnerName();212}213214/**215* If the thread corresponding to this <code>ThreadInfo</code> is blocked216* then this method returns a {@link LockInfo} object that contains details217* of the associated lock object.218*219* @return a <code>LockInfo</code> object if this <code>ThreadInfo</code>'s220* thread is currently blocked, else <code>null</code>.221*/222public LockInfo getLockInfo() {223LockInfoBase li = baseInfo.getBlockingLockInfo();224return (null == li) ? null : new LockInfo(li);225}226227/**228* Returns the native thread identifier of the thread represented by this229* <code>ThreadInfo</code>.230*231* @return the native identifier of the thread corresponding to this232* <code>ThreadInfo</code>.233*/234long getNativeThreadId() {235return baseInfo.getNativeTId();236}237238/**239* If available, returns the stack trace for the thread represented by this240* <code>ThreadInfo</code> instance. The stack trace is returned in an241* array of {@link StackTraceElement} objects with the "top" of the242* stack encapsulated in the first array element and the "bottom"243* of the stack in the last array element.244* <p>245* If this <code>ThreadInfo</code> was created without any stack trace246* information (e.g. by a call to {@link ThreadMXBean#getThreadInfo(long)})247* then the returned array will have a length of zero.248* </p>249*250* @return the stack trace for the thread represented by this251* <code>ThreadInfo</code>.252*/253public StackTraceElement[] getStackTrace() {254return baseInfo.getStackTrace().clone();255}256257/**258* Returns the thread identifier of the thread represented by this259* <code>ThreadInfo</code>.260*261* @return the identifier of the thread corresponding to this262* <code>ThreadInfo</code>.263*/264public long getThreadId() {265return baseInfo.getThreadId();266}267268/**269* Returns the name of the thread represented by this270* <code>ThreadInfo</code>.271*272* @return the name of the thread corresponding to this273* <code>ThreadInfo</code>.274*/275public String getThreadName() {276return baseInfo.getThreadName();277}278279/**280* Returns the thread state value of the thread represented by this281* <code>ThreadInfo</code>.282*283* @return the thread state of the thread corresponding to this284* <code>ThreadInfo</code>.285* @see Thread#getState()286*/287public Thread.State getThreadState() {288return baseInfo.getThreadState();289}290291/**292* The number of times that the thread represented by this293* <code>ThreadInfo</code> has gone to the "wait" or "timed294* wait" state.295*296* @return the number of times the corresponding thread has been in the297* "wait" or "timed wait" state.298*/299public long getWaitedCount() {300return baseInfo.getWaitedCount();301}302303/**304* If thread contention monitoring is supported and enabled, returns the305* total amount of time that the thread represented by this306* <code>ThreadInfo</code> has spent waiting for notifications. The time307* is measured in milliseconds and will be measured over the time period308* since thread contention was most recently enabled.309*310* @return if thread contention monitoring is currently enabled, the number311* of milliseconds that the thread associated with this312* <code>ThreadInfo</code> has spent waiting notifications. If313* thread contention monitoring is supported but currently disabled,314* <code>-1</code>.315* @throws UnsupportedOperationException316* if the virtual machine does not support thread contention317* monitoring.318* @see ThreadMXBean#isThreadContentionMonitoringSupported()319* @see ThreadMXBean#isThreadContentionMonitoringEnabled()320*/321public long getWaitedTime() {322return baseInfo.getWaitedTime();323}324325/**326* Returns a <code>boolean</code> indication of whether or not the thread327* represented by this <code>ThreadInfo</code> is currently in a native328* method.329*330* @return if the corresponding thread <i>is </i> executing a native method331* then <code>true</code>, otherwise <code>false</code>.332*/333public boolean isInNative() {334return baseInfo.isInNative();335}336337/**338* Returns a <code>boolean</code> indication of whether or not the thread339* represented by this <code>ThreadInfo</code> is currently suspended.340*341* @return if the corresponding thread <i>is </i> suspended then342* <code>true</code>, otherwise <code>false</code>.343*/344public boolean isSuspended() {345return baseInfo.isSuspended();346}347348/**349* Returns an array of <code>MonitorInfo</code> objects, one for every350* monitor object locked by the <code>Thread</code> corresponding to this351* <code>ThreadInfo</code> when it was instantiated.352*353* @return an array whose elements comprise of <code>MonitorInfo</code>354* objects - one for each object monitor locked by this355* <code>ThreadInfo</code> object's corresponding thread. If no356* monitors are locked by the thread then the array will have a357* length of zero.358*/359public MonitorInfo[] getLockedMonitors() {360MonitorInfo lockedMons[] = null;361MonitorInfoBase[] lockedMonBases = baseInfo.getLockedMonitors();362if (null != lockedMonBases) {363lockedMons = Arrays.stream(lockedMonBases)364.map(MonitorInfo::new).toArray(MonitorInfo[]::new);365}366return lockedMons;367}368369/**370* Returns an array of <code>LockInfo</code> objects, each one containing371* information on an ownable synchronizer (a synchronizer that makes use of372* the <code>AbstractOwnableSynchronizer</code> type and which is373* completely owned by a single thread) locked by the <code>Thread</code>374* corresponding to this <code>ThreadInfo</code> when it was instantiated.375*376* @return an array whose elements comprise of <code>LockInfo</code>377* objects - one for each ownable synchronizer locked by this378* <code>ThreadInfo</code> object's corresponding thread. If no379* ownable synchronizer are locked by the thread then the array will380* have a length of zero.381*/382public LockInfo[] getLockedSynchronizers() {383LockInfo lockedSyncs[] = null;384LockInfoBase[] lockedSyncBases = baseInfo.getLockedSynchronizers();385lockedSyncs = Arrays.stream(lockedSyncBases)386.map(LockInfo::new).toArray(LockInfo[]::new);387return lockedSyncs;388}389390/**391* Receives a {@link CompositeData} representing a <code>ThreadInfo</code>392* object and attempts to return the root <code>ThreadInfo</code>393* instance.394*395* @param cd396* a <code>CompositeData</code> that represents a397* <code>ThreadInfo</code>.398* @return if <code>cd</code> is non- <code>null</code>, returns a new399* instance of <code>ThreadInfo</code>. If <code>cd</code> is400* <code>null</code>, returns <code>null</code>.401* @throws IllegalArgumentException402* if argument <code>cd</code> does not correspond to a403* <code>ThreadInfo</code> with the following attributes:404* <ul>405* <li><code>threadId</code>(<code>java.lang.Long</code>)406* <li><code>threadName</code>(407* <code>java.lang.String</code>)408* <li><code>threadState</code>(409* <code>java.lang.String</code>)410* <li><code>suspended</code>(411* <code>java.lang.Boolean</code>)412* <li><code>inNative</code>(<code>java.lang.Boolean</code>)413* <li><code>blockedCount</code>(414* <code>java.lang.Long</code>)415* <li><code>blockedTime</code>(<code>java.lang.Long</code>)416* <li><code>waitedCount</code>(<code>java.lang.Long</code>)417* <li><code>waitedTime</code> (<code>java.lang.Long</code>)418/*[IF Sidecar19-SE]419* <li><code>daemon</code> (<code>java.lang.Boolean</code>)420* <li><code>priority</code> (<code>java.lang.Integer</code>)421/*[ENDIF]422* <li><code>lockInfo</code> (<code>javax.management.openmbean.CompositeData</code>)423* which holds the simple attributes <code>className</code>(<code>java.lang.String</code>),424* <code>identityHashCode</code>(<code>java.lang.Integer</code>).425* In the event that the input <code>CompositeData</code> does not hold a426* <code>lockInfo</code> attribute, the value of the <code>lockName</code>427* attribute is used for setting the returned object's428* <code>LockInfo</code> state.429* <li><code>lockName</code> (<code>java.lang.String</code>)430* <li><code>lockOwnerId</code> (<code>java.lang.Long</code>)431* <li><code>lockOwnerName</code> (<code>java.lang.String</code>)432* <li><code>stackTrace</code> (<code>javax.management.openmbean.CompositeData[]</code>)433* </ul>434* Each element of the <code>stackTrace</code> array must435* correspond to a <code>java.lang.StackTraceElement</code>436* and have the following attributes :437* <ul>438/*[IF Sidecar19-SE]439* <li><code>moduleName</code>(<code>java.lang.String</code>)440* <li><code>moduleVersion</code>(<code>java.lang.String</code>)441/*[ENDIF]442* <li><code>className</code> (<code>java.lang.String</code>)443* <li><code>methodName</code> (<code>java.lang.String</code>)444* <li><code>fileName</code> (<code>java.lang.String</code>)445* <li><code>lineNumber</code> (<code>java.lang.Integer</code>)446* <li><code>nativeMethod</code> (<code>java.lang.Boolean</code>)447* </ul>448*/449public static ThreadInfo from(CompositeData cd) {450ThreadInfo result = null;451452if (cd != null) {453// Is the received CompositeData of the required type to create454// a new ThreadInfo ?455if (!ThreadInfoUtil.getCompositeType().isValue(cd)) {456/*[MSG "K05E5", "CompositeData is not of the expected type."]*/457throw new IllegalArgumentException(com.ibm.oti.util.Msg.getString("K05E5")); //$NON-NLS-1$458}459460long threadIdVal;461String threadNameVal;462String threadStateStringVal;463Thread.State threadStateVal;464boolean suspendedVal;465boolean inNativeVal;466long blockedCountVal;467long blockedTimeVal;468long waitedCountVal;469long waitedTimeVal;470String lockNameVal;471long lockOwnerIdVal;472String lockOwnerNameVal;473StackTraceElement[] stackTraceVals;474LockInfo lockInfoVal;475MonitorInfo[] lockedMonitorVals;476LockInfo[] lockedSynchronizerVals;477478/*[IF Sidecar19-SE]*/479boolean daemonVal;480int priorityVal;481/*[ENDIF]*/482483try {484// Set the mandatory attributes - if any of these are485// missing we need to throw a IllegalArgumentException486threadIdVal = ((Long) cd.get("threadId")).longValue(); //$NON-NLS-1$487threadNameVal = (String) cd.get("threadName"); //$NON-NLS-1$488threadStateStringVal = (String) cd.get("threadState"); //$NON-NLS-1$489490// Verify that threadStateStringVal contains a string that can491// be successfully used to create a Thread.State.492try {493threadStateVal = Thread.State.valueOf(threadStateStringVal);494} catch (IllegalArgumentException e) {495/*[MSG "K0612", "CompositeData contains an unexpected threadState value."]*/496throw new IllegalArgumentException(com.ibm.oti.util.Msg.getString("K0612", e)); //$NON-NLS-1$497}498499suspendedVal = ((Boolean) cd.get("suspended")).booleanValue(); //$NON-NLS-1$500inNativeVal = ((Boolean) cd.get("inNative")).booleanValue(); //$NON-NLS-1$501blockedCountVal = ((Long) cd.get("blockedCount")).longValue(); //$NON-NLS-1$502blockedTimeVal = ((Long) cd.get("blockedTime")).longValue(); //$NON-NLS-1$503waitedCountVal = ((Long) cd.get("waitedCount")).longValue(); //$NON-NLS-1$504waitedTimeVal = ((Long) cd.get("waitedTime")).longValue(); //$NON-NLS-1$505lockNameVal = (String) cd.get("lockName"); //$NON-NLS-1$506lockOwnerIdVal = ((Long) cd.get("lockOwnerId")).longValue(); //$NON-NLS-1$507lockOwnerNameVal = (String) cd.get("lockOwnerName"); //$NON-NLS-1$508CompositeData[] stackTraceDataVal = (CompositeData[]) cd.get("stackTrace"); //$NON-NLS-1$509stackTraceVals = StackTraceElementUtil.fromArray(stackTraceDataVal);510511/*[IF Sidecar19-SE]*/512daemonVal = ((Boolean) cd.get("daemon")).booleanValue(); //$NON-NLS-1$513priorityVal = ((Integer) cd.get("priority")).intValue(); //$NON-NLS-1$514/*[ENDIF]*/515} catch (NullPointerException | InvalidKeyException e) {516// throw an IllegalArgumentException as the CompositeData517// object does not contain an expected key518/*[MSG "K05E6", "CompositeData object does not contain expected key."]*/519throw new IllegalArgumentException(com.ibm.oti.util.Msg.getString("K05E6")); //$NON-NLS-1$520}521522// Attempt to set the optional attributes - if any of these523// are missing we need to use the recommended default values.524lockInfoVal = recoverLockInfoAttribute(cd, lockNameVal);525lockedMonitorVals = recoverLockedMonitors(cd);526lockedSynchronizerVals = recoverLockedSynchronizers(cd);527528// Finally, try and create the resulting ThreadInfo529result = new ThreadInfo(threadIdVal, threadNameVal, threadStateVal,530suspendedVal, inNativeVal, blockedCountVal, blockedTimeVal,531waitedCountVal, waitedTimeVal, lockNameVal, lockOwnerIdVal,532lockOwnerNameVal, stackTraceVals, lockInfoVal,533lockedMonitorVals, lockedSynchronizerVals534/*[IF Sidecar19-SE]*/535, daemonVal, priorityVal536/*[ENDIF]*/537);538}539540return result;541}542543/**544* Helper method to retrieve an array of <code>LockInfo</code> from the545* supplied <code>CompositeData</code> object.546*547* @param cd548* a <code>CompositeData</code> that is expected to map to a549* <code>ThreadInfo</code> object550* @return an array of <code>LockInfo</code>551*/552private static LockInfo[] recoverLockedSynchronizers(CompositeData cd) {553LockInfo[] result;554try {555CompositeData[] lockedSynchronizersDataVal = (CompositeData[]) cd.get("lockedSynchronizers"); //$NON-NLS-1$556result = LockInfoUtil.fromArray(lockedSynchronizersDataVal);557} catch (InvalidKeyException e) {558result = EMPTY_LOCKINFO_ARRAY;559}560return result;561}562563/**564* Helper method to retrieve an array of <code>MonitorInfo</code> from the565* supplied <code>CompositeData</code> object.566*567* @param cd568* a <code>CompositeData</code> that is expected to map to a569* <code>ThreadInfo</code> object570* @return an array of <code>MonitorInfo</code>571*/572private static MonitorInfo[] recoverLockedMonitors(CompositeData cd) {573MonitorInfo[] result;574try {575CompositeData[] lockedMonitorsDataVal = (CompositeData[]) cd.get("lockedMonitors"); //$NON-NLS-1$576result = MonitorInfoUtil.fromArray(lockedMonitorsDataVal);577} catch (InvalidKeyException e) {578result = EMPTY_MONITORINFO_ARRAY;579}580return result;581}582583/**584* Helper method that retrieves a <code>LockInfo</code> value from the585* supplied <code>CompositeData</code> that is expected to map to a586* <code>ThreadInfo</code> instance.587*588* @param cd589* a <code>CompositeData</code> that maps to a590* <code>ThreadInfo</code>591* @param lockNameVal592* the name of a lock in the format specified by the593* {@link #getLockName()} method. If the input <code>cd</code>594* does not contain a "lockInfo" value this parameter595* will be used to construct the return value596* @return a new <code>LockInfo</code> either constructed using data597* recovered from the <code>cd</code> input or, if that fails,598* from the <code>lockNameVal</code> parameter599*/600private static LockInfo recoverLockInfoAttribute(CompositeData cd, String lockNameVal) {601LockInfo result = null;602try {603CompositeData lockInfoCDVal = cd.get("lockInfo") != null //$NON-NLS-1$604? (CompositeData) cd.get("lockInfo") //$NON-NLS-1$605: null;606if (lockInfoCDVal != null) {607// Is the received CompositeData of the required type to create608// a new ThreadInfo ?609if (!LockInfoUtil.getCompositeType().isValue(lockInfoCDVal)) {610/*[MSG "K05E5", "CompositeData is not of the expected type."]*/611throw new IllegalArgumentException(com.ibm.oti.util.Msg.getString("K05E5")); //$NON-NLS-1$612}613result = new LockInfo(614((String) lockInfoCDVal.get("className")), //$NON-NLS-1$615((Integer) lockInfoCDVal.get("identityHashCode")).intValue()); //$NON-NLS-1$616}617} catch (InvalidKeyException e) {618// Create a default LockInfo using the information from619// the lockName attribute which should have been recovered620// above621result = createLockInfoFromLockName(lockNameVal);622}623return result;624}625626/*[IF Sidecar19-SE]*/627628/**629* Returns a <code>boolean</code> indication of whether or not the thread630* represented by this <code>ThreadInfo</code> is currently a daemon thread.631*632* @return <code>true</code> if this thread is a daemon thread, otherwise <code>false</code> .633*/634public boolean isDaemon() {635return baseInfo.isDaemon();636}637638/**639* Returns the thread priority of the thread represented by this <code>ThreadInfo</code>.640*641* @return The priority of the thread represented by this <code>ThreadInfo</code>.642*/643public int getPriority() {644return baseInfo.getPriority();645}646647/*[ENDIF]*/648649/**650* @return a text description of this thread.651*/652@Override653public String toString() {654return baseInfo.toString();655}656657/**658* {@inheritDoc}659*/660@Override661public boolean equals(Object obj) {662boolean result = (this == obj);663if (!result && (null != obj) && (obj instanceof ThreadInfo)) {664result = baseInfo.equals(((ThreadInfo) obj).baseInfo);665}666return result;667}668669/**670* {@inheritDoc}671*/672@Override673public int hashCode() {674return baseInfo.hashCode();675}676677/**678* Convenience method that attempts to create a new <code>LockInfo</code>679* from the supplied string which is expected to be a valid representation680* of a lock as described in {@link LockInfo#toString()}.681*682* @param lockString683* string value representing a lock684* @return if possible, a new <code>LockInfo</code> object initialized685* from the data supplied in <code>lockString</code>. If686* <code>lockString</code> is <code>null</code> or else is not687* in the expected format then this method will return688* <code>null</code>.689*/690private static LockInfo createLockInfoFromLockName(String lockString) {691LockInfo result = null;692693if (lockString != null && lockString.length() > 0) {694// Expected format is :695// <f.q. class name of lock>@<lock identity hash code as hex string>696StringTokenizer strTok = new StringTokenizer(lockString, "@"); //$NON-NLS-1$697if (strTok.countTokens() == 2) {698try {699result = new LockInfo(strTok.nextToken(), Integer.parseInt(strTok.nextToken(), 16));700} catch (NumberFormatException e) {701// ignore and move on - the lockString is not in the correct format702}703}704}705return result;706}707708}709710711