4.6 Putting It All Together


The following code excerpts show how to use the state interrogation methods and instance callbacks to debug common problems. A class is developed that serves as a helper to find out about the actual state of objects, number of objects in memory, objects per state, and so on. Because most JDO vendors don't provide methods to get information about a PersistenceManager's in-memory cache, it is quite handy to have a vendor-independent debug facility.

All the following methods can be found in the DebugStates.java source file.

The first method is a helper to get the memory ID of an object. A simple way to get an informal ID of an object is to call identityHashCode . Although this is not a unique ID, it works on common virtual machines and can help to debug objects in memory:

 
 private static String getMemoryId(Object obj) {   long id = System.identityHashCode(obj);   return Long.toString(id,16); // Hex value } 

The next method returns a string that contains both the persistent object ID and the in-memory hash code of an object. If the object is not yet persistent, only the memory ID is returned:

 
 private static String getId(Object obj) {   if (obj instanceof PersistenceCapable) {     PersistenceManager pm =        JDOHelper.getPersistenceManager(obj);     if (pm != null) {       return "@"+getMemoryId(obj)+                    " OID:"+pm.getObjectId(obj);     }   }   return "@"+getMemoryId(obj)+" no OID"; } 

To get even more information about an object's lifecycle, a table of counters is used to keep track of persistent instances. These counters are incremented in the InstanceCallbacks interface methods and the no-args constructor, which is used by the JDO implementation to create new hollow objects. A table entry object keeps a weak reference to each persistent instance, so that it is possible to get the number of objects that were garbage-collected .

Here is a sample class that uses the DebugStates class to count its callback invocations:

 
 public class Author    implements InstanceCallbacks {     protected String name;     public Author( String name ) {         this.name = name;     }     public String toString()     {         return "Author: " + name;     }   /**    * This is the constructor called for hollow objects.    * It registers with the debug class.    */   private Author()   {     DebugStates.constructor(this);   }   // JDO callbacks, delegate to DebugStates:   public void jdoPostLoad() {     DebugStates.postLoad(this);   }   public void jdoPreStore() {     DebugStates.preStore(this);   }   public void jdoPreClear() {     DebugStates.preClear(this);   }   public void jdoPreDelete() {     DebugStates.preDelete(this);   } } 

The above DebugStates methods delegate to a function that gets a table entry object from a static table, based on the memory identity. If the entry does not exist, it is added to the table:

 
 private static ObjectTable objectTable = new ObjectTable(); private static ObjectTableEntry tableEntry(Object obj) {   return objectTable.register(obj); } /**  * Should be called in the no-args constructor of a  * persistence-capable  * class.  */ public static void constructor(Object obj) {   tableEntry(obj).constructor++; } ... equivalent methods for InstanceCallbacks ... public ObjectTableEntry register(Object obj) {   Long id = new Long(System.identityHashCode(obj));   ObjectTableEntry entry = (ObjectTableEntry)table.get(id);   if (entry == null) {     entry = new ObjectTableEntry(id,obj);     table.put(id,entry);   }   return entry; } 

Now that every object is put into the table at some point, it is easy to count used objects or find objects of a specific kind. The following method can help to get some figures of the memory usage:

 
 public String getStatistics() {   int constructors = 0;   int postLoad = 0;   int preStore = 0;   int preClear = 0;   int preDelete = 0;   int objects = 0;   int gced = 0;   for (Iter i = new Iter();i.hasNext();) {     ObjectTableEntry e = i.next();     objects++;     if (e.get() == null) gced++;     constructors += e.constructor;     postLoad += e.postLoad;     preStore += e.preStore;     preClear += e.preClear;     preDelete += e.preDelete;   }   String NL = System.getProperty("line.separator");   String result = "   Number of Objects:   "+objects+NL+     "   Garbage collected:   "+gced+NL+     "   Constructors called: "+constructors+NL+     "   postLoad called:     "+postLoad+NL+     "   preStore called:     "+preStore+NL+     "   preClear called:     "+preClear+NL+     "   preDelete called:    "+preDelete+NL;   return result; } 

The result is interesting because even the first simple allocation of two objects like the following code:

 
 pm.currentTransaction().begin(); Author a = new Author("Heiko Bobzin"); Book b = new Book("Core JDO", a); 

prints the following result:

 
 Number of Objects:   2 Garbage collected:   0 Constructors called: 2 postLoad called:     0 preStore called:     0 preClear called:     0 preDelete called:    0 

Although the no-args constructor wasn't called in the sample code, it was traced that two other objects have been created. These two objects are held as factory objects by the JDO implementation to create new Author and Book instances. The code to call the no-args constructor has been added by the enhancer for security reasons, which are explained later in Chapter 10.

When these two objects are made persistent ( Author by reachability):

 
 pm.makePersistent(b); pm.currentTransaction().commit(); 

the preStore and preClear instance callbacks are called for each object:

 
 Number of Objects:   2 Garbage collected:   0 Constructors called: 2 postLoad called:     0 preStore called:     2 preClear called:     2 preDelete called:    0 

In another test, all objects of the Book Extent were printed:

 
 pm.currentTransaction().begin(); Extent e = pm.getExtent(Book.class,true); Iterator iter = e.iterator(); while (iter.hasNext()) {   Book a = (Book)iter.next();   String title = a.title; // creates a hollow Author ! } 

The database contained 20 Book instances and 20 Author instances, so that 42 objects were created in total (two for the factory objects). postLoad and preClear have been called for the Book instances only, whereas the other objects were hollow instances:

 
 Number of Objects:   42 Garbage collected:   10 Constructors called: 42 postLoad called:     20 preStore called:     0 preClear called:     20 preDelete called:    0 


Core Java Data Objects
Core Java Data Objects
ISBN: 0131407317
EAN: 2147483647
Year: 2003
Pages: 146

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net