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 * memory of the last doHash() value computed for the Vobject
36 protected long __last_hash = 0;
38 * set by testInstanceForIdField() if Vobject should have a VorbaId
40 protected boolean registerable = false;
43 * reference to containing object for this Vobject.
45 protected Vobject V_parent=null;
47 * unique id for all vamsas objects allows unambiguous referencing to any
48 * Vobject in the vamsas document
50 protected VorbaId vorbaId = null;
53 * the source of unique VorbaIds.
55 protected IVorbaIdFactory __vorba = null;
58 * @see java.lang.Object#finalize()
60 protected void finalize() throws Throwable {
72 testInstanceForIdField();
76 * set the isRegisterable flag based on the presence of a 'private String _id' field in
77 * the reflected class instance.
79 private void testInstanceForIdField() {
80 // TODO: decide if 'id' is an appropriate reserved attribute name for the VorbaId
81 // look for the id field in all castor classes (should be an NCName string)
83 Class thisclass=this.getClass();
84 setRegisterable(false);
85 while (!thisclass.equals(Vobject.class)) {
87 java.lang.reflect.Field fd = thisclass.getDeclaredField("_id");
88 if (String.class.isAssignableFrom(fd.getType())) {
89 this.setRegisterable(true);
92 } catch (SecurityException e) {
93 log.error("Unexpected Security Exception whilst finding id fields to set!",e);
94 } catch (NoSuchFieldException e) {
95 thisclass=thisclass.getSuperclass();
99 // boolean __testedInstance=false;
101 * update the Vobject instance's _id field, based on the contents of the
102 * VorbaId. Only call this if you mean to do it!
104 protected void setInstanceIdField() {
105 /*if (!registerable && !__testedInstance) {
106 testInstanceForIdField();
107 __testedInstance=true;
112 Method fd = this.getClass().getMethod("setId", new Class[] { String.class });
113 fd.invoke((Object) this, new Object[] {new String(this.getVorbaId().id)});
114 log.debug(this.getClass().getName()+" called setInstanceIdField!");
115 } catch (InvocationTargetException e) {
116 log.error("SourceGeneration of "
117 + this.getClass().toString()
118 + "\n has resulted in an inaccessible 'setId' method!\nCannot set ID from the vorbaId Vobject.", e);
120 catch (IllegalAccessException 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);
124 } catch (SecurityException e) {
125 log.error("Security access violation for "+this.getClass().toString(),e);
126 } catch (NoSuchMethodException e) {
127 log.warn(this.getClass().toString()+" was erroneously marked as a Vorba Vobject class (Implementation error?)");
128 this.setRegisterable(false);
131 System.err.println("Client error. Trying to setInstanceIdField on a "
132 + this.getClass().toString() + " (which cannot be given a vorbaId)");
137 * calculate a hash for the Vobject with all housekeeping fields at standard
138 * values. (isRegisterable is an immutable attribute property)
139 * TODO: decide if __stored_in_document should be included in the hash or not.
140 * @return true if new hash different to last hash
142 synchronized protected boolean doHash() {
143 long __old_hash = __last_hash;
145 Vobject _V_parent=V_parent;
147 VorbaId thisid = vorbaId;
148 IVorbaIdFactory factory = __vorba;
149 boolean stored = __stored_in_document;
152 __last_hash = this.hashCode();
155 __stored_in_document = stored;
157 return (__old_hash==0) || (__old_hash == __last_hash);
161 * TODO: combine two versions of the same collection Vobject to resolve
162 * asynchronous updates to the same vamsas Vobject Merges two vamsas objects,
163 * one of which is a later version of the earlier (ie they have the same
164 * vorbaId but one is a later version recently read from the vamsasDocument
169 protected boolean merge(Vobject laterCopy) {
170 log.warn(this.getClass().getName()+".merge() not implemented.");
176 * @return true if Vobject is registered
178 public boolean isRegistered() {
179 return (registerable) ? (vorbaId != null) : false;
183 * Method to get fixed reference for the Vobject in the vamsas document.
185 * @returns null if Vobject is neither registered or not associated with a
186 * properly instantiated VorbaIdFactory.
188 public VorbaId getVorbaId() {
189 /*if (!registerable && !__testedInstance) {
190 testInstanceForIdField();
191 __testedInstance=true;
193 if (registerable && vorbaId == null) {
194 // Try to use the associated factory.
196 if ((vorbaId = __vorba.makeVorbaId(this)) == null)
197 return null; // Factory not valid.
199 this.setInstanceIdField();
207 * used by the IClient implementation to generate unique Id based on client
208 * applications current namespace.
210 protected void setVorbaId(VorbaId newid) {
215 * @return true if Vobject is present in Vamsas Document.
217 public boolean is__stored_in_document() {
218 return __stored_in_document;
222 * for use by Vorba agent to reflect state of vamsas Vobject to client
224 * Setting stored_in_document on a registerable Vobject without a
225 * vorbaId will mean is will *never* get a vorbaId and
226 * horrible things will happen.
227 * @param __stored_in_document true if Vobject has been marshalled into current document.
229 protected void set__stored_in_document(boolean __stored_in_document) {
230 this.__stored_in_document = __stored_in_document;
234 * __last_hash is the hash value computed when the Vobject was last checked
235 * against a IClientDocument generated by the Vobject's parent IClient
238 * @return Returns the __last_hash.
240 public long get__last_hash() {
245 * @return true if Vobject can have a vorbaId
247 public boolean isRegisterable() {
252 * Called by __testInstanceForidField and the post-unmarshalling handler
253 * to indicate if Vobject will have a vorbaId.
254 * @param registerable
256 protected void setRegisterable(boolean registerable) {
257 this.registerable = registerable;
260 * ensure's internal id field corresponds to vorbaId and
261 * cascade through all fields referring to an instance of Vobject
262 * calling the same method on them.
263 * TODO: LATER: properly apply castors own field mechanisms to get at accessors
264 * TODO: FIX CYCLIC __ensure+instance_ids
265 * Implementation note for the todo:
266 * this works like a depth-first search over all vamsas objects in an vamsasDocument.
267 * The doHash() function is used as the 'visited' flag -
268 * this *is not* a valid heuristic, although it will work "most of the time".
269 * TODO: LATER? Add another method for setDefaultProvenanceField (in the spirit of setInstanceIdField) using the info from the __vorba.getClient/User/Session methods
271 protected void __ensure_instance_ids() {
273 throw new Error("Improperly intialised org.vamsas.client.Vobject - no VorbaFactory given.");
274 log.debug("doing "+this.getClass()+".__ensure_instance_ids()");
275 if (!__stored_in_document && registerable)
276 setInstanceIdField();
278 return; // nothing has changed in this Vobject - probably visited it before.
279 Class descriptor = null;
280 XMLClassDescriptorImpl descimpl = null;
282 // castor descriptor resolver magic
283 descriptor = this.getClass().getClassLoader().loadClass(this.getClass().getName()+"Descriptor");
284 descimpl = (XMLClassDescriptorImpl) descriptor.getConstructor((Class[])null).newInstance((Object[])null);
285 } catch (Exception e) {
286 log.fatal("Source Generation Error!: Couldn't resolve descriptor for "
287 +this.getClass().getName()
288 +" was 'generate descriptors' set for castorbuilder.properties?");
291 FieldDescriptor fields[] = descimpl.getFields();
292 for (int i=0,j=fields.length; i<j; i++) {
293 Class type= fields[i].getFieldType();
294 if (type.isArray()) {
295 if (Vobject[].class.isAssignableFrom(type)) {
297 Object val = fields[i].getHandler().getValue(this);
299 Vobject vals[] = (Vobject[]) val;
300 for (int k=0, l=vals.length; k<l; k++) {
301 if (vals[k].__vorba==null)
302 vals[k].__vorba = __vorba; // propagate IVorbaIdFactory
303 if (vals[k].V_parent==null)
304 vals[k].V_parent=this; // propagate parent reference to this element.
305 vals[k].__ensure_instance_ids();
309 catch (Exception e) {
310 log.error("Client error - could not access array "+type.getName()+" in "+this.getClass().getName(), e);
314 if (Vobject.class.isAssignableFrom(type)) {
316 FieldHandler fh = fields[i].getHandler();
319 Object fval = fh.getValue(this);
321 if (fval.getClass().isArray()) {
322 //if (Vobject[].class.isAssignableFrom(type)) {
324 Vobject vals[] = (Vobject[]) fval;
325 for (int k=0, l=vals.length; k<l; k++) {
326 if (vals[k].__vorba==null)
327 vals[k].__vorba = __vorba; // propagate IVorbaIdFactory
328 if (vals[k].V_parent==null)
329 vals[k].V_parent=this; // propagate parent reference to this field object
330 vals[k].__ensure_instance_ids();
333 catch (Exception e) {
334 log.error("Client error - could not access (fhval)array "+type.getName()+" in "+this.getClass().getName(), e);
339 log.debug("Got value for "+fields[i].getFieldName());
343 // castor's mechanism doesn't work for this object... so...*/
344 // fuck around, fuck around, jump up jump up and get down! */
345 Object o = fields[i].getClassDescriptor();
347 // XMLClassDescriptorImpl fclasdes = (XMLClassDescriptorImpl) o;
348 String methname = "get"+fields[i].getFieldName();
349 Method fgetmeth = this.getClass().getMethod(methname,(Class[])null);
350 if (fgetmeth!=null) {
351 Object fval = fgetmeth.invoke(this,(Object[])null);
355 log.warn("Couldn't find "+this.getClass().getName()+"."+methname);
360 if (rf.__vorba==null)
361 rf.__vorba = __vorba; // propagate IVorbaIdFactory
362 if (rf.V_parent==null)
363 rf.V_parent=this; // propagate parent reference
364 rf.__ensure_instance_ids();
367 catch (Exception e) {
368 log.error("Client error - could not access "+type.getName()+" in "+this.getClass().getName(), e);
376 * @return the __parent
378 public Vobject getV_parent() {
383 * @param __parent the __parent to set
385 protected void setV_parent(Vobject V_parent) {
386 this.V_parent = V_parent;