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