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 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.io.IOException; 52: import java.nio.ByteBuffer; 53: import java.util.HashMap; 54: import java.util.Hashtable; 55: import java.util.Iterator; 56: 57: /** 58: * This class manages objects and referencetypes that are reported 59: * to the debugger. All objects and referencetypes reported to the 60: * debugger should go through this manager. 61: * 62: * A brief summary of what an <code>IdManager</code> must provide: 63: * 64: * <code> 65: * public ObjectId getObjectId (Object theObject); 66: * public ObjectId get (long id); 67: * public ObjectId readObjectId (ByteBuffer bb); 68: * public ReferenceTypeId getReferenceTypeId (Class clazz); 69: * public ReferenceTypeId getReferenceType (long id); 70: * public ReferenceTypeId readReferenceTypeId (ByteBuffer bb); 71: * </code> 72: * 73: * See the javadoc on these methods later in this file for more 74: * information on these functions. 75: * 76: * <b>NOTE:</b> All IDs handled by the ID manager (all object and reference 77: * type IDs) are assumed to be of type <code>long</code>. 78: * 79: * @author Keith Seitz (keiths@redhat.com) 80: */ 81: public class VMIdManager 82: { 83: // This factory generates ids for objects and types that may 84: // be sent to a debugger. 85: private static class IdFactory 86: { 87: // ID of last object / referencetype 88: private static Object _idLock = new Object (); 89: private static Object _ridLock = new Object (); 90: private static long _lastId = 0; 91: private static long _lastRid = 0; 92: 93: // A list of all ID types 94: private static HashMap _idList = new HashMap (); 95: 96: // Initialize the id list with known types 97: static 98: { 99: // ObjectId and ArrayId are special cases. See newObjectId. 100: _idList.put (ClassLoaderId.typeClass, ClassLoaderId.class); 101: _idList.put (ClassObjectId.typeClass, ClassObjectId.class); 102: //_idList.put (FieldId.typeClass, FieldId.class); 103: //_idList.put (FrameId.typeClass, FrameId.class); 104: //_idList.put (MethodId.typeClass, MethodId.class); 105: _idList.put (StringId.typeClass, StringId.class); 106: _idList.put (ThreadId.typeClass, ThreadId.class); 107: _idList.put (ThreadGroupId.typeClass, ThreadGroupId.class); 108: } 109: 110: /** 111: * Returns a new id for the given object 112: * 113: * @param object the object for which an id is desired 114: * @returns a suitable object id 115: */ 116: public static ObjectId newObjectId (SoftReference obj) 117: { 118: ObjectId id = null; 119: Object object = obj.get (); 120: 121: // Special case: arrays 122: if (object.getClass ().isArray ()) 123: id = new ArrayId (); 124: else 125: { 126: // Loop through all classes until we hit baseclass 127: Class myClass; 128: for (myClass = object.getClass (); myClass != null; 129: myClass = myClass.getSuperclass ()) 130: { 131: Class clz = (Class) _idList.get (myClass); 132: if (clz != null) 133: { 134: try 135: { 136: id = (ObjectId) clz.newInstance (); 137: synchronized (_idLock) 138: { 139: id.setId (++_lastId); 140: } 141: id.setReference (obj); 142: return id; 143: } 144: catch (InstantiationException ie) 145: { 146: // This really should not happen 147: throw new RuntimeException ("cannot create new ID", ie); 148: } 149: catch (IllegalAccessException iae) 150: { 151: // This really should not happen 152: throw new RuntimeException ("illegal access of ID", iae); 153: } 154: } 155: } 156: 157: /* getSuperclass returned null and no matching ID type found. 158: So it must derive from Object. */ 159: id = new ObjectId (); 160: } 161: 162: synchronized (_idLock) 163: { 164: id.setId (++_lastId); 165: } 166: id.setReference (obj); 167: return id; 168: } 169: 170: /** 171: * Returns a new reference type id for the given class 172: * 173: * @param clazz the <code>Class</code> for which an id is desired 174: * @returns a suitable reference type id or null when the 175: * reference is cleared. 176: */ 177: public static ReferenceTypeId newReferenceTypeId (SoftReference ref) 178: { 179: ReferenceTypeId id; 180: Class clazz = (Class) ref.get (); 181: if (clazz == null) 182: return null; 183: 184: if (clazz.isArray ()) 185: id = new ArrayReferenceTypeId (); 186: else if (clazz.isInterface ()) 187: id = new InterfaceReferenceTypeId (); 188: else 189: id = new ClassReferenceTypeId (); 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: ReferenceKey ref = new ReferenceKey (theObject, _refQueue); 341: ObjectId id = (ObjectId) _oidTable.get (ref); 342: if (id == null) 343: { 344: // update the tables -- this is an arbitrary place to put this 345: _update (); 346: 347: // Object not found. Make new id for it 348: id = IdFactory.newObjectId (ref); 349: _oidTable.put (ref, id); 350: _idTable.put (new Long (id.getId ()), id); 351: } 352: 353: return id; 354: } 355: 356: /** 357: * Returns the <code>JdwpId</code> for a given ID. Unlike 358: * <code>getId</code>, it throws an exception if the ID is not 359: * known. 360: * 361: * @param id the numerical ID of the desired <code>JdwpId</code> 362: * @throws InvalidObjectException if the ID is not found 363: */ 364: public ObjectId get (long id) 365: throws InvalidObjectException 366: { 367: ObjectId oid = (ObjectId) _idTable.get (new Long (id)); 368: if (oid == null) 369: throw new InvalidObjectException (id); 370: 371: return oid; 372: } 373: 374: public ObjectId readObjectId (ByteBuffer bb) 375: throws InvalidObjectException 376: { 377: long id = bb.getLong (); 378: return get (id); 379: } 380: 381: /** 382: * Gets the reference type id for the given class, creating 383: * a new one if it does not already have an id 384: * 385: * @param clazz the class for which to get an ID 386: * @returns the ID of the class 387: */ 388: public ReferenceTypeId getReferenceTypeId (Class clazz) 389: { 390: ReferenceKey ref = new ReferenceKey (clazz); 391: ReferenceTypeId id = (ReferenceTypeId)_classTable.get (ref); 392: if (id == null) 393: { 394: // Object not found. Make new id for it 395: id = IdFactory.newReferenceTypeId (ref); 396: _classTable.put (ref, id); 397: _ridTable.put (new Long (id.getId ()), id); 398: } 399: 400: return id; 401: } 402: 403: /** 404: * Returns the <code>ReferenceTypeId</code> for a given ID. Unlike 405: * <code>getReferenceTypeId</code>, it throws an exception if the ID is not 406: * known. 407: * 408: * @param id the numerical ID of the desired reference type 409: * @throws InvalidClassException if the ID is not found 410: */ 411: public ReferenceTypeId getReferenceType (long id) 412: throws InvalidClassException 413: { 414: ReferenceTypeId rid = (ReferenceTypeId) _ridTable.get (new Long (id)); 415: if (rid == null) 416: throw new InvalidClassException (id); 417: 418: return rid; 419: } 420: 421: public ReferenceTypeId readReferenceTypeId (ByteBuffer bb) 422: throws InvalidClassException 423: { 424: long id = bb.getLong (); 425: return getReferenceType (id); 426: } 427: }