Frames | No Frames |
1: /* VMIdManager.java -- A reference/example implementation of a manager for 2: JDWP object/reference type IDs 3: 4: Copyright (C) 2005, 2006, 2007 Free Software Foundation 5: 6: This file is part of GNU Classpath. 7: 8: GNU Classpath is free software; you can redistribute it and/or modify 9: it under the terms of the GNU General Public License as published by 10: the Free Software Foundation; either version 2, or (at your option) 11: any later version. 12: 13: GNU Classpath is distributed in the hope that it will be useful, but 14: WITHOUT ANY WARRANTY; without even the implied warranty of 15: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16: General Public License for more details. 17: 18: You should have received a copy of the GNU General Public License 19: along with GNU Classpath; see the file COPYING. If not, write to the 20: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 21: 02110-1301 USA. 22: 23: Linking this library statically or dynamically with other modules is 24: making a combined work based on this library. Thus, the terms and 25: conditions of the GNU General Public License cover the whole 26: combination. 27: 28: As a special exception, the copyright holders of this library give you 29: permission to link this library with independent modules to produce an 30: executable, regardless of the license terms of these independent 31: modules, and to copy and distribute the resulting executable under 32: terms of your choice, provided that you also meet, for each linked 33: terms of your choice, provided that you also meet, for each linked 34: independent module, the terms and conditions of the license of that 35: module. An independent module is a module which is not derived from 36: or based on this library. If you modify this library, you may extend 37: this exception to your version of the library, but you are not 38: obligated to do so. If you do not wish to do so, delete this 39: exception statement from your version. */ 40: 41: 42: package gnu.classpath.jdwp; 43: 44: import gnu.classpath.jdwp.exception.InvalidClassException; 45: import gnu.classpath.jdwp.exception.InvalidObjectException; 46: import gnu.classpath.jdwp.id.*; 47: 48: import java.lang.ref.Reference; 49: import java.lang.ref.ReferenceQueue; 50: import java.lang.ref.SoftReference; 51: import java.nio.ByteBuffer; 52: import java.util.HashMap; 53: import java.util.Hashtable; 54: 55: /** 56: * This class manages objects and referencetypes that are reported 57: * to the debugger. All objects and referencetypes reported to the 58: * debugger should go through this manager. 59: * 60: * A brief summary of what an <code>IdManager</code> must provide: 61: * 62: * <code> 63: * public ObjectId getObjectId (Object theObject); 64: * public ObjectId get (long id); 65: * public ObjectId readObjectId (ByteBuffer bb); 66: * public ReferenceTypeId getReferenceTypeId (Class clazz); 67: * public ReferenceTypeId getReferenceType (long id); 68: * public ReferenceTypeId readReferenceTypeId (ByteBuffer bb); 69: * </code> 70: * 71: * See the javadoc on these methods later in this file for more 72: * information on these functions. 73: * 74: * <b>NOTE:</b> All IDs handled by the ID manager (all object and reference 75: * type IDs) are assumed to be of type <code>long</code>. 76: * 77: * <b>NOTE:</b> This class does not manage virtual machine-specific types, 78: * like methods, fields, and frames. These already have unique IDs within 79: * the virtual machine and do not need further abstraction here. 80: * 81: * @author Keith Seitz (keiths@redhat.com) 82: */ 83: public class VMIdManager 84: { 85: // This factory generates ids for objects and types that may 86: // be sent to a debugger. 87: private static class IdFactory 88: { 89: // ID of last object / referencetype 90: private static Object _idLock = new Object (); 91: private static Object _ridLock = new Object (); 92: private static long _lastId = 0; 93: private static long _lastRid = 0; 94: 95: // A list of all ID types 96: private static HashMap _idList = new HashMap (); 97: 98: // Initialize the id list with known types 99: static 100: { 101: // ObjectId and ArrayId are special cases. See newObjectId. 102: _idList.put (ClassLoaderId.typeClass, ClassLoaderId.class); 103: _idList.put (ClassObjectId.typeClass, ClassObjectId.class); 104: _idList.put (StringId.typeClass, StringId.class); 105: _idList.put (ThreadId.typeClass, ThreadId.class); 106: _idList.put (ThreadGroupId.typeClass, ThreadGroupId.class); 107: } 108: 109: /** 110: * Returns a new id for the given object 111: * 112: * @param obj SoftReference of the object for which an id is desired 113: * @returns a suitable object id 114: */ 115: public static ObjectId newObjectId (SoftReference obj) 116: { 117: ObjectId id = null; 118: Object object = obj.get (); 119: 120: // Special case: arrays 121: if (object.getClass ().isArray ()) 122: id = new ArrayId (); 123: else 124: { 125: // Loop through all classes until we hit baseclass 126: Class myClass; 127: for (myClass = object.getClass (); myClass != null; 128: myClass = myClass.getSuperclass ()) 129: { 130: Class clz = (Class) _idList.get (myClass); 131: if (clz != null) 132: { 133: try 134: { 135: id = (ObjectId) clz.newInstance (); 136: synchronized (_idLock) 137: { 138: id.setId (++_lastId); 139: } 140: id.setReference (obj); 141: return id; 142: } 143: catch (InstantiationException ie) 144: { 145: // This really should not happen 146: throw new RuntimeException ("cannot create new ID", ie); 147: } 148: catch (IllegalAccessException iae) 149: { 150: // This really should not happen 151: throw new RuntimeException ("illegal access of ID", iae); 152: } 153: } 154: } 155: 156: /* getSuperclass returned null and no matching ID type found. 157: So it must derive from Object. */ 158: id = new ObjectId (); 159: } 160: 161: synchronized (_idLock) 162: { 163: id.setId (++_lastId); 164: } 165: id.setReference (obj); 166: return id; 167: } 168: 169: /** 170: * Returns a new reference type id for the given class 171: * 172: * @param ref SoftReference to the desired type 173: * @returns a suitable reference type id or null when the 174: * reference is cleared. 175: */ 176: public static ReferenceTypeId newReferenceTypeId (SoftReference ref) 177: { 178: ReferenceTypeId id; 179: Class clazz = (Class) ref.get (); 180: if (clazz == null) 181: return null; 182: 183: if (clazz.isArray ()) 184: id = new ArrayReferenceTypeId (); 185: else if (clazz.isInterface ()) 186: id = new InterfaceReferenceTypeId (); 187: else 188: id = new ClassReferenceTypeId (); 189: id.setReference (ref); 190: synchronized (_ridLock) 191: { 192: id.setId (++_lastRid); 193: } 194: return id; 195: } 196: } 197: 198: /** 199: * This class is a SoftReferenceIdentity type that is used by 200: * the ID manager. 201: */ 202: class ReferenceKey extends SoftReference 203: { 204: // Hash code of referent 205: private int _hash; 206: 207: /** 208: * Constructs a new <code>ReferenceKey</code> object 209: * with the given referent. 210: * 211: * <p>This constructor should only be used for object lookups 212: * by the backend. 213: * 214: * @param referent the object to reference 215: */ 216: public ReferenceKey (Object referent) 217: { 218: super (referent); 219: _hash = referent.hashCode (); 220: } 221: 222: /** 223: * Constructs a new <code>ReferenceKey</code> object 224: * with the given referent and reference queue. 225: * 226: * <p>The JDWP back-end stores a <code>ReferenceKey</code> 227: * with its corresponding <code>JdwpId</code>. This constructor 228: * is used by the back-end when adding new IDs to be managed. 229: * 230: * @param referent the object to reference 231: * @param queue the queue to which to report garbage collections 232: */ 233: public ReferenceKey (Object referent, ReferenceQueue queue) 234: { 235: super (referent, queue); 236: _hash = referent.hashCode (); 237: } 238: 239: /** 240: * Returns the hash code of the referent. 241: * This seems hacky, but is required in order to use this class 242: * as a hash table key. 243: * 244: * @returns the hash code of the referent 245: */ 246: public int hashCode () 247: { 248: return _hash; 249: } 250: 251: /** 252: * Comparator for keys 253: * 254: * This method can be used in two ways: 255: * 256: * <ol> 257: * <li>For table lookups, where we want to compare referents</li> 258: * <li>For clearing GCd objects, where we want to compare the actual 259: * key object (not the referent)</li> 260: * </ol> 261: */ 262: public boolean equals (Object obj) 263: { 264: if (obj instanceof ReferenceKey) 265: { 266: ReferenceKey ref = (ReferenceKey) obj; 267: 268: /* First check if the two references are the same. 269: If they are, that means we must be clearing GCd objects. */ 270: if (this == obj) 271: return true; 272: 273: return (ref.get () == get ()); 274: } 275: 276: return false; 277: } 278: } 279: 280: // instance of VMIdManager 281: private static VMIdManager _idm = new VMIdManager (); 282: 283: // A reference queue for our objects 284: private ReferenceQueue _refQueue; 285: 286: // Mapping of objects (ReferenceKey) to IDs (ObjectId) 287: private Hashtable _oidTable; 288: 289: // Mapping of ID numbers (Long) to IDs (ObjectId) 290: private Hashtable _idTable; 291: 292: /* Mapping of class (ReferenceKey) to IDs (ReferenceTypeId) for reference 293: types. Unlike other types, reference id types are NEVER released. */ 294: private Hashtable _classTable; 295: 296: // Mapping of ID numbers (Long) to reference type IDs (ReferenceTypeId) 297: private Hashtable _ridTable; 298: 299: /** 300: * Gets the instance of VMIdManager, constructing a new one 301: * if none currently exists. 302: */ 303: public static VMIdManager getDefault () 304: { 305: return _idm; 306: } 307: 308: // Constructs a new <code>IdManager</code> 309: private VMIdManager () 310: { 311: _refQueue = new ReferenceQueue (); 312: _oidTable = new Hashtable (50); 313: _idTable = new Hashtable (50); 314: _classTable = new Hashtable (20); 315: _ridTable = new Hashtable (20); 316: } 317: 318: // Updates the object ID table, removing IDs whose objects have 319: // been garbage collected. 320: private void _update () 321: { 322: Reference ref; 323: while ((ref = _refQueue.poll ()) != null) 324: { 325: ObjectId id = (ObjectId) _oidTable.get (ref); 326: _oidTable.remove (ref); 327: _idTable.remove (new Long (id.getId ())); 328: } 329: } 330: 331: /** 332: * Returns an id for the given object, adding it 333: * if it does not have an id. 334: * 335: * @param theObject the object to get an ID/add 336: * @returns the ID of the object 337: */ 338: public ObjectId getObjectId (Object theObject) 339: { 340: // Special case: null object. 341: if (theObject == null) 342: return new NullObjectId (); 343: 344: ReferenceKey ref = new ReferenceKey (theObject, _refQueue); 345: ObjectId id = (ObjectId) _oidTable.get (ref); 346: if (id == null) 347: { 348: // update the tables -- this is an arbitrary place to put this 349: _update (); 350: 351: // Object not found. Make new id for it 352: id = IdFactory.newObjectId (ref); 353: _oidTable.put (ref, id); 354: _idTable.put (new Long (id.getId ()), id); 355: } 356: 357: return id; 358: } 359: 360: /** 361: * Returns the <code>JdwpId</code> for a given ID. Unlike 362: * <code>getId</code>, it throws an exception if the ID is not 363: * known. 364: * 365: * @param id the numerical ID of the desired <code>JdwpId</code> 366: * @throws InvalidObjectException if the ID is not found 367: */ 368: public ObjectId get (long id) 369: throws InvalidObjectException 370: { 371: // Special case: null object id. 372: if (id == 0) 373: return new NullObjectId (); 374: 375: ObjectId oid = (ObjectId) _idTable.get (new Long (id)); 376: if (oid == null) 377: throw new InvalidObjectException (id); 378: 379: return oid; 380: } 381: 382: public ObjectId readObjectId (ByteBuffer bb) 383: throws InvalidObjectException 384: { 385: long id = bb.getLong (); 386: return get (id); 387: } 388: 389: /** 390: * Gets the reference type id for the given class, creating 391: * a new one if it does not already have an id 392: * 393: * @param clazz the class for which to get an ID 394: * @returns the ID of the class 395: */ 396: public ReferenceTypeId getReferenceTypeId (Class clazz) 397: { 398: ReferenceKey ref = new ReferenceKey (clazz); 399: ReferenceTypeId id = (ReferenceTypeId)_classTable.get (ref); 400: if (id == null) 401: { 402: // Object not found. Make new id for it 403: id = IdFactory.newReferenceTypeId (ref); 404: _classTable.put (ref, id); 405: _ridTable.put (new Long (id.getId ()), id); 406: } 407: 408: return id; 409: } 410: 411: /** 412: * Returns the <code>ReferenceTypeId</code> for a given ID. Unlike 413: * <code>getReferenceTypeId</code>, it throws an exception if the ID is not 414: * known. 415: * 416: * @param id the numerical ID of the desired reference type 417: * @throws InvalidClassException if the ID is not found 418: */ 419: public ReferenceTypeId getReferenceType (long id) 420: throws InvalidClassException 421: { 422: ReferenceTypeId rid = (ReferenceTypeId) _ridTable.get (new Long (id)); 423: if (rid == null) 424: throw new InvalidClassException (id); 425: 426: return rid; 427: } 428: 429: public ReferenceTypeId readReferenceTypeId (ByteBuffer bb) 430: throws InvalidClassException 431: { 432: long id = bb.getLong (); 433: return getReferenceType (id); 434: } 435: }