package uk.ac.vamsas.client.simpleclient; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.util.Hashtable; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import uk.ac.vamsas.client.Events; /** * monitors watcher objects and generates events. */ public class EventGeneratorThread extends Thread implements Runnable { private static Log log = LogFactory.getLog(EventGeneratorThread.class); private SimpleClient client; private Hashtable handlers; // manager object private VamsasSession session; /** * list with all the clientHandles for the session */ protected FileWatcher clientfile=null; /** * the session's vamsasDocument */ protected FileWatcher vamsasfile=null; /** * written to by client when its app calls storeDocument. */ protected FileWatcher storeFile=null; private boolean watch=false; EventGeneratorThread(VamsasSession s, SimpleClient _client, Hashtable eventhandlers) { if (eventhandlers==null || s==null || _client==null) throw new Error("Null arguments to EventGeneratorThread constructor."); handlers = eventhandlers; session = s; client = _client; setName(s.sessionDir.getName()); initWatchers(); } private void initWatchers() { if (clientfile==null) clientfile = session.getClientWatcher(); if (vamsasfile ==null) vamsasfile = session.getDocWatcher(); if (storeFile == null) storeFile = session.getStoreWatcher(); clientfile.setState(); vamsasfile.setState(); storeFile.setState(); } boolean ownsf = false; /** * scans all watchers and fires changeEvents if necessary * @return number of events generated. */ private int checkforEvents() { Lock watchlock; //TODO : leave slog.info messages for the events that occur. int raised=0; // could make this general - but for now keep simple if ((watchlock=storeFile.getChangedState())!=null) { // TODO: define the storeFile semaphore mechanism : file exists - all clients inform their apps, and then the client that wrote the file should delete the file (it should hold the lock to it). if (storeFile.exists) { PropertyChangeSupport h = (PropertyChangeSupport) handlers.get(Events.DOCUMENT_FINALIZEAPPDATA); if (h!=null) { log.debug("Triggering DOCUMENT_FINALIZEAPPDATA"); raised++; h.firePropertyChange(client.getSessionUrn(), null, client); // expect client to vamsasfile.setState(); } } } if ((watchlock=clientfile.getChangedState())!=null) { // see what happened to the clientfile - compare our internal version with the one in the file, or just send the updated list out...? // /** * 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 = "uk.ac.vamsas.client.events.clientCreateEvent"; */ // as the test /** * Generated when a vamsas client leaves a session (Handle is passed to all * others). public static final String CLIENT_FINALIZATION = "uk.ac.vamsas.client.events.clientFinalizationEvent"; */ // again - as the test. raised++; } if ((watchlock=vamsasfile.getChangedState())!=null) { /** * 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 = "uk.ac.vamsas.client.events.documentUpdateEvent"; */ // read apphandle from 'lastUpdate' session file. // pass apphandle name to appHandler ? } /** * 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 = "uk.ac.vamsas.client.events.documentCreateEvent"; */ // check if this session's appInit flag is set - if not - generate event for this app. // prolly don't need this at the moment - when an app does getDocument it can to the initing then. /** * Generated prior to session Shutdown, after the last participating vamsas * client has finalized. * TODO: decide on purpose of this ? is this for benefit of multi-session Apps only ? public static final String SESSION_SHUTDOWN = "uk.ac.vamsas.client.events.SessionShutdownEvent"; */ /** * 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. * EventName: * NewValue: uk.ac.vamsas.client.IClient for session. * public static final String DOCUMENT_FINALIZEAPPDATA = "uk.ac.vamsas.client.events.DocumentFinalizeAppData"; */ // watch for finalization semaphore (last finalised sessionFile). /** * 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"; return raised; } private void initEvents() { } /** * Events raised by IClient and propagated to others in session */ /** * number of milliseconds between any file state check. */ long POLL_UNIT = 20; protected void wait(int u) { if (u<=0) u=1; long l = System.currentTimeMillis()+POLL_UNIT*u; while (System.currentTimeMillis()