Source for gnu.classpath.jdwp.VMIdManager

   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: }