/*
* This file is part of the Vamsas Client version 0.1.
* Copyright 2009 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);
}
}