1 package uk.ac.vamsas.client.simpleclient;
3 import java.beans.PropertyChangeListener;
4 import java.beans.PropertyChangeSupport;
5 import java.util.Hashtable;
7 import org.apache.commons.logging.Log;
8 import org.apache.commons.logging.LogFactory;
10 import uk.ac.vamsas.client.Events;
13 * monitors watcher objects and generates events.
15 public class EventGeneratorThread {
16 private static Log log = LogFactory.getLog(EventGeneratorThread.class);
17 private SimpleClient client;
18 private Hashtable handlers; // manager object
19 private VamsasSession session;
21 * thread watching all the session's file objects
23 protected VamsasFileWatcherThread watchThread=null;
25 * Watcher element for list of all the clientHandles for the session
27 protected SessionFileWatcherElement clientfile=null;
29 * the session's vamsasDocument
31 protected VamsasFileWatcherElement vamsasfile=null;
33 * written to by client when its app calls storeDocument.
35 protected SessionFileWatcherElement storeFile=null;
37 EventGeneratorThread(VamsasSession s, SimpleClient _client, Hashtable eventhandlers) {
38 if (eventhandlers==null || s==null || _client==null)
39 throw new Error("Null arguments to EventGeneratorThread constructor.");
40 handlers = eventhandlers;
43 log.debug("Creating VamsasFileWatcherThread.");
44 watchThread = new VamsasFileWatcherThread(this);
48 private void initWatchers() {
49 if (clientfile==null) {
50 log.debug("Initializing clientfile Watcher");
51 clientfile = session.getClientWatcherElement();
52 clientfile.setHandler(new WatcherCallBack() {
54 public boolean handleWatchEvent(WatcherElement watcher, Lock lock) {
55 // TODO Auto-generated method stub
56 return clientListChanged(watcher, lock);
59 watchThread.addElement(clientfile);
61 final EventGeneratorThread evgen=this;
63 if (vamsasfile ==null) {
64 log.debug("Initializing VamsasFileWatcher");
65 vamsasfile = new VamsasFileWatcherElement(session.vamArchive,
66 new WatcherCallBack() {
67 public boolean handleWatchEvent(WatcherElement watcher, Lock lock) {
68 return evgen.documentChanged(lock);
71 watchThread.addElement(vamsasfile);
73 if (storeFile == null) {
74 storeFile = new SessionFileWatcherElement(session.getStoreDocFile(),
75 new WatcherCallBack() {
76 public boolean handleWatchEvent(WatcherElement watcher, Lock lock) {
77 return evgen.storeDocRequest(lock);
80 log.debug("Initializing storeDocFile flag watcher");
84 log.debug("Watchers inited.");
86 void _raise(String handlerEvent, String property, Object oldval, Object newval) {
87 PropertyChangeSupport h = (PropertyChangeSupport) handlers.get(handlerEvent);
89 log.debug("Triggering:"+handlerEvent);
91 h.firePropertyChange(property, oldval, newval);
92 } catch (Exception e) {
93 log.warn("Client Exception during handling of "+handlerEvent, e);
95 log.debug("Finished :"+handlerEvent);
97 log.debug("No handlers for raised "+handlerEvent);
99 protected boolean storeDocRequest(Lock lock) {
100 if (log.isDebugEnabled())
101 log.debug("StoreDocRequest on "+(lock==null ? (lock.isLocked() ? "" : "Invalid ") : "Non-")+"Existing lock");
102 // 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).
103 if (storeFile.getWatcher().exists) {
104 _raise(Events.DOCUMENT_FINALIZEAPPDATA, client.getSessionUrn(), null, client);
105 // expect client to write to document so update watcher state on return
106 vamsasfile.getWatcher().setState();
112 protected boolean documentChanged(Lock doclock) {
113 boolean continueWatching=true;
114 if (!block_document_updates) {
115 session.vamArchive.fileLock=doclock;
116 if (client.pickmanager!=null)
117 client.pickmanager.setPassThru(false);
118 // TODO: decide if individual object update handlers are called as well as overall event handler
119 _raise(Events.DOCUMENT_UPDATE, client.getSessionUrn(), null, client);
120 if (client.pickmanager!=null)
121 client.pickmanager.setPassThru(true);
122 if (log.isDebugEnabled()) {
123 log.debug("Finished handling a documentChanged event. Document is "+(client.cdocument==null ? "closed" : "open"));
126 client._session.getVamsasDocument().closeArchive();
127 } catch (Exception e) {log.warn("Unexpected exception when closing document after update.",e);};
130 // TODO: check documentChanged */
131 log.debug("Ignoring documentChanged event for "+client.getSessionUrn());
133 return continueWatching;
135 boolean ownsf = false;
137 * scans all watchers and fires changeEvents if necessary
138 * @return number of events generated.
140 private boolean clientListChanged(WatcherElement clientfile, Lock watchlock) {
141 log.debug("ClientListChanged handler called for "+clientfile.getWatcher().getSubject());
142 // could make this general - but for now keep simple
143 if (watchlock!=null) {
144 // TODO: compare new client list to old list version. is it changed ?
145 // see what happened to the clientfile - compare our internal version with the one in the file, or just send the updated list out...?
148 * Generated when a new vamsas client is attached to a session (Handle is
149 * passed) Note: the newly created client does not receive the event.
151 public static final String CLIENT_CREATION = "uk.ac.vamsas.client.events.clientCreateEvent";
154 * Generated when a vamsas client leaves a session (Handle is passed to all
156 public static final String CLIENT_FINALIZATION = "uk.ac.vamsas.client.events.clientFinalizationEvent";
157 */ // again - as the test.
162 * Events raised by IClient and propagated to others in session
166 * number of milliseconds between any file state check.
169 protected void wait(int u) {
172 long l = System.currentTimeMillis()+POLL_UNIT*u;
173 while (System.currentTimeMillis()<l)
178 private boolean block_document_updates=false;
179 int STORE_WAIT=5; // how many units before we decide all clients have finalized their appdatas
180 private boolean in_want_to_store_phase=false;
182 * client App requests offline storage of vamsas data.
183 * Call blocks whilst other apps do any appData finalizing
184 * and then returns (after locking the vamsasDocument in the session)
185 * Note - the calling app may also receive events through the EventGeneratorThread for document updates.
187 * @return Lock for session.vamArchive
188 * @param STORE_WAIT indicates how lock the call will block for when nothing appears to be happening to the session.
190 protected Lock want_to_store() {
191 if (in_want_to_store_phase) {
192 log.error("client error: want_to_store called again before first call has completed.");
195 in_want_to_store_phase=true;
196 // TODO: test the storeDocumentRequest mechanism
197 /*/ watchThread.haltWatchers();
199 log.debug("Stopping document_update watcher");
200 vamsasfile.haltWatch();
201 // block_document_updates=true;
202 log.debug("Cleared flag for ignoring document_update requests");
204 log.debug("Sending Store Document Request");
206 session.addStoreDocumentRequest(client.getClientHandle(), client.getUserHandle());
207 } catch (Exception e) {
208 log.warn("Whilst writing StoreDocumentRequest for "+client.getClientHandle().getClientUrn()+" "+client.getUserHandle(),
210 log.info("trying to continue after storeDocumentRequest exception.");
212 log.debug("Waiting for other apps to do FinalizeApp handling.");
213 // LATER: refine this semaphore process
214 // to make a robust signalling mechanism:
215 // app1 requests, app1..n do something (or don't - they may be dead),
216 // app1 realises all apps have done their thing, it then continues with synchronized data.
217 // this probably needs two files - a request file,
218 // and a response file which is acknowledged by the app1 requestor for each app.
219 // eventually, no more responses are received for the request, and the app can then only continue with its store.
220 FileWatcher sfwatcher=session.getStoreWatcher();
221 FileWatcher vfwatcher=session.getDocWatcher();
222 int units = 0; // zero if updates occured over a sleep period
223 while (units<STORE_WAIT) {
225 Thread.sleep(watchThread.WATCH_SLEEP);
226 } catch (InterruptedException e) {
227 log.debug("interrupted.");
229 if (sfwatcher.hasChanged() || vfwatcher.hasChanged()) {
236 block_document_updates=false;
237 vamsasfile.enableWatch();
238 log.debug("Cleared flag for ignoring document_update requests");
239 // wait around again (until our own watcher has woken up and synchronized).
240 while (units<STORE_WAIT) {
242 Thread.sleep(watchThread.WATCH_SLEEP);
243 } catch (InterruptedException e) {
244 log.debug("interrupted.");
246 if (sfwatcher.hasChanged() || vfwatcher.hasChanged())
253 log.debug("finished waiting.");
254 in_want_to_store_phase=false;
255 return session.vamArchive.getLock();
258 * count handlers for a particular vamsas event
259 * @param event string enumeration from uk.ac.vamsas.client.Events
260 * @return -1 for an invalid event, otherwise the number of handlers
262 protected int countHandlersFor(String event) {
263 if (handlers.containsKey(event)) {
264 PropertyChangeSupport handler = (PropertyChangeSupport) handlers.get(event);
265 PropertyChangeListener[] listeners;
267 return ((listeners=handler.getPropertyChangeListeners())==null)
268 ? -1 : listeners.length;
273 public void disableDocumentWatch() {
274 vamsasfile.haltWatch();
277 public boolean isDocumentWatchEnabled() {
278 return (vamsasfile!=null) && vamsasfile.isWatchEnabled();
281 public void enableDocumentWatch() {
282 vamsasfile.enableWatch();
285 public boolean isWatcherAlive() {
286 return watchThread!=null && watchThread.running && watchThread.isAlive();
289 public void interruptWatching() {
290 if (watchThread!=null && watchThread.isAlive())
291 watchThread.interrupt();
295 * called to start the session watching thread which generates events
297 public void startWatching() {
298 enableDocumentWatch();
300 while (!watchThread.running && watchThread.isAlive())
301 log.debug("Waiting until watcher is really started.");
304 public void stopWatching() {
306 watchThread.haltWatchers();