From 11dfd6edca6d81c20e7ae0f6098b5a0251611473 Mon Sep 17 00:00:00 2001 From: jprocter Date: Tue, 24 Apr 2007 16:13:49 +0000 Subject: [PATCH] introduced hashCode based object change detection that works (see vamsas.test.exampleApplication) git-svn-id: https://svn.lifesci.dundee.ac.uk/svn/repository/trunk@383 be28352e-c001-0410-b1a7-c7978e42abec --- src/uk/ac/vamsas/client/Vobject.java | 72 ++++++++++++++++---- src/uk/ac/vamsas/client/Vobjhash.java | 13 +++- src/uk/ac/vamsas/client/VorbaXmlBinder.java | 23 ++----- .../vamsas/test/simpleclient/ArchiveReports.java | 33 ++++++++- 4 files changed, 102 insertions(+), 39 deletions(-) diff --git a/src/uk/ac/vamsas/client/Vobject.java b/src/uk/ac/vamsas/client/Vobject.java index 8e9fc51..6603f1a 100644 --- a/src/uk/ac/vamsas/client/Vobject.java +++ b/src/uk/ac/vamsas/client/Vobject.java @@ -80,6 +80,12 @@ public abstract class Vobject { super(); testInstanceForIdField(); } + /** + * Override Object.hashCode with base value for castor generated object hashcodes. + */ + public int hashCode() { + return 17; + } java.lang.reflect.Field ___id_field=null; // set to ease pain of reflection /** * set the isRegisterable flag based on the presence of a 'private String _id' field in @@ -179,7 +185,36 @@ public abstract class Vobject { } return null; } - + /** + * calls the castor-generated hashCode() method + * @return + */ + protected int __callHash() { + try { + Method fd = this.getClass().getMethod("hashCode", (Class[]) null); + Object hashvalue = fd.invoke((Object) this, (Object[]) null); + if (log.isDebugEnabled()) + log.debug(this.getClass().getName()+" called hashCode()!"); + if (hashvalue!=null && hashvalue instanceof Integer) { + return ((Integer) hashvalue).intValue(); + } + } catch (InvocationTargetException e) { + log.error("SourceGeneration of " + + this.getClass().toString() + + "\n has resulted in an inaccessible 'hashCode' method!\nHave you set org.exolab.castor.builder.equalsmethod=true in castorbuilder.properties ?.", e); + } + catch (IllegalAccessException e) { + log.error("SourceGeneration of " + + this.getClass().toString() + + "\n has resulted in an inaccessible 'hashCode' method!\nHave you set org.exolab.castor.builder.equalsmethod=true in castorbuilder.properties ?.", e); + } catch (SecurityException e) { + log.error("Security access violation for "+this.getClass().toString(),e); + } catch (NoSuchMethodException e) { + log.warn(this.getClass().toString()+" was erroneously extending from a Vorba Vobject class (Implementation error? no hashCode() method)" + + "\nHave you set org.exolab.castor.builder.equalsmethod=true in castorbuilder.properties ?.", e); + } + return 0; + } /** * calculate a hash for the Vobject with all housekeeping fields at standard * values. (isRegisterable is an immutable attribute property) @@ -187,28 +222,29 @@ public abstract class Vobject { * @return true if new hash different to last hash (or first time its been computed) */ synchronized protected boolean doHash() { + boolean stored = __stored_in_document; + __stored_in_document=false; + boolean updated = __updated_since_last_read; + __updated_since_last_read=false; + boolean added_since=__added_since_last_read; + __added_since_last_read=false; long __old_hash = __last_hash; __last_hash = 0; + // leave registerable - doesn't change + boolean visited = __visited; + __visited=false; Vobject _V_parent=V_parent; V_parent=null; VorbaId thisid = vorbaId; + vorbaId = null; IVorbaIdFactory factory = __vorba; - boolean stored = __stored_in_document; - boolean updated = __updated_since_last_read; - boolean visited = __visited; + __vorba = null; java.lang.reflect.Field idfield = ___id_field; ___id_field=null; - __updated_since_last_read=false; - __stored_in_document=false; - boolean added_since=__added_since_last_read; - __added_since_last_read=false; long l_hash = __l_hash; __l_hash = 0; - vorbaId = null; - __vorba = null; - __visited=false; // compute hash - __last_hash = this.hashCode(); + __last_hash = __callHash(); // reset houseskeeping variables ___id_field=idfield; vorbaId = thisid; @@ -317,6 +353,8 @@ public abstract class Vobject { */ protected void set__stored_in_document(boolean __stored_in_document) { this.__stored_in_document = __stored_in_document; + if(__stored_in_document && log.isDebugEnabled()) + log.debug("Retrieved document object: "+this.getVorbaId()); } /** @@ -324,6 +362,9 @@ public abstract class Vobject { */ protected void set__added_since_last_read(boolean __added_since_last_read) { this.__added_since_last_read = __added_since_last_read; + + if(__added_since_last_read && log.isDebugEnabled()) + log.debug("New object in document: "+this.getVorbaId()); } /** @@ -333,7 +374,7 @@ public abstract class Vobject { * * @return Returns the __last_hash. */ - public int get__last_hash() { + public long get__last_hash() { return __last_hash; } @@ -384,7 +425,10 @@ public abstract class Vobject { XMLClassDescriptorImpl descimpl = null; try { // castor descriptor resolver magic - descriptor = this.getClass().getClassLoader().loadClass(this.getClass().getName()+"Descriptor"); + StringBuffer desname = new StringBuffer(this.getClass().getName()); + desname.insert(desname.lastIndexOf("."), ".descriptors"); + desname.append("Descriptor"); + descriptor = this.getClass().getClassLoader().loadClass(desname.toString()); descimpl = (XMLClassDescriptorImpl) descriptor.getConstructor((Class[])null).newInstance((Object[])null); } catch (Exception e) { log.fatal("Source Generation Error!: Couldn't resolve descriptor for " diff --git a/src/uk/ac/vamsas/client/Vobjhash.java b/src/uk/ac/vamsas/client/Vobjhash.java index 767a9a6..1b258dc 100644 --- a/src/uk/ac/vamsas/client/Vobjhash.java +++ b/src/uk/ac/vamsas/client/Vobjhash.java @@ -7,13 +7,17 @@ package uk.ac.vamsas.client; * */ public class Vobjhash { - long hashvalue; + private static org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(Vobjhash.class); + long hashvalue; /** * creates a record of the hash value for a vamsas document object * @param tobemarshalled */ public Vobjhash(Vobject tobemarshalled) { - hashvalue = tobemarshalled.__getLHash(); + tobemarshalled.doHash(); + hashvalue = tobemarshalled.get__last_hash(); + if (log.isDebugEnabled()) + log.debug(tobemarshalled.getVorbaId()+":"+hashvalue); } /** * compares the old hash value with the unmarshalled object's most recently computed hash value and updates internal record. @@ -22,7 +26,10 @@ public class Vobjhash { */ public boolean isUpdated(Vobject unmarshalled) { long oldhash = hashvalue; - hashvalue=unmarshalled.__getLHash(); + unmarshalled.doHash(); + hashvalue=unmarshalled.get__last_hash(); + if (log.isDebugEnabled() && oldhash!=hashvalue) + log.debug(" has changed."+unmarshalled.getVorbaId()); return oldhash!=hashvalue; } } diff --git a/src/uk/ac/vamsas/client/VorbaXmlBinder.java b/src/uk/ac/vamsas/client/VorbaXmlBinder.java index 12543f2..dadd3fa 100644 --- a/src/uk/ac/vamsas/client/VorbaXmlBinder.java +++ b/src/uk/ac/vamsas/client/VorbaXmlBinder.java @@ -25,7 +25,6 @@ import org.exolab.castor.xml.UnmarshalListener; import org.exolab.castor.xml.Unmarshaller; import org.exolab.castor.xml.ValidationException; -import uk.ac.vamsas.client.utils.ChecksummedReader; import uk.ac.vamsas.objects.core.VamsasDocument; /** * Implements the Vamsas Vobject ID machinery for translating @@ -43,14 +42,7 @@ public class VorbaXmlBinder implements UnmarshalListener { private final Hashtable oldobjhashes; private final Hashtable objrefs; private final Vector updatedobjs; // not yet used elswhere ? - private final ChecksummedReader inStream; - public VorbaXmlBinder(IVorbaIdFactory vorbafactory, Vector obj, Hashtable objrefs, Hashtable oldobjhashes, Vector updatedobjs) { - this(vorbafactory, obj, objrefs, oldobjhashes, updatedobjs, null); - - } - - public VorbaXmlBinder(IVorbaIdFactory vorbafactory2, Vector unrefedObj, Hashtable objrefs2, Hashtable oldobjhashes, Vector updatedObj, ChecksummedReader ckedInstream) { - this.inStream = ckedInstream; + public VorbaXmlBinder(IVorbaIdFactory vorbafactory2, Vector unrefedObj, Hashtable objrefs2, Hashtable oldobjhashes, Vector updatedObj) { this.vorbafactory = vorbafactory2; this.obj = unrefedObj; this.objrefs = objrefs2; @@ -90,8 +82,6 @@ public class VorbaXmlBinder implements UnmarshalListener { public void initialized(Object object) { if (object instanceof Vobject) { Vobject nobj = (Vobject) object; - if (nobj.isRegisterable() && nobj.___id_field!=null && inStream!=null) - nobj.__setInitHash(inStream.getChecksum()); // record start tag position } } @@ -107,8 +97,6 @@ public class VorbaXmlBinder implements UnmarshalListener { nobj.set__stored_in_document(true); try { if (nobj.isRegisterable() && nobj.___id_field!=null) { - if (inStream!=null) - nobj.__setFinalHash(inStream.getChecksum()); // compute LHash as difference between end and start tag positions VorbaId nobj_id=null; // look for the id field (should be an NCName string) nobj.__vorba = vorbafactory; @@ -131,8 +119,7 @@ public class VorbaXmlBinder implements UnmarshalListener { // TODO: add to list of objects without a valid vorbaId obj.add(nobj); } - nobj.doHash(); // TODO: DECIDE IF WE NEED TO DO THIS STILL: THIS DOESNT WORK! updates detected by comparing with last hash when marshalling - + nobj.doHash(); // updates detected by comparing with last hash when marshalling // check to see if new object was present in old object hash if (nobj_id!=null) { if (oldobjhashes.containsKey(nobj_id)) { @@ -140,7 +127,6 @@ public class VorbaXmlBinder implements UnmarshalListener { if (oldhash.isUpdated(nobj)) { // mark the object as updated in this document read. nobj.set__updated_since_last_read(true); - oldobjhashes.put(nobj_id, new Vobjhash(nobj)); updatedobjs.addElement(nobj); } } else { @@ -242,14 +228,13 @@ public class VorbaXmlBinder implements UnmarshalListener { final VorbaIdFactory vorbafactory = factory; final Vector unrefedObj = new Vector(); final Vector updatedObj = new Vector(); - ChecksummedReader ckedInstream = new ChecksummedReader(instream); - unmarshaller.setUnmarshalListener(new VorbaXmlBinder(vorbafactory, unrefedObj, objrefs, oobjhashes,updatedObj, ckedInstream)); + unmarshaller.setUnmarshalListener(new VorbaXmlBinder(vorbafactory, unrefedObj, objrefs, oobjhashes,updatedObj)); // Call the unmarshaller. try { while (instream.ready()) { // TODO: mark objects in oobjhash prior to unmarshalling, to detect when objects have been lost through an update. //tohere - Object obj = unmarshaller.unmarshal(ckedInstream); + Object obj = unmarshaller.unmarshal(instream); boolean sync=ensure_references(unrefedObj, objrefs); if (!(obj instanceof Vobject)) return null; diff --git a/src/uk/ac/vamsas/test/simpleclient/ArchiveReports.java b/src/uk/ac/vamsas/test/simpleclient/ArchiveReports.java index 94d06bf..766d6a2 100644 --- a/src/uk/ac/vamsas/test/simpleclient/ArchiveReports.java +++ b/src/uk/ac/vamsas/test/simpleclient/ArchiveReports.java @@ -41,8 +41,10 @@ public class ArchiveReports { return false; } Entry[] pe = p.getEntry(); - for (int i=0; i") +") contains "+(ds=r.getDataSetCount())+" DataSets, " + (tr=r.getTreeCount())+" Global trees\n"); + outputVobjectState(r, outstr); if (cascade) { for (int j=0; j0) { for (int i=0; i