4 package org.vamsas.client;
6 import java.lang.reflect.Field;
7 import java.lang.reflect.InvocationTargetException;
8 import java.lang.reflect.Method;
9 import java.util.Iterator;
11 import org.apache.commons.logging.Log;
12 import org.apache.commons.logging.LogFactory;
13 import org.exolab.castor.mapping.FieldDescriptor;
14 import org.exolab.castor.mapping.FieldHandler;
15 import org.exolab.castor.xml.util.XMLClassDescriptorImpl;
16 import org.vamsas.test.simpleclient.VamsasArchive;
19 * Base class for all Vamsas objects extracted from an IClientDocument. An
20 * Vobject maybe registered or unregistered.
25 public abstract class Vobject {
26 static Log log = LogFactory.getLog(Vobject.class);
29 * true if Vobject was stored in a vamsas Document or has been retrieved from it
31 protected boolean __stored_in_document = false;
33 * true if Vobject was updated since the vamsas library last read a Vobj with the same VorbaId from a document.
35 protected boolean __updated_since_last_read = false;
37 * memory of the last doHash() value computed for the Vobject
40 protected int __last_hash = 0;
42 * set by testInstanceForIdField() if Vobject should have a VorbaId
44 protected boolean registerable = false;
45 protected boolean __visited = false;
47 * reference to containing object for this Vobject.
49 protected Vobject V_parent=null;
51 * unique id for all vamsas objects allows unambiguous referencing to any
52 * Vobject in the vamsas document
54 protected VorbaId vorbaId = null;
57 * the source of unique VorbaIds.
59 protected IVorbaIdFactory __vorba = null;
62 * @see java.lang.Object#finalize()
64 protected void finalize() throws Throwable {
76 testInstanceForIdField();
78 java.lang.reflect.Field ___id_field=null; // set to ease pain of reflection
80 * set the isRegisterable flag based on the presence of a 'private String _id' field in
81 * the reflected class instance.
83 private void testInstanceForIdField() {
84 // TODO: decide if 'id' is an appropriate reserved attribute name for the VorbaId
85 // look for the id field in all castor classes (should be an NCName string)
87 Class thisclass=this.getClass();
88 setRegisterable(false);
89 while (!thisclass.equals(Vobject.class)) {
91 java.lang.reflect.Field fd = thisclass.getDeclaredField("_id");
92 if (String.class.isAssignableFrom(fd.getType())) {
94 this.setRegisterable(true);
97 } catch (SecurityException e) {
98 log.error("Unexpected Security Exception whilst finding id fields to set!",e);
99 } catch (NoSuchFieldException e) {
100 thisclass=thisclass.getSuperclass();
104 // boolean __testedInstance=false;
106 * update the Vobject instance's _id field, based on the contents of the
107 * VorbaId. Only call this if you mean to do it!
109 protected void setInstanceIdField() {
110 /*if (!registerable && !__testedInstance) {
111 testInstanceForIdField();
112 __testedInstance=true;
117 Method fd = this.getClass().getMethod("setId", new Class[] { String.class });
118 fd.invoke((Object) this, new Object[] {new String(this.getVorbaId().id)});
119 log.debug(this.getClass().getName()+" called setInstanceIdField!");
120 } catch (InvocationTargetException e) {
121 log.error("SourceGeneration of "
122 + this.getClass().toString()
123 + "\n has resulted in an inaccessible 'setId' method!\nCannot set ID from the vorbaId Vobject.", e);
125 catch (IllegalAccessException e) {
126 log.error("SourceGeneration of "
127 + this.getClass().toString()
128 + "\n has resulted in an inaccessible 'setId' method!\nCannot set ID from the vorbaId Vobject.", e);
129 } catch (SecurityException e) {
130 log.error("Security access violation for "+this.getClass().toString(),e);
131 } catch (NoSuchMethodException e) {
132 log.warn(this.getClass().toString()+" was erroneously marked as a Vorba Vobject class (Implementation error?)");
133 this.setRegisterable(false);
136 System.err.println("Client error. Trying to setInstanceIdField on a "
137 + this.getClass().toString() + " (which cannot be given a vorbaId)");
141 protected String __getInstanceIdField() {
142 /*if (!registerable && !__testedInstance) {
143 testInstanceForIdField();
144 __testedInstance=true;
149 Method fd = this.getClass().getMethod("getId", (Class[]) null);
150 Object idstring = fd.invoke((Object) this, (Object[]) null);
151 log.debug(this.getClass().getName()+" called getInstanceIdField!");
152 if (idstring!=null && idstring instanceof String) {
153 if (((String) idstring).length()>0)
154 return (String) idstring;
156 } catch (InvocationTargetException e) {
157 log.error("SourceGeneration of "
158 + this.getClass().toString()
159 + "\n has resulted in an inaccessible 'getId' method!\nCannot set ID from the vorbaId Vobject.", e);
161 catch (IllegalAccessException e) {
162 log.error("SourceGeneration of "
163 + this.getClass().toString()
164 + "\n has resulted in an inaccessible 'getId' method!\nCannot set ID from the vorbaId Vobject.", e);
165 } catch (SecurityException e) {
166 log.error("Security access violation for "+this.getClass().toString(),e);
167 } catch (NoSuchMethodException e) {
168 log.warn(this.getClass().toString()+" was erroneously marked as a Vorba Vobject class (Implementation error?)");
169 this.setRegisterable(false);
172 System.err.println("Client error. Trying to getInstanceIdField on a "
173 + this.getClass().toString() + " (which cannot be given a vorbaId)");
179 * calculate a hash for the Vobject with all housekeeping fields at standard
180 * values. (isRegisterable is an immutable attribute property)
181 * TODO: decide if __stored_in_document should be included in the hash or not.
182 * @return true if new hash different to last hash
184 synchronized protected boolean doHash() {
185 long __old_hash = __last_hash;
187 Vobject _V_parent=V_parent;
189 VorbaId thisid = vorbaId;
190 IVorbaIdFactory factory = __vorba;
191 boolean stored = __stored_in_document;
192 boolean updated = __updated_since_last_read;
193 boolean visited = __visited;
194 java.lang.reflect.Field idfield = ___id_field;
196 __updated_since_last_read=false;
201 __last_hash = this.hashCode();
202 // reset houseskeeping variables
206 __stored_in_document = stored;
207 __updated_since_last_read=updated;
210 return (__old_hash==0) || (__old_hash == __last_hash);
214 * TODO: combine two versions of the same collection Vobject to resolve
215 * asynchronous updates to the same vamsas Vobject Merges two vamsas objects,
216 * one of which is a later version of the earlier (ie they have the same
217 * vorbaId but one is a later version recently read from the vamsasDocument
222 protected boolean merge(Vobject laterCopy) {
223 log.warn(this.getClass().getName()+".merge() not implemented.");
229 * @return true if Vobject is registered
231 public boolean isRegistered() {
232 return (registerable) ? (vorbaId != null) : false;
236 * Method to get fixed reference for the Vobject in the vamsas document.
238 * @returns null if Vobject is neither registered or not associated with a
239 * properly instantiated VorbaIdFactory.
241 public VorbaId getVorbaId() {
242 if (registerable && vorbaId == null) {
243 if (this.__stored_in_document) {
245 vorbaId=org.vamsas.client.VorbaId.newId(this.__getInstanceIdField());
247 // Try to use the associated factory.
249 if ((vorbaId = __vorba.makeVorbaId(this)) == null)
250 return null; // Factory not valid.
252 this.setInstanceIdField();
260 * used by the IClient implementation to generate unique Id based on client
261 * applications current namespace.
263 protected void setVorbaId(VorbaId newid) {
268 * @return true if Vobject is present in Vamsas Document.
270 public boolean is__stored_in_document() {
271 return __stored_in_document;
275 * @return true if this object has been updated in the currently stored document since the last time a Vobject with the same ID was read from a Vamsas Document
277 public boolean is__updated_since_last_read() {
278 return __updated_since_last_read;
282 * Set internal flag to indicate this object was updated since the last document read
283 * @param __updated_since_last_read the __updated_since_last_read to set
285 protected void set__updated_since_last_read(boolean __updated_since_last_read) {
286 this.__updated_since_last_read = __updated_since_last_read;
290 * for use by Vorba agent to reflect state of vamsas Vobject to client
292 * Setting stored_in_document on a registerable Vobject without a
293 * vorbaId will mean is will *never* get a vorbaId and
294 * horrible things will happen.
295 * @param __stored_in_document true if Vobject has been marshalled into current document.
297 protected void set__stored_in_document(boolean __stored_in_document) {
298 this.__stored_in_document = __stored_in_document;
302 * __last_hash is the hash value computed when the Vobject was last checked
303 * against a IClientDocument generated by the Vobject's parent IClient
306 * @return Returns the __last_hash.
308 public int get__last_hash() {
313 * @return true if Vobject can have a vorbaId
315 public boolean isRegisterable() {
320 * Called by __testInstanceForidField and the post-unmarshalling handler
321 * to indicate if Vobject will have a vorbaId.
322 * @param registerable
324 protected void setRegisterable(boolean registerable) {
325 this.registerable = registerable;
328 * ensure's internal id field corresponds to vorbaId and
329 * cascade through all fields referring to an instance of Vobject
330 * calling the same method on them.
331 * TODO: LATER: properly apply castors own field mechanisms to get at accessors
332 * TODO: FIX CYCLIC __ensure+instance_ids
333 * Implementation note for the todo:
334 * this works like a depth-first search over all vamsas objects in an vamsasDocument.
335 * __visited is the visited flag, any Vobj who's flag is of a different parity
336 * to the visited argument will be recursed on.
337 * note - the doHash() function used to be used as the 'visited' flag -
338 * this *is not* a valid heuristic, although it will work "most of the time".
339 * TODO: LATER? Add another method for setDefaultProvenanceField (in the spirit of setInstanceIdField) using the info from the __vorba.getClient/User/Session methods
341 protected void __ensure_instance_ids() {
342 __ensure_instance_ids(!__visited);
344 protected void __ensure_instance_ids(boolean visited) {
346 throw new Error("Improperly intialised org.vamsas.client.Vobject - no VorbaFactory given.");
347 log.debug("doing "+this.getClass()+".__ensure_instance_ids()");
348 if (!__stored_in_document && registerable)
349 setInstanceIdField();
350 if (__visited==visited)
353 __vorba.updateHashValue(this);
355 Class descriptor = null;
356 XMLClassDescriptorImpl descimpl = null;
358 // castor descriptor resolver magic
359 descriptor = this.getClass().getClassLoader().loadClass(this.getClass().getName()+"Descriptor");
360 descimpl = (XMLClassDescriptorImpl) descriptor.getConstructor((Class[])null).newInstance((Object[])null);
361 } catch (Exception e) {
362 log.fatal("Source Generation Error!: Couldn't resolve descriptor for "
363 +this.getClass().getName()
364 +" was 'generate descriptors' set for castorbuilder.properties?");
367 FieldDescriptor fields[] = descimpl.getFields();
368 for (int i=0,j=fields.length; i<j; i++) {
369 Class type= fields[i].getFieldType();
370 if (type.isArray()) {
371 if (Vobject[].class.isAssignableFrom(type)) {
373 Object val = fields[i].getHandler().getValue(this);
375 Vobject vals[] = (Vobject[]) val;
376 for (int k=0, l=vals.length; k<l; k++) {
377 if (vals[k].__vorba==null)
378 vals[k].__vorba = __vorba; // propagate IVorbaIdFactory
379 if (vals[k].V_parent==null)
380 vals[k].V_parent=this; // propagate parent reference to this element.
381 vals[k].__ensure_instance_ids(visited);
385 catch (Exception e) {
386 log.error("Client error - could not access array "+type.getName()+" in "+this.getClass().getName(), e);
390 if (Vobject.class.isAssignableFrom(type)) {
392 FieldHandler fh = fields[i].getHandler();
395 Object fval = fh.getValue(this);
397 if (fval.getClass().isArray()) {
398 //if (Vobject[].class.isAssignableFrom(type)) {
400 Vobject vals[] = (Vobject[]) fval;
401 for (int k=0, l=vals.length; k<l; k++) {
402 if (vals[k].__vorba==null)
403 vals[k].__vorba = __vorba; // propagate IVorbaIdFactory
404 if (vals[k].V_parent==null)
405 vals[k].V_parent=this; // propagate parent reference to this field object
406 vals[k].__ensure_instance_ids(visited);
409 catch (Exception e) {
410 log.error("Client error - could not access (fhval)array "+type.getName()+" in "+this.getClass().getName(), e);
415 log.debug("Got value for "+fields[i].getFieldName());
419 // castor's mechanism doesn't work for this object... so...*/
420 // fuck around, fuck around, jump up jump up and get down! */
421 Object o = fields[i].getClassDescriptor();
423 // XMLClassDescriptorImpl fclasdes = (XMLClassDescriptorImpl) o;
424 String methname = "get"+fields[i].getFieldName();
425 Method fgetmeth = this.getClass().getMethod(methname,(Class[])null);
426 if (fgetmeth!=null) {
427 Object fval = fgetmeth.invoke(this,(Object[])null);
431 log.warn("Couldn't find "+this.getClass().getName()+"."+methname);
436 if (rf.__vorba==null)
437 rf.__vorba = __vorba; // propagate IVorbaIdFactory
438 if (rf.V_parent==null)
439 rf.V_parent=this; // propagate parent reference
440 rf.__ensure_instance_ids(visited);
443 catch (Exception e) {
444 log.error("Client error - could not access "+type.getName()+" in "+this.getClass().getName(), e);
452 * @return the __parent
454 public Vobject getV_parent() {
459 * @param __parent the __parent to set
461 protected void setV_parent(Vobject V_parent) {
462 this.V_parent = V_parent;