/** * */ package org.vamsas.client; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Iterator; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.exolab.castor.mapping.FieldDescriptor; import org.exolab.castor.mapping.FieldHandler; import org.exolab.castor.xml.util.XMLClassDescriptorImpl; import org.vamsas.test.simpleclient.VamsasArchive; /** * Base class for all Vamsas objects extracted from an IClientDocument. An * Vobject maybe registered or unregistered. * * @author jimp * */ public abstract class Vobject { static Log log = LogFactory.getLog(Vobject.class); /** * true if Vobject was stored in a vamsas Document or has been retrieved from it */ protected boolean __stored_in_document = false; /** * memory of the last doHash() value computed for the Vobject * @see doHash() */ protected long __last_hash = 0; /** * set by testInstanceForIdField() if Vobject should have a VorbaId */ protected boolean registerable = false; /** * unique id for all vamsas objects allows unambiguous referencing to any * Vobject in the vamsas document */ protected VorbaId vorbaId = null; /** * the source of unique VorbaIds. */ protected IVorbaIdFactory __vorba = null; /** * */ protected Vobject() { super(); testInstanceForIdField(); } /** * set the isRegisterable flag based on the presence of a 'private String _id' field in * the reflected class instance. */ private void testInstanceForIdField() { // look for the id field (should be an NCName string) // TODO: decide if 'id' is an appropriate reserved attribute name for the VorbaId try { java.lang.reflect.Field fd = this.getClass().getDeclaredField("_id"); if (String.class.isAssignableFrom(fd.getType())) { this.setRegisterable(true); } } catch (SecurityException e) { e.printStackTrace(); } catch (NoSuchFieldException e) { this.setRegisterable(false); } } /** * update the Vobject instance's _id field, based on the contents of the * VorbaId. Only call this if you mean to do it! */ protected void setInstanceIdField() { if (registerable) { if (__vorba != null) try { Method fd = this.getClass().getMethod("setId", new Class[] { String.class }); fd.invoke((Object) this, new Object[] {new String(this.getVorbaId().id)}); log.debug(this.getClass().getName()+" called setInstanceIdField!"); } catch (InvocationTargetException e) { log.error("SourceGeneration of " + this.getClass().toString() + "\n has resulted in an inaccessible 'setId' method!\nCannot set ID from the vorbaId Vobject.", e); } catch (IllegalAccessException e) { log.error("SourceGeneration of " + this.getClass().toString() + "\n has resulted in an inaccessible 'setId' method!\nCannot set ID from the vorbaId Vobject.", e); } catch (SecurityException e) { log.error("Security access violation for "+this.getClass().toString(),e); } catch (NoSuchMethodException e) { log.warn(this.getClass().toString()+" was erroneously marked as a Vorba Vobject class (Implementation error?)"); this.setRegisterable(false); } } else { System.err.println("Client error. Trying to setInstanceIdField on a " + this.getClass().toString() + " (which cannot be given a vorbaId)"); } } /** * calculate a hash for the Vobject with all housekeeping fields at standard * values. (isRegisterable is an immutable attribute property) * TODO: decide if __stored_in_document should be included in the hash or not. * @return true if new hash different to last hash */ synchronized protected boolean doHash() { long __old_hash = __last_hash; __last_hash = 0; VorbaId thisid = vorbaId; IVorbaIdFactory factory = __vorba; boolean stored = __stored_in_document; vorbaId = null; __vorba = null; __last_hash = this.hashCode(); vorbaId = thisid; __vorba = factory; __stored_in_document = stored; return (__old_hash==0) || (__old_hash == __last_hash); } /** * TODO: combine two versions of the same collection Vobject to resolve * asynchronous updates to the same vamsas Vobject Merges two vamsas objects, * one of which is a later version of the earlier (ie they have the same * vorbaId but one is a later version recently read from the vamsasDocument * collection. * * @return */ protected boolean merge(Vobject laterCopy) { log.warn(this.getClass().getName()+".merge() not implemented."); return true; } /** * * @return true if Vobject is registered */ public boolean isRegistered() { return (registerable) ? (vorbaId != null) : false; } /** * Method to get fixed reference for the Vobject in the vamsas document. * * @returns null if Vobject is neither registered or not associated with a * properly instantiated VorbaIdFactory. */ public VorbaId getVorbaId() { if (registerable && vorbaId == null) { // Try to use the associated factory. if (__vorba != null) if ((vorbaId = __vorba.makeVorbaId(this)) == null) return null; // Factory not valid. else { this.setInstanceIdField(); return vorbaId; } } return vorbaId; } /** * used by the IClient implementation to generate unique Id based on client * applications current namespace. */ protected void setVorbaId(VorbaId newid) { vorbaId = newid; } /** * @return true if Vobject is present in Vamsas Document. */ public boolean is__stored_in_document() { return __stored_in_document; } /** * for use by Vorba agent to reflect state of vamsas Vobject to client * application. * Setting stored_in_document on a registerable Vobject without a * vorbaId will mean is will *never* get a vorbaId and * horrible things will happen. * @param __stored_in_document true if Vobject has been marshalled into current document. */ protected void set__stored_in_document(boolean __stored_in_document) { this.__stored_in_document = __stored_in_document; } /** * __last_hash is the hash value computed when the Vobject was last checked * against a IClientDocument generated by the Vobject's parent IClient * instance. * * @return Returns the __last_hash. */ public long get__last_hash() { return __last_hash; } /** * @return true if Vobject can have a vorbaId */ public boolean isRegisterable() { return registerable; } /** * Called by __testInstanceForidField and the post-unmarshalling handler * to indicate if Vobject will have a vorbaId. * @param registerable */ protected void setRegisterable(boolean registerable) { this.registerable = registerable; } /** * ensure's internal id field corresponds to vorbaId and * cascade through all fields referring to an instance of Vobject * calling the same method on them. * TODO: LATER: properly apply castors own field mechanisms to get at accessors * TODO: FIX CYCLIC __ensure+instance_ids * Implementation note for the todo: * this works like a depth-first search over all vamsas objects in an vamsasDocument. * The doHash() function is used as the 'visited' flag - * this *is not* a valid heuristic, although it will work "most of the time". * TODO: LATER? Add another method for setDefaultProvenanceField (in the spirit of setInstanceIdField) using the info from the __vorba.getClient/User/Session methods */ protected void __ensure_instance_ids() { if (__vorba==null) throw new Error("Improperly intialised org.vamsas.client.Vobject - no VorbaFactory given."); log.debug("doing "+this.getClass()+".__ensure_instance_ids()"); if (!__stored_in_document && registerable) setInstanceIdField(); if (!doHash()) return; // nothing has changed in this Vobject - probably visited it before. Class descriptor = null; XMLClassDescriptorImpl descimpl = null; try { // castor descriptor resolver magic descriptor = this.getClass().getClassLoader().loadClass(this.getClass().getName()+"Descriptor"); descimpl = (XMLClassDescriptorImpl) descriptor.getConstructor(null).newInstance(null); } catch (Exception e) { log.fatal("Source Generation Error!: Couldn't resolve descriptor for " +this.getClass().getName() +" was 'generate descriptors' set for castorbuilder.properties?"); return; } FieldDescriptor fields[] = descimpl.getFields(); for (int i=0,j=fields.length; i