/* * This file is part of the Vamsas Client version 0.2. * Copyright 2010 by Jim Procter, Iain Milne, Pierre Marguerite, * Andrew Waterhouse and Dominik Lindner. * * Earlier versions have also been incorporated into Jalview version 2.4 * since 2008, and TOPALi version 2 since 2007. * * The Vamsas Client is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * The Vamsas Client is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the Vamsas Client. If not, see . */ package uk.ac.vamsas.test; import uk.ac.vamsas.client.*; import uk.ac.vamsas.client.picking.IMessageHandler; import uk.ac.vamsas.client.picking.IPickManager; import uk.ac.vamsas.client.picking.Message; import uk.ac.vamsas.objects.core.VAMSAS; import uk.ac.vamsas.test.objects.Core; import java.awt.Event; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.DataInput; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.util.Vector; /** * Toy vamsas command line client application demonstrating the API. Currently * runs with one argument: ExampleApplication.main(new String("watch")) Test: * Start up at least two of these processes independently and they will * successively modify and handle update events from the document model. * * Behaviour of each client: Event handlers are registered for documentUpdate. - * every document update: * the vamsas document will be dumped to standard out * using uk.ac.vamsas.test.simpleclient.ArchiveReports * 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. 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). A new vamsas root generated * from uk.ac.vamsas.test.objects.Core.getDemoVamsas is added to the document. * Then a while loop waits around for events until shutdown: - currently it will * shutdown after 9 updates (totalUpdates) - an update will be made after every * other update that is detected. * * Status: PickManager now shuts down correctly. Serial updates for two * instances work correctly and are detected under j1.4 (need to test in 1.5 and * 1.6). * * TODO: test appData get/set methods * * TODO: verify and test pickManager and interaction between it and other * session events * * TODO: add more session interaction events * * TODO: test client add/leave events - currently library generates exceptions * for sessionlist and clientlist modifications. * * @author jimp */ public class ExampleApplication { private ClientHandle app; private UserHandle user; // TODO: make this something defined by the // api private IClientFactory clientfactory; private IClient vorbaclient; private byte[] mydata; private Vector vamsasObjects; private boolean isUpdated = false; private boolean isShuttingdown = false; private boolean isFinalizing = false; private int totalUpdates = 9; private uk.ac.vamsas.client.VorbaId recover = null; private int calls = 0; private long mdatahash = 0; private long muserdatahash = 0; private void processVamsasDocument(IClientDocument doc) { if (doc.getVamsasRoots().length < 4) { doc.addVamsasRoot(Core.getDemoVamsas()); } else { try { uk.ac.vamsas.objects.core.DataSet ds = doc.getVamsasRoots()[1] .getDataSet(0); uk.ac.vamsas.objects.core.AlignmentSequence alsq = ds.getAlignment(0) .getAlignmentSequence(0); if (recover == null) { recover = alsq.getVorbaId(); } else { Vobject recoverd = doc.getObject(recover); System.out.println("Recovery of " + recover + " was " + ((recoverd == null) ? "A FAILURE" : "SUCCESSFUL")); } System.out.println("Modifying Sequence:\n" + alsq.hashCode()); alsq.setSequence(alsq.getSequence() + ds.getAlignment(0).getGapChar()); System.out.println("Modifying Sequence:\n" + alsq.hashCode()); System.out.println("Modified Sequence:\n" + alsq.getSequence()); doc.setVamsasRoots(doc.getVamsasRoots()); } catch (Exception ee) { } } // get this apps 'mydata' if it hasn't got it already. System.out.println("Trying to get appdata and modify it....."); try { processAppData(doc); System.out.println(".....Finished."); } catch (Exception e) { System.err.println("Failed to process appdata for our application."); e.printStackTrace(System.err); } // .. access this application's 'public' mydata' if there is any. vorbaclient.updateDocument(doc); // merge vamsasObjects with vamsas objects in document } private int appdatareads = 0; boolean clientappd = true; boolean userappd = false; private void processAppData(IClientDocument doc) throws Exception { appdatareads++; boolean writtenonce = false; if (doc != null) { uk.ac.vamsas.client.IClientAppdata appd = doc.getClientAppdata(); if (clientappd) { if (appd.hasClientAppdata() && !(appdatareads % 2 == 0)) { // byte[] cappd = appd.getClientAppdata(); // if (cappd!=null) // System.out.println("Client appdata\n"+cappd.toString()+"\nEnd of // Appdata\n"); System.out.println("Testing read from inputstream"); String cappds = readData(appd.getClientInputStream()); System.out .println("Client appdata\n" + cappds + "\nEnd of Appdata\n"); } else { if (!writtenonce) { String newapd = "Client Appdata:"; if (appd.hasClientAppdata()) { AppDataInputStream is; newapd = readData(is = appd.getClientInputStream()); is.close(); } writtenonce = true; // appd.setClientAppdata(makeappData("Client Appdata for // "+user.toString()+" written")); writeData(appd.getClientOutputStream(), newapd + " : Client Appdata for all users written on " + appdatareads + " read by " + vorbaclient.getUserHandle()); System.out.println("Written to ClientAppdata stream."); } } } if (userappd) { if (appd.hasUserAppdata() && !(appdatareads % 2 == 0)) { byte[] cappd = appd.getUserAppdata(); if (cappd != null) System.out.println("User appdata\n" + new String(cappd) + "\nEnd of Users' Appdata\n"); else { System.out.println("No user appdata."); appd .setUserAppdata(("no default - overwritten null byte set on " + appdatareads + " read by " + vorbaclient.getUserHandle() + "") .getBytes()); } } else if (!writtenonce) { writtenonce = true; byte[] bts = makeappData("User Appdata for " + user + " written on " + appdatareads + " read at "); System.out.println("Setting appData bytes to\n" + new String(bts) + "\nEnd."); appd.setUserAppdata(bts); System.out.println("Written to UserAppdata stream."); } } } } private byte[] makeappData(String message) { StringBuffer sb = new StringBuffer(); sb.append(message); sb.append("on " + new java.util.Date()); return sb.toString().getBytes(); } private boolean writeData(AppDataOutputStream os, String message) { StringBuffer sb = new StringBuffer(); sb.append(message); sb.append("on " + new java.util.Date()); try { ObjectOutputStream oos = new ObjectOutputStream(os); oos.writeObject(sb.toString()); oos.flush(); oos.close(); } catch (Exception e) { System.err.println("Problem serialising this message:\n" + sb); e.printStackTrace(System.err); return false; } return true; } private String readData(AppDataInputStream is) { if (is != null) { try { if (is.available() > 0) { ObjectInputStream ois = new ObjectInputStream(is); String rs = (String) ois.readObject(); return rs; } } catch (Exception e) { System.err.println("Failed to read a string from input stream!"); e.printStackTrace(System.err); } } return ""; } private void addHandlers(IClient avorbaclient) { final ExampleApplication me = this; // make a non-volatile reference to the client instance. final IClient vorbaclient = avorbaclient; // register update handler vorbaclient.addDocumentUpdateHandler(new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { System.out.println("Vamsas document update for " + evt.getPropertyName() + ": " + evt.getOldValue() + " to " + evt.getNewValue()); // merge new data into ours. // example - output doc try { IClientDocument cdoc = vorbaclient.getClientDocument(); if (calls > 2 && cdoc.getVamsasRoots().length > 0 && !cdoc.getVamsasRoots()[0].is__stored_in_document()) { System.err .println("Pathological Update Detected - Document is zeroed!"); } calls++; uk.ac.vamsas.test.simpleclient.ArchiveReports.rootReport(cdoc .getVamsasRoots(), true, System.out); // Simple update try { if (cdoc.getVamsasRoots().length > 2) { uk.ac.vamsas.objects.core.DataSet ds = cdoc.getVamsasRoots()[1] .getDataSet(0); uk.ac.vamsas.objects.core.AlignmentSequence alsq = ds .getAlignment(0).getAlignmentSequence(0); if (alsq.isUpdated()) System.out.println("Seqeuence was updated since last time."); alsq.setSequence(alsq.getSequence() + ds.getAlignment(0).getGapChar()); System.out.println("Newly Modified Sequence:\n" + alsq.getSequence()); cdoc.setVamsasRoots(cdoc.getVamsasRoots()); } } catch (Exception ee) { System.err.println("Exception whilst updating :"); ee.printStackTrace(System.err); } vorbaclient.updateDocument(cdoc); } catch (Exception e) { System.err .println("Exception whilst dumping document tree after an update."); e.printStackTrace(System.err); } isUpdated = true; // tell main thread to reflect change... } }); // register close handler vorbaclient.addVorbaEventHandler(Events.DOCUMENT_REQUESTTOCLOSE, new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { System.out.println("Received request to close vamsas document."); // ask user for a filename to save it to. // Then pass it to the vorba object... vorbaclient.storeDocument(new java.io.File("UserLocation")); } }); // register some more handlers to monitor the session : vorbaclient.addVorbaEventHandler(Events.CLIENT_CREATION, new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { System.out.println("New Vamsas client for " + evt.getPropertyName() + ": " + evt.getOldValue() + " to " + evt.getNewValue()); // tell app add new client to its list of clients. } }); vorbaclient.addVorbaEventHandler(Events.CLIENT_FINALIZATION, new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { System.out.println("Vamsas client finalizing for " + evt.getPropertyName() + ": " + evt.getOldValue() + " to " + evt.getNewValue()); // tell app to update its list of clients to communicate // with. } }); vorbaclient.addVorbaEventHandler(Events.SESSION_SHUTDOWN, new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { System.out.println("Session " + evt.getPropertyName() + " is shutting down."); // tell app to finalize its session data before // shutdown. } }); vorbaclient.addVorbaEventHandler(Events.DOCUMENT_FINALIZEAPPDATA, new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { boolean finalized = false; System.out .println("Application received a DOCUMENT_FINALIZEAPPDATA event."); IClientDocument cdoc = null; try { cdoc = vorbaclient.getClientDocument(); if (cdoc != null) { IClientAppdata apd = cdoc.getClientAppdata(); if (apd != null) { String userd = ""; if (apd.getUserAppdata() != null) { userd = new String(apd.getUserAppdata()); } String appdat = me.readData(apd.getClientInputStream()); me.writeData(apd.getClientOutputStream(), appdat + "\nUser Data merged\n" + userd + "\n"); } finalized = true; vorbaclient.updateDocument(cdoc); } // tell app to finalize its session data prior to the // storage of the current session as an archive. } catch (Exception e) { if (!finalized) { System.err .println("Probable library problem - exception when trying to update document after writing merged appdata."); e.printStackTrace(System.err); } else { System.err .println("Recovering from exception when writing merged appdata"); e.printStackTrace(System.err); try { if (cdoc != null) { vorbaclient.updateDocument(cdoc); } } catch (Exception e2) { System.err .println("Probable library problem - exception when trying to update document after an exception when writing merged appdata."); e2.printStackTrace(System.err); } } return; } System.out.println("Application finalized appdata successfuly."); } }); } public String Usage = "ExampleApplication :/n [-arena ][-session ] [+]\n" + " is one of :\n\tsave,new,watch\n" + "-arena and -session are not yet supported"; // TODO for release String sess = null; String importFile = null; String outputFile = null; boolean newSession = false; boolean imported = false; private boolean parseArgs(String args[]) { if (args.length == 0) { return false; } int cpos = 0; boolean parsed = false; while (!parsed && cpos < args.length) { // TODO: note importDocument not yet supported. // if (args[cpos].toLowerCase().equals("load") && cpos + 1 < args.length) // { // importFile = args[cpos + 1]; // cpos += 2; // continue; // } if (args[cpos].toLowerCase().equals("new") && cpos + 1 < args.length) { importFile = args[cpos + 1]; newSession = true; cpos += 2; continue; } if (args[cpos].toLowerCase().equals("save") && cpos + 1 < args.length) { outputFile = args[cpos + 1]; cpos += 2; continue; } // default behaviour - if not anything else its probably a session urn if (!args[cpos].toLowerCase().equals("watch")) { sess = args[cpos]; } cpos++; } return true; } class ExamplePicker extends Thread { String me = null; public IPickManager pm = null; ExamplePicker(String me, IPickManager pm) { this.me = me; this.pm = pm; } public void run() { int mcount = 1; while (pm != null) { try { Thread.sleep(1000 + (long) Math.random() * 10000); } catch (Exception e) { } if (pm != null) { pm.sendMessage(new uk.ac.vamsas.client.picking.CustomMessage( "Message " + mcount++ + " from " + me)); } } } } long shutdown = -1; public void runMe(String[] args) { if (!parseArgs(args)) { System.err.print(Usage); } // get IClientFactory try { clientfactory = new uk.ac.vamsas.client.simpleclient.SimpleClientFactory(); } catch (IOException e) { System.err.println(e + "\n" + Usage); System.exit(1); } // get an Iclient with session data app = new ClientHandle("uk.ac.vamsas.test.ExampleApplication", "0.1"); user = new UserHandle("arnolduser", "deathsdoor"); if (sess != null && importFile != null) { System.err .println("Import of an existing document into an existing session is not fully supported yet."); System.exit(3); } try { if (sess != null) { System.out.println("Connecting to " + sess); vorbaclient = clientfactory.getIClient(app, user, sess); } else { if (newSession) { if (importFile == null) { System.out.println("Connecting to a new VAMSAS Session."); vorbaclient = clientfactory.getNewSessionIClient(app, user); } else { System.out .println("Connecting to a new VAMSAS Session to share data in existing document:" + importFile); File vfile = new File(importFile); vorbaclient = clientfactory.openAsNewSessionIClient(app, user, vfile); System.out .println("Succesfully imported document into new session."); imported = true; importFile = null; } } else { vorbaclient = clientfactory.getIClient(app, user); } } } catch (InvalidSessionDocumentException e) { System.err.println("Failed to create a session to share " + importFile); System.err.println("Sorry it didn't work out. This is what went wrong:"); e.printStackTrace(System.err); System.exit(3); } catch (NoDefaultSessionException e) { System.err .println("There appear to be several sessions to choose from :"); String[] sessions = clientfactory.getCurrentSessions(); for (int s = 0; s < sessions.length; s++) System.err.println(sessions[s]); System.exit(2); } addHandlers(vorbaclient); try { vorbaclient.joinSession(); } catch (Exception se) { se.printStackTrace(); System.err.println(se + " when joining session.\n" + Usage); System.exit(1); } // register an update listener and a close listener. // import any data if requested to if (importFile != null) { File vfile = new File(importFile); try { vorbaclient.importDocument(vfile); imported = true; } catch (Exception e) { System.err.println("Failed to import file " + importFile); System.err.println("Exception received was " + e); e.printStackTrace(System.err); } } // Write out any data if requested to if (outputFile != null) { File vfile = new File(outputFile); try { vorbaclient.storeDocument(vfile); } catch (Exception e) { System.err.println("Failed to export session as file " + outputFile); System.err.println("Exception received was " + e); e.printStackTrace(System.err); } } // get document data try { IClientDocument cdoc = vorbaclient.getClientDocument(); if (imported) { if (cdoc.getVamsasRoots() == null || cdoc.getVamsasRoots()[0].getDataSetCount() < 1) { System.err .println("Imported an Empty vamsas document. Is this correct ?"); } } processVamsasDocument(cdoc); } catch (Exception e) { System.err .println("Unexpected exception when retrieving the client document for the first time!"); e.printStackTrace(System.err); System.exit(1); } int update = 0; ExamplePicker picker = new ExamplePicker(vorbaclient.getClientHandle() .getClientUrn(), vorbaclient.getPickManager()); picker.start(); if (picker.pm != null) { picker.pm.registerMessageHandler(new IMessageHandler() { public void handleMessage(Message message) { System.out.println("Received |" + message.getRawMessage() + "|"); shutdown += 100; // hang around for 5 seconds or so before dying // naturally. } }); } shutdown = System.currentTimeMillis() + 10000; // hang around for 10 // seconds or so before // dying naturally. while (!isShuttingdown && update < totalUpdates) { // do something with data // , update document, or something. // .. if (isUpdated) { System.out.println("Update handler called " + (++update) + " times"); System.out.println("******************************************"); isUpdated = false; // TODO: saner update det method. shutdown = System.currentTimeMillis() + 10000; if (update % 2 == 1) { try { IClientDocument cdoc = vorbaclient.getClientDocument(); processVamsasDocument(cdoc); } catch (Exception e) { System.err .println("Error when updating document after an even numbered update."); e.printStackTrace(System.err); } try { Thread.sleep(200); } catch (Exception e) { } } } else { if (System.currentTimeMillis() > shutdown) { isShuttingdown = true; } } try { Thread.sleep(50); } catch (Exception e) { } } System.out.println("Finalizing."); // call finalizeClient vorbaclient.finalizeClient(); System.out.println("Shutting down picker."); picker.pm = null; while (picker.isAlive()) { System.out.println("Waiting for picker to die..."); try { Thread.sleep(1000); } catch (Exception e) { } ; } // { meanwhile, eventHandlers are called to do any saves if need be } // and all registered listeners will be deregistered to avoid deadlock. // finish vorbaclient = null; clientfactory = null; System.out.println("Byee!"); // try { Thread.sleep(100000); } catch (Exception e) {} } public static void main(String[] args) { ExampleApplication example = new ExampleApplication(); example.runMe(args); } }