public void setClientName(String clientName) {
this.clientName = clientName;
}
+
+ public boolean equals(Object that) {
+ if (that instanceof ClientHandle)
+ return this.equals((ClientHandle) that);
+ return false;
+ }
+ public boolean equals(ClientHandle that) {
+ return (clientName.equals(that.clientName) && version.equals(that.version) && clientUrn.equals(that.clientUrn));
+ }
}
package org.vamsas.client;
+
/**
- * Enumerates the event types generated
- * during the lifecycle of a Vamsas session.
+ * Enumerates the event types generated during the lifecycle of a Vamsas
+ * session.
*/
public class Events {
/**
- * Generated when a client has finished updating the document.
- * Passes applicationHandle of client so the updating client can recognise its own updates.
+ * Generated when a client has finished updating the document. Passes
+ * applicationHandle of client so the updating client can recognise its own
+ * updates.
*/
- public static final String DOCUMENT_UPDATE="org.vamsas.client.events.documentUpdateEvent";
+ public static final String DOCUMENT_UPDATE = "org.vamsas.client.events.documentUpdateEvent";
+
/**
- * Generated when a new vamsas document is created (perhaps from some existing Vamsas data)
- * so an application may do its own data space initialization.
- * TODO: decide if this is called when an app is connected to a stored session...
+ * Generated when a new vamsas document is created (perhaps from some existing
+ * Vamsas data) so an application may do its own data space initialization.
+ * TODO: decide if this is called when an app is connected to a stored
+ * session...
*/
- public static final String DOCUMENT_CREATE="org.vamsas.client.events.documentCreateEvent";
+ public static final String DOCUMENT_CREATE = "org.vamsas.client.events.documentCreateEvent";
+
/**
- * Generated when a new vamsas client is attached to a session (Handle is passed)
- * Note: the newly created client does not receive the event.
+ * Generated when a new vamsas client is attached to a session (Handle is
+ * passed) Note: the newly created client does not receive the event.
*/
- public static final String CLIENT_CREATION="org.vamsas.client.events.clientCreateEvent";
+ public static final String CLIENT_CREATION = "org.vamsas.client.events.clientCreateEvent";
+
/**
- * Generated when a vamsas client leaves a session (Handle is passed to all others).
+ * Generated when a vamsas client leaves a session (Handle is passed to all
+ * others).
*/
- public static final String CLIENT_FINALIZATION="org.vamsas.client.events.clientFinalizationEvent";
+ public static final String CLIENT_FINALIZATION = "org.vamsas.client.events.clientFinalizationEvent";
+
/**
- * Generated prior to session Shutdown, after the last participating vamsas client has finalized.
+ * Generated prior to session Shutdown, after the last participating vamsas
+ * client has finalized.
*/
- public static final String SESSION_SHUTDOWN="org.vamsas.client.events.SessionShutdownEvent";
+ public static final String SESSION_SHUTDOWN = "org.vamsas.client.events.SessionShutdownEvent";
+
/**
- * Generated by Vorba stub after the penultimate client makes a call to closeDocument().
- * Sequence is as follows :
- * 1. All other vamsas clients have called closeDocument()
- * 2. Final living client monitors closures, and realises that it is last.
- * 3. Final client generates event to prompt associated application to inquire if
- * the user wishes to save the document for future reference.
- *
- * * Any call to closeDocument in a thread other than the registered EventListener
- * will block until the RequestToClose handler has exited.
- *
+ * Generated for all clients when any client calls IClient.storeDocument() to
+ * allow them to store any updates before an offline copy of the session is
+ * created. Any client that handles this should call the
+ * IClient.getDocument(), update and then IClient.updateDocument in the same
+ * handler thread.
*/
- public static final String DOCUMENT_REQUESTTOCLOSE="org.vamas.client.DocumentRequestToCloseEvent";
+ public static final String DOCUMENT_FINALIZEAPPDATA = "org.vamsas.client.events.DocumentFinalizeAppData";
+
+ /**
+ * Generated by Vorba stub after the penultimate client makes a call to
+ * closeDocument(). Sequence is as follows : 1. All other vamsas clients have
+ * called closeDocument() 2. Final living client monitors closures, and
+ * realises that it is last. 3. Final client generates event to prompt
+ * associated application to inquire if the user wishes to save the document
+ * for future reference.
+ * * Any call to closeDocument in a thread other than the registered
+ * EventListener will block until the RequestToClose handler has exited.
+ *
+ */
+ public static final String DOCUMENT_REQUESTTOCLOSE = "org.vamas.client.DocumentRequestToCloseEvent";
+
public static java.util.Vector EventList = initList();
+
private static java.util.Vector initList() {
java.util.Vector vec = new java.util.Vector();
vec.add((Object) DOCUMENT_UPDATE);
vec.add((Object) CLIENT_FINALIZATION);
vec.add((Object) SESSION_SHUTDOWN);
vec.add((Object) DOCUMENT_REQUESTTOCLOSE);
+ vec.add((Object) DOCUMENT_FINALIZEAPPDATA);
return vec;
}
}
/*
* (non-Javadoc)
- *
+ * TODO: check that build substitution variables are correct
* @see org.vamsas.client.IClient#getAbout()
*/
public String getAbout() {
public static void main(String[] args) {
}
+
+ /* (non-Javadoc)
+ * @see org.vamsas.client.IClient#joinSession()
+ */
+ public void joinSession() throws Exception {
+ // TODO Auto-generated method stub
+
+ }
}
package org.vamsas.client.simpleclient;
+
import org.vamsas.client.*;
import java.io.BufferedInputStream;
import java.util.Vector;
/**
- * @author jim
- * Handler for the clientsFile within a vamsas session.
+ * @author jim Handler for the clientsFile within a vamsas session.
*/
public class ClientsFile {
private File filelist;
+
/**
- * number of my client in list
- * (not known at start but used when known to make lock)
+ * number of my client in list (not known at start but used when known to make
+ * lock)
*/
- private int syncnum=1;
-
- ClientsFile(File filelist) {
- this.filelist=filelist;
+ private int syncnum = 1;
+
+ public ClientsFile(File filelist) throws IOException {
+ this.filelist = filelist;
+ if (!this.filelist.exists())
+ this.filelist.createNewFile();
}
- public ClientHandle[] retrieveClientList() {
- if (filelist!=null) {
+
+ private Lock listlock = null;
+
+ /**
+ * Get a lock for the ClientsFile
+ *
+ * @return true if lock was made
+ */
+ protected boolean lockList() {
+ if (listlock != null && listlock.isLocked())
+ return true;
+ listlock = null;
+ if (filelist != null) {
if (filelist.exists()) {
- Lock listlock;
- do {
- listlock = new Lock(filelist); // TODO: wait around if we can't get the lock.
- } while (!listlock.isLocked());
+ // TODO: see if we need to loop-wait for locks or they just block until
+ // lock is made...
+ // do {
+ // listlock = new Lock(filelist); // TODO: wait around if we can't get
+ // the lock.
+ // } while (!listlock.isLocked());
+ listlock = new Lock(filelist);
+ return listlock.isLocked();
+ }
+ } else
+ throw new Error(
+ "org.vamsas.client.simpleclient.ClientsFile.lockList called for non-initialised ClientsFile!");
+
+ // no lock possible
+ return false;
+ }
+
+ /**
+ * Explicitly release the ClientsFile lock.
+ *
+ * @return true if lock was released.
+ */
+ protected boolean unlockList() {
+ if (listlock != null) {
+
+ if (listlock.isLocked()) {
try {
- ObjectInputStream is = new ObjectInputStream(new BufferedInputStream(new java.io.FileInputStream(filelist)));
- ClientHandle[] clients;
- clients = (ClientHandle[]) ((Vector) is.readObject()).toArray();
- return clients;
+ listlock.lock.release();
+ } catch (IOException e) {
+ // TODO Deal with unlock Lock.release exception!
+ e.printStackTrace(System.err);
}
- catch (FileNotFoundException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (Exception e) {
- e.printStackTrace();
+ }
+
+ listlock = null;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * internal method for getting clientList - ensures a lock has been made but
+ * does not release it.
+ *
+ * @return list of clients
+ */
+ private ClientHandle[] retrieveClientHandles() {
+ if (lockList()) {
+ try {
+ ClientHandle[] clients=null;
+ if (this.listlock.rafile.length()>0) {
+ ObjectInputStream is = new ObjectInputStream(new BufferedInputStream(
+ new java.io.FileInputStream(filelist)));
+ Object o;
+ o=is.readObject();
+ if (o!=null) {
+ try {
+ clients = (ClientHandle[]) o;
+ }
+ catch (Exception e) {
+ System.err.println("Garbage in the clientHandle list "+this.filelist);
+ }
+ }
}
+ return clients;
+ } catch (FileNotFoundException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace(System.err);
+ } catch (Exception e) {
+ e.printStackTrace(System.err);
}
- // else return null.
- } else {
- throw new Error("Tried to retrieve a clientList without specifying client list filename");
}
return null;
-
}
- public boolean putClientList(ClientHandle[] clients) {
- if (filelist!=null) {
- if (filelist.exists()) {
- Lock listlock;
- do {
- listlock = new Lock(filelist); // TODO: wait around if we can't get the lock? should return and make client wait until write has finished so it can read new client info...
- } while (!listlock.isLocked());
+ /**
+ * get the clientList from the file. May return false if lock failed!
+ * @return clientList
+ */
+ public ClientHandle[] retrieveClientList() {
+ if (lockList()) {
+ ClientHandle[] clients = retrieveClientHandles();
+ if (unlockList())
+ return clients;
+ }
+ return null;
+ }
+
+ /**
+ * adds the ClientHandle to the list - if it is not unique, then the
+ * ClientHandle object is modified to make it unique in the list. returns the
+ * clientNumber for the client in the session.
+ *
+ * @param me
+ * @return
+ */
+
+ public int addClient(ClientHandle me) {
+ syncnum = addClient(me, true);
+ unlockList();
+ return syncnum;
+ }
+
+ /**
+ * removes 'me' from the session ClientList without complaint if 'me' isn't in the clientList already.
+ * @param me
+ */
+ public void removeClient(ClientHandle me) {
+ int mynum=-1;
+ if (lockList()) {
+ ClientHandle[] clients = retrieveClientHandles();
+ if (clients != null) {
+ if (clients[syncnum-1]!=me) {
+ for (int i = 0, j = clients.length; i < j; i++)
+ if (clients[i].equals(me)) {
+ mynum=i;
+ break;
+ }
+ } else {
+ mynum=syncnum-1;
+ }
+ if (mynum>-1) {
+ ClientHandle[] newlist = new ClientHandle[clients.length - 1];
+ for (int k=0,i = 0, j = clients.length; i < j; i++)
+ if (i!=mynum)
+ newlist[k++] = clients[i];
+ if (!putClientList(clients))
+ throw new Error("Failed to write new clientList!"); // failed to put the clientList to disk.
+ }
+ }
+ unlockList();
+ } else {
+ throw new Error("Couldn't get lock for "+((filelist==null) ? "Unitialised filelist in ClientsFile" : filelist.getAbsolutePath()));
+ }
+ }
+ /**
+ * Adds a ClientHandle to the ClientList file - optionally disambiguating
+ * the ClientHandle (modifes the URN).
+ * Note: Caller is left to release the lock on the ClientList.
+ * @param me
+ * @param disambiguate -
+ * flag indicating if the URN for me should be disambiguate to
+ * differentiate between sessions.
+ * @return index of clientHandle in new list, or -1-position of existing
+ * clientHandle (if disambiguate is true)
+ */
+ protected int addClient(ClientHandle me, boolean disambiguate) {
+ int newclient = 0;
+ if (lockList()) {
+ ClientHandle[] clients = retrieveClientHandles();
+ if (me.getClientUrn()==null) {
+ // TODO: move this into ClientUrn as a standard form method.
+ me.setClientUrn("vamsas://"+me.getClientName()+":"+me.getVersion()+"/");
+ }
+ if (clients == null) {
+ clients = new ClientHandle[1];
+ clients[0] = me;
+ newclient = 1;
+ } else {
+ int k = 0;
+ for (int i = 0, j = clients.length; i < j; i++) {
+ if (clients[i].equals(me)) {
+ if (disambiguate) {
+ while (clients[i].equals(me)) {
+ me.setClientUrn(me.getClientUrn() + k++); // TODO: make a better
+ // disambiguation of
+ // urn.
+ }
+ } else {
+ // will not write the ambiguous clientHandle to disk, just return
+ // its index.
+ return -1 - i;
+ }
+ }
+ }
+ int i, j;
+ ClientHandle[] newlist = new ClientHandle[clients.length + 1];
+ for (i = 0, j = clients.length; i < j; i++)
+ newlist[i] = clients[i];
+ newlist[j] = me;
+ clients = newlist;
+ newclient = j+1;
+ }
+ if (!putClientList(clients))
+ return 0; // failed to put the clientList to disk.
+ }
+ return newclient;
+ }
+
+ /**
+ * safely writes clients array to the file referred to by filelist.
+ *
+ * @param clients
+ * @return true if successful write. Throws Errors otherwise.
+ */
+ protected boolean putClientList(ClientHandle[] clients) {
+ if (lockList()) {
+ File templist = null;
+ try {
+ templist = File.createTempFile(filelist.getName(),".old", filelist.getParentFile());
+ FileOutputStream tos = new FileOutputStream(templist);
+ tos.getChannel().transferFrom(listlock.rafile.getChannel(), 0,
+ listlock.rafile.length());
+ tos.close();
+ } catch (FileNotFoundException e1) {
+ System.err.println("Can't create temp file for clientlist");
+ e1.printStackTrace(System.err);
+ } catch (IOException e1) {
+ System.err
+ .println("Error when copying content to temp file for clientlist");
+ e1.printStackTrace(System.err);
+ }
+ if (templist != null) {
try {
- File templist=File.createTempFile(filelist.getName(),".temp",filelist);
- ObjectOutputStream os = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(templist)));
+ listlock.rafile.setLength(0);
+ ObjectOutputStream os = new ObjectOutputStream(
+ new BufferedOutputStream(new FileOutputStream(this.filelist)));
os.writeObject(clients);
os.close();
- }
- catch (FileNotFoundException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
+ // All done - remove the backup.
+ templist.delete();
+ templist = null;
} catch (Exception e) {
- e.printStackTrace();
+ if (templist != null) {
+ System.err
+ .println("Serious - problems writing to filelist. Backup in "
+ + templist.getAbsolutePath());
+ e.printStackTrace(System.err);
+ }
}
+ } else {
+ throw new Error(
+ "Couldn't create backup of the clientList before writing to it!");
}
- // else return null.
} else {
- throw new Error("Tried to retrieve a clientList without specifying client list filename");
+ throw new Error("Could not lock the clientList: "
+ + ((filelist == null) ? "Unitialized ClientsFile"
+ : " failed to get lock on " + filelist.getAbsolutePath()));
}
- return false;
+ // successful!
+ return true;
}
}
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.RandomAccessFile;
import java.nio.channels.FileLock;
/**
public class Lock {
FileLock lock = null;
+ RandomAccessFile rafile;
/**
* creates a valid Lock (test with <method>isLocked</method>)
* if a lock could be obtained for <param>lockfile</param>
return;
}
- lock = new FileOutputStream(lockfile).getChannel().tryLock();
+ lock = (rafile=new RandomAccessFile(lockfile,"rw")).getChannel().tryLock();
} catch (FileNotFoundException e) {
System.err.println("Error! Couldn't create a lockfile at "
+ lockfile.getAbsolutePath());
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) {
+ System.out.println("Application received a DOCUMENT_FINALIZEAPPDATA event.");
+ // tell app to finalize its session data prior to the storage of the current session as an archive.
+ }
+ });
+
}
public static String
Usage="ExampleApplication <vamsasFileDirectory> <vamsasSessionURN> <action> [+<arguments>]\n"
user = new UserHandle("arnolduser","deathsdoor");
vorbaclient = clientfactory.getIClient(app, user);
addHandlers(vorbaclient);
- vorbaclient.joinSession();
+ 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.
// get document data
processVamsasDocument(vorbaclient.getClientDocument());
--- /dev/null
+package org.vamsas.test.simpleclient;
+
+import java.util.Iterator;
+import java.util.Vector;
+
+import org.vamsas.client.ClientHandle;
+import org.vamsas.client.simpleclient.ClientsFile;
+
+public class ClientsFileTest {
+ private static Vector commands;
+ static {
+ ClientsFileTest.commands=new Vector();
+ ClientsFileTest.commands.add(new String("add"));
+ ClientsFileTest.commands.add(new String("remove"));
+ ClientsFileTest.commands.add(new String("list"));
+ ClientsFileTest.commands.add(new String("clear"));
+ }
+ private static void complainArgs(int argl, int argpos, String cmd, int argneed, String msg) {
+ if (argl-argpos<argneed)
+ throw new Error(cmd+" needs "+argneed+" arguments : "+msg);
+ }
+ public static void main(String[] args) {
+ java.io.File cf = new java.io.File(args[0]);
+ System.out.println("Connecting to clientFile "+args[0]);
+ ClientsFile cfhand;
+ try {
+ cfhand = new ClientsFile(cf);
+ }
+ catch (Exception e) {
+ e.printStackTrace(System.err);
+ return;
+ }
+ int argc=0;
+ while (argc<args.length) {
+ argc++;
+ Iterator coms = commands.iterator();
+ int com=-1;
+ while ((coms!=null) && coms.hasNext()) {
+ com++;
+ if (args[argc].toLowerCase().equals((String) coms.next())) {
+ System.out.println("Doing "+args[argc]);
+ ClientHandle ch;
+ argc++;
+ switch (com) {
+ case 0:
+ // Add
+ ClientsFileTest.complainArgs(args.length, argc, "add", 2, "for the Client's 'Name' and 'Version'");
+ int pos = cfhand.addClient(ch=new ClientHandle(args[argc],args[argc+1]));
+ argc+=1;
+ if (pos!=0)
+ System.out.println("Client added at "+pos+" as urn:"+ch.getClientUrn());
+ else
+ System.out.println("Client was not added.");
+ break;
+ case 1:
+ // remove
+ ClientsFileTest.complainArgs(args.length, argc, "remove", 3, "for the Client's 'Name', Version and URN");
+ ch=new ClientHandle(args[argc], args[argc+1]);
+ ch.setClientUrn(args[argc+2]);
+ argc+=2;
+ cfhand.removeClient(ch);
+ System.out.println("Client removed (apparently)");
+ break;
+ case 2:
+ // list
+ ClientHandle[] chlist = cfhand.retrieveClientList();
+ if (chlist!=null) {
+ for (int chi=0,che=chlist.length; chi<che; chi++) {
+ System.out.println("Client "+chi+" ("+chlist[chi].getClientName()+" "+chlist[chi].getVersion()+" "+chlist[chi].getClientUrn()+")");
+ }
+ } else {
+ System.out.println("Client list is empty.");
+ }
+ break;
+ case 3:
+ // clear
+ cfhand = null;
+ cf.delete();
+ try {
+ cf.createNewFile();
+ cfhand = new ClientsFile(cf);
+ }
+ catch (Exception e) {
+ System.err.println("Failed on new empty clientfile creation!");
+ e.printStackTrace(System.err);
+ }
+ break;
+ }
+ coms = null;
+ }
+ }
+ if (coms!=null) {
+ System.err.println("Unknown command : "+args[argc] + "*Ignored!*");
+ };
+ }
+ }
+
+}