6b1d7fca95ae7f0cb6c57297eb40af39d757d0c5
[vamsas.git] / src / org / vamsas / client / simpleclient / ClientDocument.java
1 /*
2  *
3  */
4 package org.vamsas.client.simpleclient;
5
6 import java.util.Vector;
7
8 import org.apache.commons.logging.Log;
9 import org.apache.commons.logging.LogFactory;
10 import org.vamsas.client.ClientHandle;
11 import org.vamsas.client.IClientAppdata;
12 import org.vamsas.client.IClientDocument;
13 import org.vamsas.client.Vobject;
14 import org.vamsas.client.VorbaId;
15 import org.vamsas.objects.core.ApplicationData;
16 import org.vamsas.objects.core.VAMSAS;
17 import org.vamsas.objects.core.VamsasDocument;
18 import org.vamsas.objects.utils.AppDataReference;
19 import org.vamsas.test.objects.Core;
20
21 /**
22  * Maintains a collection of vamsas objects, appdatas and states, and provides api for a SimpleClient's client.
23  * @author jimp 
24  */
25 public class ClientDocument extends org.vamsas.client.ClientDocument implements IClientDocument {
26   private static Log log = LogFactory.getLog(ClientDocument.class);
27   private VamsasDocument doc;
28   protected SimpleClient sclient;
29   protected VamsasArchive archive = null;
30   /**
31    * indicate if new data has been incorporated
32    */
33   private boolean isModified = false;
34   /**
35    * Public method for internal use by SimpleClient.
36    * @return true if document has been modified.
37    */
38   public boolean isModified() {
39     return isModified;
40   }
41   private java.util.Hashtable objrefs=null;
42   /**
43    *
44    *  prepare Application-side dataset from the vamsas Document archive
45    * @param doc - the dataset
46    * @param docHandler - the sessionFile IO handler
47    * @param Factory - the source of current and new vorbaIds
48    * @param sclient - the simpleclient instance
49    */
50   protected ClientDocument(VamsasDocument doc, VamsasArchive docHandler, IdFactory Factory, SimpleClient sclient) {
51     super(Factory.getVorbaIdHash(), Factory);
52     
53     /**
54      * prepare Application-side dataset from the vamsas Document archive
55      */
56     this.sclient = sclient;
57     archive = docHandler;
58     this.doc = doc;
59     objrefs = Factory.getVorbaIdHash();
60   }
61   
62   /*
63    * (non-Javadoc)
64    * 
65    * @see org.vamsas.client.IClientDocument#getObject(org.vamsas.client.VorbaId)
66    */
67   public Vobject getObject(VorbaId id) {
68     if (objrefs.containsKey(id))
69       return (Vobject) objrefs.get(id);
70     log.debug("Returning null Vobject reference for id "+id.getId());
71     return null;
72   }
73   
74   /*
75    * (non-Javadoc)
76    * 
77    * @see org.vamsas.client.IClientDocument#getObjects(org.vamsas.client.VorbaId[])
78    */
79   public Vobject[] getObjects(VorbaId[] ids) {
80     Vobject[] vo = new Vobject[ids.length];
81     for (int i=0,j=ids.length; i<j;i++) 
82       if (objrefs.containsKey(ids[i]))
83         vo[i] = (Vobject) objrefs.get(ids[i]);
84       else
85         log.debug("Returning null Vobject reference for id "+ids[i].getId());
86     return vo;
87   }
88   /**
89    * internal reference to single copy of Document Roots array
90    */
91   private VAMSAS[] _VamsasRoots=null;
92   /*
93    * (non-Javadoc)
94    * LATER: currently there is only one Vector of roots ever passed to client - decide if this is correct (means this is not thread safe and may behave unexpectedly)
95    * @see org.vamsas.client.IClientDocument#getVamsasRoots()
96    */
97   public VAMSAS[] getVamsasRoots() {
98     if (_VamsasRoots!=null)
99       return _VamsasRoots;
100     VAMSAS[] roots = doc.getVAMSAS();
101     if (roots == null) {
102       // Make a new one to return to client : TODO: Decide if this is correct
103       _VamsasRoots = new VAMSAS[] { new VAMSAS() };
104     } else {
105       _VamsasRoots = new VAMSAS[roots.length];
106       for (int r=0;r<roots.length; r++)
107         _VamsasRoots[r] = roots[r];
108     }
109     return _VamsasRoots;
110   }
111   
112   private int _contains(VAMSAS root, VAMSAS[] docRoots) {
113     if (root==null)
114       return -1;
115     if (docRoots==null || docRoots.length==0)
116       return -1;
117     String r_id = root.getId();
118     for (int i=0,j=docRoots.length; i<j; i++)
119       if (docRoots[i]==root || (docRoots[i]!=null && docRoots[i].getId().equals(r_id)))
120         return i;
121     return -1;
122   }
123 /**
124  * verify that newr version is really an intact version of the 
125  * @param newVersion (may be modified)
126  * @param oldVersion 
127  * @return true if newVersion is a valid root that preserves original references
128  */
129   private boolean isValidUpdate(VAMSAS newVersion, final VAMSAS oldVersion) {
130     // ideal - this cascades down the two structures, ensuring that all ID'd objects in one are present in the other.
131     if (oldVersion==newVersion) {
132       // may be a virgin root element.
133       if (!newVersion.isRegistered())
134         _registerObject(newVersion);
135       // Should retrieve original version and compare - unless local hashes can be used to determine if resultSet has been truncated.
136       // just do internal validation for moment.
137       if (newVersion.isValid())
138         return true;
139       return false;
140     } else {
141       // redundant ? if (oldVersion.is__stored_in_document())
142       if (!newVersion.isRegistered())
143         _registerObject(newVersion);
144       if (newVersion.isValid())
145         return true;
146     }
147     return false;
148     /**
149      * LATER isValidUpdate : Ideally. we efficiently walk down, comparing hashes, to deal with merging and verifying provenance for objects
150     
151     // extract root objects
152     if (newroots != null) {
153       // check newroots for objects that were present in the old document
154       // check to see if the 'old' objects have been modified
155       // if they have ? we overwrite them with their new version, ensuring that
156       // provenance is updated.
157       // if they haven't ? do nothing ?
158       
159       for (int i = 0, k = newroots.length; i < k; i++) {
160         if (newroots[i].isRegistered()) {
161           // easy - just check if anything has changed and do provenance
162           Vobject oldversion = getObject(newroots[i].getVorbaId());
163           if (oldversion instanceof VAMSAS) {
164             // LATER: appropriate merging behaviour when two clients have improperly modified the same Vobject independently.
165             if (newroots[i].get__last_hash() != newroots[i].hashCode()) {
166               // client has modified this Vobject since last retrieval.
167               if (newroots[i].get__last_hash() != oldversion.get__last_hash()) {
168                 // Vobject has been modified by another client since this
169                 // client's
170                 // last access to document.
171               }
172             }
173           } else {
174             throw new Error(
175                 "SimpleClient error when using setVamsasRoots : The vorbaId for Vobject "
176                 + i
177                 + " does not refer to an Vobject of type VAMSAS in the current document!");
178           }
179         } else {
180           if (!newroots[i].is__stored_in_document()) {
181             // check if Vobject is modified
182             if (newroots[i].get__last_hash() != newroots[i].hashCode()) {
183               // it is - so we add newroots[i] as a new Vobject, with updated
184               // provenance.
185             } else {
186               // do nothing
187               newroots[i] = null;
188             }
189           } else {
190             // just add newroots[i] as a new Vobject in the document
191             // - with appropriate provenance.
192           }
193         }
194       }*/
195   }
196   /**
197    * merge old and new root vectors
198    * @param newr This array may be written to
199    * @param original
200    * @param the client document (usually this) which this root set belongs to.
201    * @return merged vector of vamsas roots
202    */
203   private VAMSAS[] _combineRoots(VAMSAS[] newr, final VAMSAS[] original, ClientDocument modflag) {
204     Vector rts = new Vector();
205     boolean modified=false;
206     for (int i=0,j=original.length; i<j; i++) {
207       int k = _contains(original[i], newr);
208       if (k>-1) {
209         if (isValidUpdate(newr[k], original[i])) {
210           modified=true;
211           rts.add(newr[k]);
212           newr[k]=null;
213         } else {
214           // LATER: try harder to merge ducument roots.
215           log.warn("Couldn't merge new VAMSAS root "+newr[k].getId());
216           newr[k] = null; // LATER: this means we ignore mangled roots. NOT GOOD
217         }
218       } else {
219         // add in order.
220         rts.add(original[i]);
221       }
222     }
223     // add remaining (new) roots
224     for (int i=0,j=newr.length; i<j; i++) {
225       if (newr[i]!=null) {
226         rts.add(newr[i]);
227         modified=true;
228       }
229     }
230     newr = new VAMSAS[rts.size()];
231     for (int i=0,j=rts.size(); i<j; i++)
232       newr[i] = (VAMSAS) rts.get(i);
233     if (modflag!=null)
234       modflag.isModified = modified;
235     return newr;
236   }
237   
238   /**
239    * update the document with new roots.
240    * LATER: decide: this affects the next call to getVamsasRoots()
241    * @see org.vamsas.IClientDocument.setVamsasRoots
242    */
243   public void setVamsasRoots(VAMSAS[] newroots) {
244     VAMSAS[] newr;
245     if (newroots==null) {
246       log.debug("setVamsasRoots(null) - do nothing.");
247       return;
248     }
249     // are we dealing with same array ?
250     if (_VamsasRoots!=newroots) {
251       // merge roots into local version.
252       newr = new VAMSAS[newroots.length];
253       for (int i=0;i<newr.length;i++)
254         newr[i] = newroots[i];
255       newr=_combineRoots(newr,_VamsasRoots,this);
256     } else {
257       newr = new VAMSAS[_VamsasRoots.length];
258       for (int i=0;i<newr.length;i++)
259         newr[i]=_VamsasRoots[i];
260     }
261     //  actually compare with document root set for final combination (to ensure nothing is lost)
262     _VamsasRoots = _combineRoots(newr, doc.getVAMSAS(), this); 
263   }
264   
265   
266   /* (non-Javadoc)
267    * LATER: decide: this affects the next call to getVamsasRoots()
268    * @see org.vamsas.client.IClientDocument#addVamsasRoot(org.vamsas.objects.core.VAMSAS)
269    */
270   public void addVamsasRoot(VAMSAS newroot) {
271     VAMSAS[] newroots = _combineRoots(new VAMSAS[] {newroot}, _VamsasRoots, this);
272     _VamsasRoots = newroots;  
273   }
274   
275   /*
276    * (non-Javadoc)
277    * 
278    * @see org.vamsas.client.IClientDocument#registerObjects(org.vamsas.client.Vobject[])
279    */
280   public VorbaId[] registerObjects(Vobject[] unregistered) {
281     if (unregistered!=null) {
282       VorbaId ids[] = new VorbaId[unregistered.length];
283       for (int i=0,k=unregistered.length; i<k; i++)
284         if (unregistered[i]!=null) {
285           log.warn("Null Vobject passed to registerObject[] at position "+i);
286           return null;
287         } else {
288           ids[i]=registerObject(unregistered[i]);
289         }
290       log.debug("Registered "+unregistered.length+" objects - total of "+objrefs.size()+" ids.");
291       return ids;
292     }
293     return null;
294   }
295   /* (non-Javadoc)
296    * @see org.vamsas.client.IClientDocument#registerObject(org.vamsas.client.Vobject)
297    */
298   public VorbaId registerObject(Vobject unregistered) {
299     if (unregistered!=null) {
300       VorbaId id = _registerObject(unregistered);
301       log.debug("Registered object - total of "+objrefs.size()+" ids.");
302       return id;
303     }
304     log.warn("Null Vobject passed to registerObject.");
305     return null;
306   }
307   /**
308    * IClientAppdata instance - if it exists.
309    */
310   SimpleClientAppdata scappd = null;
311   /* (non-Javadoc)
312    * @see org.vamsas.client.IClientDocument#getClientAppdata()
313    */
314   public IClientAppdata getClientAppdata() {
315     if (scappd==null) {
316       log.debug("Creating new SimpleClientAppdata instance for "+sclient.getSessionHandle());
317       scappd = new SimpleClientAppdata(this);
318       if (scappd==null) {
319         // LATER: may not need this as a warning message.
320         log.warn("Null appdata object for "+sclient.getSessionHandle());
321       } else {
322         log.debug("Created SimpleClientAppdata successfully.");
323       }
324     } else {
325       log.debug("Returning existing SimpleClientAppdata reference.");
326     }
327     return scappd;
328   }
329   /**
330    * access the vamsas document
331    * @return the session's vamsas document
332    */
333   protected VamsasDocument getVamsasDocument() {
334     return doc;
335   }
336   /**
337    * returns the read-only IO interface for the vamsas document Jar file
338    * @return
339    */
340   protected VamsasArchiveReader getVamsasArchiveReader() {
341     try {
342       return archive.getOriginalArchiveReader();
343     } catch (Exception e) {
344       log.warn("Unable to create OriginalArchiveReader!", e);
345     }
346     return null;
347   }
348   protected boolean updateSessionDocument() throws java.io.IOException {
349     if (!isModified() && !scappd.isModified())
350       return false;
351     VamsasSession session = sclient._session;
352     log.debug("updating Session Document in "+session.sessionDir);
353     // update the VamsasDocument structure with any new appData's.
354     // try to update the sessionFile
355     log.debug("Attempting to update session "+sclient.session.getSessionUrn());
356     if (scappd.isModified()) {
357       ClientHandle client = sclient.client;
358       scappd.closeForWriting();      
359       long newAppd = scappd.newAppData.sessionFile.length();
360       
361       if (scappd.appsGlobal==null) {
362         ApplicationData appd = scappd.appsGlobal = new ApplicationData();
363         appd.setName(client.getClientName());
364         appd.setUrn(client.getClientUrn());
365         appd.setVersion(client.getVersion());
366         doc.addApplicationData(appd);
367         // embed or jarEntry ?  - for now only jarEntry's are dealt with.
368         appd.setDataReference(AppDataReference.uniqueAppDataReference(doc, sclient.client.getClientUrn()));
369       }
370       if (scappd.newAppData!=null && scappd.newAppData.sessionFile.exists()) {
371         if (scappd.appsGlobal.getData()!=null) {
372           scappd.appsGlobal.setData(null);
373           scappd.appsGlobal.setDataReference(AppDataReference.uniqueAppDataReference(doc, sclient.client.getClientUrn()));
374         }
375         
376           
377           
378       }
379       if (scappd.usersData==null) {
380         
381       }
382       
383     }
384     cdocument.archive.putVamsasDocument(vdoc);
385     // write the appHandle to the lastupdate file.
386   }
387   if (scappd!=null) {
388       if (scappd.accessedDocument)
389     }
390     va = new org.vamsas.client.simpleclient.VamsasArchive(newf, false, true, sfile);
391     doc = va.getVamsasDocument();
392     doc.addVAMSAS(Core.getDemoVamsas());
393     doc.addApplicationData(makeDemoAppdata(va, 
394         "org.vamsas.test.simpleclient.VamsasArchive", "another old Bugger esq", "rescinded"));
395     if (va.transferRemainingAppDatas())
396       log.info("Remain appdatas were transferred.");
397     else
398       log.warn("No appdatas were transferred. This is wrong.");
399     va.putVamsasDocument(doc);
400     va.closeArchive();
401
402     return true; // successfully updated.
403   }
404   /* (non-Javadoc)
405    * @see java.lang.Object#finalize()
406    */
407   protected void finalize() throws Throwable {
408     log.debug("Garbage collecting on ClientDocument instance.");
409     if (scappd!=null) {
410       scappd.finalize();
411       scappd = null;
412     }
413     // TODO local garbage collection
414     super.finalize();
415   }
416
417 }