e7557dfad0682e838b08bb9bc80e7aa69a200700
[vamsas.git] / src / uk / ac / vamsas / test / ExampleApplication.java
1 package uk.ac.vamsas.test;
2
3 import uk.ac.vamsas.client.*;
4 import uk.ac.vamsas.client.picking.IMessageHandler;
5 import uk.ac.vamsas.client.picking.IPickManager;
6 import uk.ac.vamsas.client.picking.Message;
7 import uk.ac.vamsas.objects.core.VAMSAS;
8 import uk.ac.vamsas.test.objects.Core;
9
10 import java.awt.Event;
11 import java.beans.PropertyChangeEvent;
12 import java.beans.PropertyChangeListener;
13 import java.io.DataInput;
14 import java.io.DataInputStream;
15 import java.io.DataOutputStream;
16 import java.io.File;
17 import java.io.IOException;
18 import java.io.InputStream;
19 import java.io.ObjectInputStream;
20 import java.io.ObjectOutputStream;
21 import java.io.OutputStream;
22 import java.util.Vector;
23
24 /**
25  * Toy vamsas command line client application demonstrating the API. 
26  * Currently runs with one argument: ExampleApplication.main(new String("watch"))
27  * Test: Start up at least two of these processes independently and they will 
28  * successively modify and handle update events from the document model.
29  * 
30  * Behaviour of each client:
31  * Event handlers are registered for documentUpdate.
32  * - every document update:
33  *   * the vamsas document will be dumped to standard out using uk.ac.vamsas.test.simpleclient.ArchiveReports
34  *   * if there are more than 4 vamsas roots, the first sequence in the first alignment of the first dataset of the second vamsas root will be appended with a single gap character and then written back to the session document. 
35  * A pick thread is started, which sends random CUSTOM pick events every so often (tuning: flooding the pick channel seems to affect the behaviour of other vamsasClient threads, suggesting some object lock contention).
36  * A new vamsas root generated from uk.ac.vamsas.test.objects.Core.getDemoVamsas is added to the document.
37  * Then a while loop waits around for events until shutdown:
38  *  - currently it will shutdown after 9 updates (totalUpdates)
39  *  - an update will be made after every other update that is detected.
40  * 
41  * Status: PickManager now shuts down correctly. Serial updates for two
42  * instances work correctly and are detected under j1.4 (need to test in 1.5 and
43  * 1.6).
44  * 
45  * TODO: test appData get/set methods
46  * 
47  * TODO: verify and test pickManager and interaction between it and other
48  * session events
49  * 
50  * TODO: add more session interaction events
51  * 
52  * TODO: test client add/leave events - currently library generates exceptions
53  * for sessionlist and clientlist modifications.
54  * 
55  * @author jimp
56  */
57
58 public class ExampleApplication {
59   private ClientHandle app;
60
61   private UserHandle user; // TODO: make this something defined by the
62
63   // api
64
65   private IClientFactory clientfactory;
66
67   private IClient vorbaclient;
68
69   private byte[] mydata;
70
71   private Vector vamsasObjects;
72
73   private boolean isUpdated = false;
74
75   private boolean isShuttingdown = false;
76
77   private boolean isFinalizing = false;
78
79   private int totalUpdates = 9;
80
81   private uk.ac.vamsas.client.VorbaId recover = null;
82
83   private int calls = 0;
84
85   private long mdatahash = 0;
86
87   private long muserdatahash = 0;
88
89   private void processVamsasDocument(IClientDocument doc) {
90     if (doc.getVamsasRoots().length < 4) {
91       doc.addVamsasRoot(Core.getDemoVamsas());
92     } else {
93       try {
94         uk.ac.vamsas.objects.core.DataSet ds = doc.getVamsasRoots()[1]
95             .getDataSet(0);
96         uk.ac.vamsas.objects.core.AlignmentSequence alsq = ds.getAlignment(0)
97             .getAlignmentSequence(0);
98         if (recover == null) {
99           recover = alsq.getVorbaId();
100         } else {
101           Vobject recoverd = doc.getObject(recover);
102           System.out.println("Recovery of " + recover + " was "
103               + ((recoverd == null) ? "A FAILURE" : "SUCCESSFUL"));
104         }
105         System.out.println("Modifying Sequence:\n" + alsq.hashCode());
106         alsq.setSequence(alsq.getSequence() + ds.getAlignment(0).getGapChar());
107         System.out.println("Modifying Sequence:\n" + alsq.hashCode());
108         System.out.println("Modified Sequence:\n" + alsq.getSequence());
109         doc.setVamsasRoots(doc.getVamsasRoots());
110       } catch (Exception ee) {
111
112       }
113     }
114     // get this apps 'mydata' if it hasn't got it already.
115     System.out.println("Trying to get appdata and modify it.....");
116     try {
117       processAppData(doc);
118       System.out.println(".....Finished.");
119     } catch (Exception e) {
120       System.err.println("Failed to process appdata for our application.");
121       e.printStackTrace(System.err);
122     }
123
124     // .. access this application's 'public' mydata' if there is any.
125     vorbaclient.updateDocument(doc);
126     // merge vamsasObjects with vamsas objects in document
127
128   }
129
130   private int appdatareads = 0;
131
132   private void processAppData(IClientDocument doc) throws Exception {
133     appdatareads++;
134     boolean writtenonce = false;
135     if (doc != null) {
136       uk.ac.vamsas.client.IClientAppdata appd = doc.getClientAppdata();
137       if (appd.hasClientAppdata() && !(appdatareads % 2 == 0)) {
138         // byte[] cappd = appd.getClientAppdata();
139         // if (cappd!=null)
140         // System.out.println("Client appdata\n"+cappd.toString()+"\nEnd of
141         // Appdata\n");
142         System.out.println("Testing read from inputstream");
143         String cappds = readData(appd.getClientInputStream());
144         System.out.println("Client appdata\n" + cappds + "\nEnd of Appdata\n");
145       } else {
146         if (!writtenonce) {
147           String newapd = "Client Appdata:";
148           if (appd.hasClientAppdata())
149           {
150             AppDataInputStream is;
151             newapd = readData(is = appd.getClientInputStream());
152             is.close();
153           }
154           writtenonce = true;
155           // appd.setClientAppdata(makeappData("Client Appdata for
156           // "+user.toString()+" written"));
157           writeData(appd.getClientOutputStream(),
158               newapd+" : Client Appdata for all users written on " + appdatareads
159                   + " read by " + vorbaclient.getUserHandle());
160           System.out.println("Written to ClientAppdata stream.");
161         }
162       }
163       if (appd.hasUserAppdata() && !(appdatareads % 2 == 0)) {
164         byte[] cappd = appd.getUserAppdata();
165         if (cappd != null)
166           System.out.println("User appdata\n" + new String(cappd)
167               + "\nEnd of Users' Appdata\n");
168         else {
169           System.out.println("No user appdata.");
170           appd.setUserAppdata(("no default - overwritten null byte set on "
171               + appdatareads + " read by " + vorbaclient.getUserHandle() + "")
172               .getBytes());
173         }
174       } else if (!writtenonce) {
175         writtenonce = true;
176         byte[] bts = makeappData("User Appdata for " + user + " written on "
177             + appdatareads + " read at ");
178         System.out.println("Setting appData bytes to\n" + new String(bts)
179             + "\nEnd.");
180         appd.setUserAppdata(bts);
181         System.out.println("Written to UserAppdata stream.");
182       }
183     }
184   }
185
186   private byte[] makeappData(String message) {
187     StringBuffer sb = new StringBuffer();
188     sb.append(message);
189     sb.append("on " + new java.util.Date());
190     return sb.toString().getBytes();
191   }
192
193   private boolean writeData(AppDataOutputStream os, String message) {
194     StringBuffer sb = new StringBuffer();
195     sb.append(message);
196     sb.append("on " + new java.util.Date());
197     try {
198       ObjectOutputStream oos = new ObjectOutputStream(os);
199       oos.writeObject(sb.toString());
200       oos.flush();
201       oos.close();
202     } catch (Exception e) {
203       System.err.println("Problem serialising this message:\n" + sb);
204       e.printStackTrace(System.err);
205       return false;
206     }
207     return true;
208   }
209
210   private String readData(AppDataInputStream is) {
211     if (is != null) {
212       try {
213         if (is.available() > 0) {
214           ObjectInputStream ois = new ObjectInputStream(is);
215           String rs = (String) ois.readObject();
216           return rs;
217         }
218       } catch (Exception e) {
219         System.err.println("Failed to read a string from input stream!");
220         e.printStackTrace(System.err);
221       }
222     }
223     return "";
224   }
225
226   private void addHandlers(IClient avorbaclient) {
227     final ExampleApplication me = this;
228     // make a non-volatile reference to the client instance.
229     final IClient vorbaclient = avorbaclient;
230     // register update handler
231     vorbaclient.addDocumentUpdateHandler(new PropertyChangeListener() {
232       public void propertyChange(PropertyChangeEvent evt) {
233         System.out.println("Vamsas document update for "
234             + evt.getPropertyName() + ": " + evt.getOldValue() + " to "
235             + evt.getNewValue());
236         // merge new data into ours.
237         // example - output doc
238         try {
239           IClientDocument cdoc = vorbaclient.getClientDocument();
240           if (calls > 2 && cdoc.getVamsasRoots().length > 0
241               && !cdoc.getVamsasRoots()[0].is__stored_in_document()) {
242             System.err
243                 .println("Pathological Update Detected - Document is zeroed!");
244           }
245           calls++;
246           uk.ac.vamsas.test.simpleclient.ArchiveReports.rootReport(cdoc
247               .getVamsasRoots(), true, System.out);
248           // Simple update
249           try {
250             if (cdoc.getVamsasRoots().length > 2) {
251               uk.ac.vamsas.objects.core.DataSet ds = cdoc.getVamsasRoots()[1]
252                   .getDataSet(0);
253               uk.ac.vamsas.objects.core.AlignmentSequence alsq = ds
254                   .getAlignment(0).getAlignmentSequence(0);
255               if (alsq.isUpdated())
256                 System.out.println("Seqeuence was updated since last time.");
257               alsq.setSequence(alsq.getSequence()
258                   + ds.getAlignment(0).getGapChar());
259               System.out.println("Newly Modified Sequence:\n"
260                   + alsq.getSequence());
261               cdoc.setVamsasRoots(cdoc.getVamsasRoots());
262             }
263           } catch (Exception ee) {
264             System.err.println("Exception whilst updating :");
265             ee.printStackTrace(System.err);
266           }
267           vorbaclient.updateDocument(cdoc);
268         } catch (Exception e) {
269           System.err
270               .println("Exception whilst dumping document tree after an update.");
271           e.printStackTrace(System.err);
272         }
273         isUpdated = true; // tell main thread to reflect change...
274       }
275     });
276     // register close handler
277     vorbaclient.addVorbaEventHandler(Events.DOCUMENT_REQUESTTOCLOSE,
278         new PropertyChangeListener() {
279           public void propertyChange(PropertyChangeEvent evt) {
280             System.out.println("Received request to close vamsas document.");
281             // ask user for a filename to save it to.
282             // Then pass it to the vorba object...
283             vorbaclient.storeDocument(new java.io.File("UserLocation"));
284           }
285         });
286
287     // register some more handlers to monitor the session :
288
289     vorbaclient.addVorbaEventHandler(Events.CLIENT_CREATION,
290         new PropertyChangeListener() {
291           public void propertyChange(PropertyChangeEvent evt) {
292             System.out.println("New Vamsas client for " + evt.getPropertyName()
293                 + ": " + evt.getOldValue() + " to " + evt.getNewValue());
294             // tell app add new client to its list of clients.
295           }
296         });
297     vorbaclient.addVorbaEventHandler(Events.CLIENT_FINALIZATION,
298         new PropertyChangeListener() {
299           public void propertyChange(PropertyChangeEvent evt) {
300             System.out.println("Vamsas client finalizing for "
301                 + evt.getPropertyName() + ": " + evt.getOldValue() + " to "
302                 + evt.getNewValue());
303             // tell app to update its list of clients to communicate
304             // with.
305           }
306         });
307     vorbaclient.addVorbaEventHandler(Events.SESSION_SHUTDOWN,
308         new PropertyChangeListener() {
309           public void propertyChange(PropertyChangeEvent evt) {
310             System.out.println("Session " + evt.getPropertyName()
311                 + " is shutting down.");
312             // tell app to finalize its session data before
313             // shutdown.
314           }
315         });
316     vorbaclient.addVorbaEventHandler(Events.DOCUMENT_FINALIZEAPPDATA,
317         new PropertyChangeListener() {
318           public void propertyChange(PropertyChangeEvent evt) {
319             boolean finalized = false;
320             System.out
321                 .println("Application received a DOCUMENT_FINALIZEAPPDATA event.");
322             IClientDocument cdoc = null;
323             try {
324               cdoc = vorbaclient.getClientDocument();
325               if (cdoc != null) {
326                 IClientAppdata apd = cdoc.getClientAppdata();
327                 if (apd != null) {
328                   String userd = "";
329                   if (apd.getUserAppdata()!=null)
330                     {
331                       userd = new String(apd.getUserAppdata());
332                     }
333                   String appdat = me.readData(apd.getClientInputStream());
334                   me.writeData(apd.getClientOutputStream(), appdat
335                       + "\nUser Data merged\n" + userd + "\n");
336                 }
337                 finalized = true;
338                 vorbaclient.updateDocument(cdoc);
339               }
340               // tell app to finalize its session data prior to the
341               // storage of the current session as an archive.
342             } catch (Exception e) {
343               if (!finalized) {
344                 System.err
345                     .println("Probable library problem - exception when trying to update document after writing merged appdata.");
346                 e.printStackTrace(System.err);
347               } else {
348                 System.err
349                     .println("Recovering from exception when writing merged appdata");
350                 e.printStackTrace(System.err);
351                 try {
352                   if (cdoc != null) {
353                     vorbaclient.updateDocument(cdoc);
354                   }
355                 } catch (Exception e2) {
356                   System.err
357                       .println("Probable library problem - exception when trying to update document after an exception when writing merged appdata.");
358                   e2.printStackTrace(System.err);
359                 }
360               }
361               return;
362             }
363             System.out.println("Application finalized appdata successfuly.");
364           }
365
366         });
367   }
368
369   public String Usage = "ExampleApplication :/n [-arena <vamsasFileDirectory>][-session <vamsasSessionURN>] <action> [+<arguments>]\n"
370       + "<action> is one of :\n\tsave,update,close,watch";
371
372   String sess = null;
373
374   String importFile = null;
375
376   String outputFile = null;
377
378   private boolean parseArgs(String args[]) {
379     if (args.length == 0) {
380       return false;
381     }
382     int cpos = 0;
383     boolean parsed = false;
384     while (!parsed && cpos < args.length) {
385       if (args[cpos].toLowerCase().equals("load") && cpos + 1 < args.length) {
386         importFile = args[cpos + 1];
387         cpos += 2;
388         continue;
389       }
390       if (args[cpos].toLowerCase().equals("save") && cpos + 1 < args.length) {
391         outputFile = args[cpos + 1];
392         cpos += 2;
393         continue;
394       }
395       // default behaviour - if not anything else its probably a session urn
396       if (!args[cpos].toLowerCase().equals("watch")) {
397         sess = args[cpos];
398       }
399       cpos++;
400     }
401     return true;
402   }
403
404   class ExamplePicker extends Thread {
405     String me = null;
406
407     public IPickManager pm = null;
408
409     ExamplePicker(String me, IPickManager pm) {
410       this.me = me;
411       this.pm = pm;
412     }
413
414     public void run() {
415       int mcount = 1;
416       while (pm != null) {
417         try {
418           Thread.sleep(1000 + (long) Math.random() * 10000);
419         } catch (Exception e) {
420         }
421
422         if (pm != null) {
423           pm.sendMessage(new uk.ac.vamsas.client.picking.CustomMessage(
424               "Message " + mcount++ + " from " + me));
425         }
426       }
427     }
428
429   }
430
431   long shutdown = -1;
432
433   public void runMe(String[] args) {
434     if (!parseArgs(args)) {
435       System.err.print(Usage);
436     }
437     // get IClientFactory
438     try {
439       clientfactory = new uk.ac.vamsas.client.simpleclient.SimpleClientFactory();
440     } catch (IOException e) {
441       System.err.println(e + "\n" + Usage);
442       System.exit(1);
443     }
444
445     // get an Iclient with session data
446     app = new ClientHandle("uk.ac.vamsas.test.ExampleApplication", "0.1");
447     user = new UserHandle("arnolduser", "deathsdoor");
448     try {
449       if (sess != null) {
450         System.out.println("Connecting to " + sess);
451         vorbaclient = clientfactory.getIClient(app, user, sess);
452       } else {
453         vorbaclient = clientfactory.getIClient(app, user);
454       }
455     } catch (NoDefaultSessionException e) {
456       System.err
457           .println("There appear to be several sessions to choose from :");
458       String[] sessions = clientfactory.getCurrentSessions();
459       for (int s = 0; s < sessions.length; s++)
460         System.err.println(sessions[s]);
461       System.exit(2);
462     }
463     addHandlers(vorbaclient);
464     try {
465       vorbaclient.joinSession();
466     } catch (Exception se) {
467       se.printStackTrace();
468       System.err.println(se + " when joining session.\n" + Usage);
469       System.exit(1);
470     }
471     // register an update listener and a close listener.
472     // import any data if requested to
473     if (importFile != null) {
474       File vfile = new File(importFile);
475       try {
476         vorbaclient.importDocument(vfile);
477       } catch (Exception e) {
478         System.err.println("Failed to import file " + importFile);
479         System.err.println("Exception received was " + e);
480         e.printStackTrace(System.err);
481       }
482
483     }
484     // Write out any data if requested to
485     if (outputFile != null) {
486       File vfile = new File(outputFile);
487       try {
488         vorbaclient.storeDocument(vfile);
489       } catch (Exception e) {
490         System.err.println("Failed to export session as file " + outputFile);
491         System.err.println("Exception received was " + e);
492         e.printStackTrace(System.err);
493       }
494     }
495     // get document data
496     try {
497       IClientDocument cdoc = vorbaclient.getClientDocument();
498       processVamsasDocument(cdoc);
499     } catch (Exception e) {
500       System.err
501           .println("Unexpected exception when retrieving the client document for the first time!");
502       e.printStackTrace(System.err);
503       System.exit(1);
504     }
505     int update = 0;
506     ExamplePicker picker = new ExamplePicker(vorbaclient.getClientHandle()
507         .getClientUrn(), vorbaclient.getPickManager());
508
509     picker.start();
510     if (picker.pm != null) {
511       picker.pm.registerMessageHandler(new IMessageHandler() {
512
513         public void handleMessage(Message message) {
514           System.out.println("Received |" + message.getRawMessage() + "|");
515           shutdown += 100; // hang around for 5 seconds or so before dying
516           // naturally.
517
518         }
519
520       });
521     }
522     shutdown = System.currentTimeMillis() + 10000; // hang around for 10
523     // seconds or so before
524     // dying naturally.
525     while (!isShuttingdown && update < totalUpdates) {
526       // do something with data
527       // , update document, or something.
528       // ..
529       if (isUpdated) {
530         System.out.println("Update handler called " + (++update) + " times");
531         System.out.println("******************************************");
532         isUpdated = false; // TODO: saner update det method.
533         shutdown = System.currentTimeMillis() + 10000;
534         if (update % 2 == 1) {
535           try {
536             IClientDocument cdoc = vorbaclient.getClientDocument();
537             processVamsasDocument(cdoc);
538           } catch (Exception e) {
539             System.err
540                 .println("Error when updating document after an even numbered update.");
541             e.printStackTrace(System.err);
542           }
543         }
544       } else {
545         if (System.currentTimeMillis() > shutdown) {
546           isShuttingdown = true;
547         }
548       }
549
550       try {
551         Thread.sleep(50);
552       } catch (Exception e) {
553       }
554
555     }
556     System.out.println("Finalizing.");
557     // call finalizeClient
558     vorbaclient.finalizeClient();
559     System.out.println("Shutting down picker.");
560     picker.pm = null;
561     while (picker.isAlive()) {
562       System.out.println("Waiting for picker to die...");
563       try {
564         Thread.sleep(1000);
565       } catch (Exception e) {
566       }
567       ;
568     }
569
570     // { meanwhile, eventHandlers are called to do any saves if need be }
571     // and all registered listeners will be deregistered to avoid deadlock.
572
573     // finish
574     vorbaclient=null;
575     clientfactory=null;
576     System.out.println("Byee!");
577     //try { Thread.sleep(100000); } catch (Exception e) {}
578   }
579
580   public static void main(String[] args) {
581     ExampleApplication example = new ExampleApplication();
582     example.runMe(args);
583   }
584 }