*
*/
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 object maybe registered or unregistered.
+ * Base class for all Vamsas objects extracted from an IClientDocument. An
+ * object maybe registered or unregistered.
*
* @author jimp
- *
+ *
*/
public abstract class object {
+ static Log log = LogFactory.getLog(object.class);
+
+ /**
+ * true if object 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 object
+ * @see doHash()
+ */
+ protected long __last_hash = 0;
+ /**
+ * set by testInstanceForIdField() if object should have a VorbaId
+ */
+ protected boolean registerable = false;
- /**
- * unique id for all vamsas objects
- * allows unambiguous referencing
- * to any object in the vamsas document
- */
- protected boolean __stored_in_document=false;
- protected long __last_hash=0;
- protected VorbaId vorbaId=null;
- protected IVorbaIdFactory __vorba=null;
- /**
- *
- * @return true if object is registered
- */
- public boolean isRegistered() {
- return (vorbaId!=null);
- }
- /**
- * Method to get fixed reference for
- * the object in the vamsas document.
- * @returns null if object is neither registered
- * or not associated with a properly instantiated
- * VorbaIdFactory.
- */
- public String getVorbaId() {
- if (vorbaId==null) {
- // Try to use the associated factory.
- if (__vorba!=null)
- if ((vorbaId = __vorba.makeVorbaId())==null)
- return null; // Factory not valid.
- else
- return null;
+
+ /**
+ * unique id for all vamsas objects allows unambiguous referencing to any
+ * object in the vamsas document
+ */
+ protected VorbaId vorbaId = null;
+
+ /**
+ * the source of unique VorbaIds.
+ */
+ protected IVorbaIdFactory __vorba = null;
+
+ /**
+ *
+ */
+ protected object() {
+ 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);
}
- return vorbaId.getId();
+ } catch (SecurityException e) {
+ e.printStackTrace();
+ } catch (NoSuchFieldException e) {
+ this.setRegisterable(false);
}
- /**
- * used by the IClient implementation
- * to generate unique Id based on
- * client applications current namespace.
- */
- protected void setVorbaId(VorbaId newid) {
- vorbaId = newid;
+ }
+
+ /**
+ * update the object 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 object.", 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 object.", 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 object 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)");
}
-
- /**
- * @return true if object is present in Vamsas Document.
- */
- public boolean is__stored_in_document() {
- return __stored_in_document;
+ }
+
+ /**
+ * calculate a hash for the object 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 object to resolve
+ * asynchronous updates to the same vamsas object 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(object laterCopy) {
+ log.warn(this.getClass().getName()+".merge() not implemented.");
+ return true;
+ }
+
+ /**
+ *
+ * @return true if object is registered
+ */
+ public boolean isRegistered() {
+ return (registerable) ? (vorbaId != null) : false;
+ }
+
+ /**
+ * Method to get fixed reference for the object in the vamsas document.
+ *
+ * @returns null if object 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()) == null)
+ return null; // Factory not valid.
+ else {
+ this.setInstanceIdField();
+ return vorbaId;
+ }
}
- /**
- * for use by Vorba agent to reflect state of
- * vamsas object to client application.
- * @param __stored_in_document The __stored_in_document to set.
- */
- protected void set__stored_in_document(boolean __stored_in_document) {
- this.__stored_in_document = __stored_in_document;
+ 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 object 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 object to client
+ * application.
+ * Setting stored_in_document on a registerable object without a
+ * vorbaId will mean is will *never* get a vorbaId and
+ * horrible things will happen.
+ * @param __stored_in_document true if object 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 object was last checked
+ * against a IClientDocument generated by the object's parent IClient
+ * instance.
+ *
+ * @return Returns the __last_hash.
+ */
+ public long get__last_hash() {
+ return __last_hash;
+ }
+
+ /**
+ * @return true if object can have a vorbaId
+ */
+ public boolean isRegisterable() {
+ return registerable;
+ }
+
+ /**
+ * Called by __testInstanceForidField and the post-unmarshalling handler
+ * to indicate if object 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 object
+ * 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".
+ */
+ protected void __ensure_instance_ids() {
+ if (__vorba==null)
+ throw new Error("Improperly intialised org.vamsas.client.object - 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 object - 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;
}
- /**
- * __last_hash is the hash value computed
- * when the object was last checked against
- * a IClientDocument generated by the
- * object's parent IClient instance.
- * @return Returns the __last_hash.
- */
- public long get__last_hash() {
- return __last_hash;
+ FieldDescriptor fields[] = descimpl.getFields();
+ for (int i=0,j=fields.length; i<j; i++) {
+ Class type= fields[i].getFieldType();
+ if (type.isArray()) {
+ if (object[].class.isAssignableFrom(type)) {
+ try {
+ Object val = fields[i].getHandler().getValue(this);
+ if (val!=null) {
+ object vals[] = (object[]) val;
+ for (int k=0, l=vals.length; k<l; k++) {
+ if (vals[k].__vorba==null)
+ vals[k].__vorba = __vorba; // propagate IVorbaIdFactory
+ vals[k].__ensure_instance_ids();
+ }
+ }
+ }
+ catch (Exception e) {
+ log.error("Client error - could not access array "+type.getName()+" in "+this.getClass().getName(), e);
+ }
+ }
+ } else
+ if (object.class.isAssignableFrom(type)) {
+ try {
+ FieldHandler fh = fields[i].getHandler();
+ object rf = null;
+ if (fh != null) {
+ Object fval = fh.getValue(this);
+ if (fval!=null) {
+ if (fval.getClass().isArray()) {
+ //if (object[].class.isAssignableFrom(type)) {
+ try {
+ object vals[] = (object[]) fval;
+ for (int k=0, l=vals.length; k<l; k++) {
+ if (vals[k].__vorba==null)
+ vals[k].__vorba = __vorba; // propagate IVorbaIdFactory
+ vals[k].__ensure_instance_ids();
+ }
+ }
+ catch (Exception e) {
+ log.error("Client error - could not access (fhval)array "+type.getName()+" in "+this.getClass().getName(), e);
+ }
+ //}
+ } else {
+ rf = (object) fval;
+ log.debug("Got value for "+fields[i].getFieldName());
+ }
+ }
+ } else {
+ // fuck around, fuck around, jump up jump up and get down! */
+ Object o = fields[i].getClassDescriptor();
+ if (o!=null) {
+ // XMLClassDescriptorImpl fclasdes = (XMLClassDescriptorImpl) o;
+ String methname = "get"+fields[i].getFieldName();
+ Method fgetmeth = this.getClass().getMethod(methname,null);
+ if (fgetmeth!=null) {
+ Object fval = fgetmeth.invoke(this,null);
+ if (fval!=null)
+ rf = (object) fval;
+ } else {
+ log.warn("Couldn't find "+this.getClass().getName()+"."+methname);
+ }
+ }
+ }
+ if (rf!=null) {
+ if (rf.__vorba==null)
+ rf.__vorba = __vorba; // propagate IVorbaIdFactory
+ rf.__ensure_instance_ids();
+ }
+ }
+ catch (Exception e) {
+ log.error("Client error - could not access "+type.getName()+" in "+this.getClass().getName(), e);
+ }
+ }
}
+
+ }
}