package uk.ac.vamsas.client.simpleclient; 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 { private static Log log = LogFactory.getLog(EventGeneratorThread.class); private SimpleClient client; private Hashtable handlers; // manager object private VamsasSession session; /** * thread watching all the session's file objects */ protected VamsasFileWatcherThread watchThread=null; /** * Watcher element for list of all the clientHandles for the session */ protected SessionFileWatcherElement clientfile=null; /** * the session's vamsasDocument */ protected VamsasFileWatcherElement vamsasfile=null; /** * written to by client when its app calls storeDocument. */ protected SessionFileWatcherElement storeFile=null; 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; log.debug("Creating VamsasFileWatcherThread."); watchThread = new VamsasFileWatcherThread(this); initWatchers(); } private void initWatchers() { if (clientfile==null) { log.debug("Initializing clientfile Watcher"); clientfile = session.getClientWatcherElement(); // handler is set in the Vamsas session /* clientfile.setHandler(new WatcherCallBack() { public boolean handleWatchEvent(WatcherElement watcher, Lock lock) { return clientListChanged(watcher, lock); } });*/ watchThread.addElement(clientfile); } final EventGeneratorThread evgen=this; if (vamsasfile ==null) { log.debug("Initializing VamsasFileWatcher"); vamsasfile = new VamsasFileWatcherElement(session.vamArchive, new WatcherCallBack() { public boolean handleWatchEvent(WatcherElement watcher, Lock lock) { return evgen.documentChanged(lock); } }); watchThread.addElement(vamsasfile); } if (storeFile == null) { storeFile = new SessionFileWatcherElement(session.getStoreDocFile(), new WatcherCallBack() { public boolean handleWatchEvent(WatcherElement watcher, Lock lock) { return evgen.storeDocRequest(lock); } }); log.debug("Initializing storeDocFile flag watcher"); } /* */ log.debug("Watchers inited."); } /** * Call registered handlers for a vamsas session event * @param handlerEvent a named event * @param property property name to pass to handler * @param oldval old value of property to pass * @param newval new value of property to pass * @return true if event generation did not raise any exceptions. */ boolean _raise(String handlerEvent, String property, Object oldval, Object newval) { PropertyChangeSupport h = (PropertyChangeSupport) handlers.get(handlerEvent); if (h!=null) { log.debug("Triggering:"+handlerEvent); try { h.firePropertyChange(property, oldval, newval); } catch (Exception e) { log.warn("Client Exception during handling of "+handlerEvent, e); return false; } catch (Error e) { log.error("Serious! Client Error during handling of "+handlerEvent, e); return false; } log.debug("Finished :"+handlerEvent); } else log.debug("No handlers for raised "+handlerEvent); return true; } protected boolean storeDocRequest(Lock lock) { if (log.isDebugEnabled()) log.debug("StoreDocRequest on "+(lock==null ? (lock.isLocked() ? "" : "Invalid ") : "Non-")+"Existing lock"); // 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.getWatcher().exists) { _raise(Events.DOCUMENT_FINALIZEAPPDATA, client.getSessionUrn(), null, client); // expect client to write to document so update watcher state on return vamsasfile.getWatcher().setState(); lock.release(); } return true; } protected boolean documentChanged(Lock doclock) { boolean continueWatching=true; if (!block_document_updates) { session.vamArchive.fileLock=doclock; if (client.pickmanager!=null) client.pickmanager.setPassThru(false); if (log.isDebugEnabled()) { log.debug("Initiating a documentChanged event. Document is "+(client.cdocument==null ? "closed" : "open")); } // TODO: decide if individual object update handlers are called as well as overall event handler if (!_raise(Events.DOCUMENT_UPDATE, client.getSessionUrn(), null, client)) { log.info("Recovering from errors or exceptions generated by client application"); if (client.cdocument!=null) { try { client.tidyAwaySessionDocumentState(); } catch (Exception e) { log.warn("Exception generated by vamsas library - when tidying away session document:",e); } catch (Error e) { log.error("LIBRARY Implementation error - when tidying away session document:",e); } } } if (client.pickmanager!=null) client.pickmanager.setPassThru(true); if (log.isDebugEnabled()) { log.debug("Finished handling a documentChanged event. Document is "+(client.cdocument==null ? "closed" : "open")); } if (client.cdocument!=null) { log.warn("Implementation Error ? ClientDocument instance has not been closed or updated by handler!"); } /*try { client._session.getVamsasDocument().closeArchive(); } catch (Exception e) {log.warn("Unexpected exception when closing document after update.",e);}; */ } else { // TODO: check documentChanged */ log.debug("Ignoring documentChanged event for "+client.getSessionUrn()); } return continueWatching; } boolean ownsf = false; /** * Moved to SimpleClientSessionManager * scans all watchers and fires changeEvents if necessary * @return number of events generated. */ private boolean clientListChanged(WatcherElement clientfile, Lock watchlock) { log.debug("ClientListChanged handler called for "+clientfile.getWatcher().getSubject()); // could make this general - but for now keep simple if (watchlock!=null) { // TODO: compare new client list to old list version. is it changed ? // 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. } return true; } /** * 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()