refactored org to uk
[vamsas.git] / src / uk / ac / vamsas / client / simpleclient / EventGeneratorThread.java
1 package uk.ac.vamsas.client.simpleclient;
2
3 import java.beans.PropertyChangeEvent;
4 import java.beans.PropertyChangeListener;
5 import java.beans.PropertyChangeSupport;
6 import java.util.Hashtable;
7
8 import org.apache.commons.logging.Log;
9 import org.apache.commons.logging.LogFactory;
10
11 import uk.ac.vamsas.client.Events;
12
13 /**
14  * monitors watcher objects and generates events.
15  */
16 public class EventGeneratorThread extends Thread implements Runnable {
17   private static Log log = LogFactory.getLog(EventGeneratorThread.class);
18   private SimpleClient client;
19   private Hashtable handlers; // manager object
20   private VamsasSession session;
21
22   /** 
23    * list with all the clientHandles for the session
24    */
25   protected FileWatcher clientfile=null;
26   /**
27    * the session's vamsasDocument
28    */
29   protected FileWatcher vamsasfile=null;
30   /**
31    * written to by client when its app calls storeDocument.
32    */
33   protected FileWatcher storeFile=null;
34   
35   private boolean watch=false;
36   
37   
38   EventGeneratorThread(VamsasSession s, SimpleClient _client, Hashtable eventhandlers) {
39     if (eventhandlers==null || s==null || _client==null)
40       throw new Error("Null arguments to EventGeneratorThread constructor.");
41     handlers = eventhandlers;
42     session = s;
43     client = _client;
44     setName(s.sessionDir.getName());
45     initWatchers();
46   }
47   
48   private void initWatchers() {
49     if (clientfile==null)
50       clientfile = session.getClientWatcher();
51     if (vamsasfile ==null)
52       vamsasfile = session.getDocWatcher();
53     if (storeFile == null)
54       storeFile = session.getStoreWatcher();
55     clientfile.setState();
56     vamsasfile.setState();
57     storeFile.setState();
58   }
59   boolean ownsf = false;
60   /**
61    * scans all watchers and fires changeEvents if necessary
62    * @return number of events generated.
63    */
64   private int checkforEvents() {
65     Lock watchlock;
66     //TODO : leave slog.info messages for the events that occur.
67     int raised=0;
68     // could make this general - but for now keep simple
69     if ((watchlock=storeFile.getChangedState())!=null) {
70       // 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).
71       if (storeFile.exists) { 
72         PropertyChangeSupport h = (PropertyChangeSupport) handlers.get(Events.DOCUMENT_FINALIZEAPPDATA);
73         if (h!=null) {
74           log.debug("Triggering DOCUMENT_FINALIZEAPPDATA");
75           raised++;
76           h.firePropertyChange(client.getSessionUrn(), null, client);
77           // expect client to 
78           vamsasfile.setState();
79         }
80       }
81     }
82     if ((watchlock=clientfile.getChangedState())!=null) {
83       // see what happened to the clientfile - compare our internal version with the one in the file, or just send the updated list out...?
84       //
85       /**
86        * Generated when a new vamsas client is attached to a session (Handle is
87        * passed) Note: the newly created client does not receive the event.
88        *
89       public static final String CLIENT_CREATION = "uk.ac.vamsas.client.events.clientCreateEvent";
90   */ // as the test
91       /**
92        * Generated when a vamsas client leaves a session (Handle is passed to all
93        * others).
94       public static final String CLIENT_FINALIZATION = "uk.ac.vamsas.client.events.clientFinalizationEvent";
95        */ // again - as the test.
96           raised++;
97     }
98     if ((watchlock=vamsasfile.getChangedState())!=null) {
99       
100       /**
101        * Generated when a client has finished updating the document. Passes
102        * applicationHandle of client so the updating client can recognise its own
103        * updates.
104       public static final String DOCUMENT_UPDATE = "uk.ac.vamsas.client.events.documentUpdateEvent";
105        */
106       // read apphandle from 'lastUpdate' session file.
107       // pass apphandle name to appHandler ?
108       
109     }
110     /**
111      * Generated when a new vamsas document is created (perhaps from some existing
112      * Vamsas data) so an application may do its own data space initialization.
113      * TODO: decide if this is called when an app is connected to a stored
114      * session...
115      public static final String DOCUMENT_CREATE = "uk.ac.vamsas.client.events.documentCreateEvent";
116     */
117     // check if this session's appInit flag is set - if not - generate event for this app.
118     // prolly don't need this at the moment - when an app does getDocument it can to the initing then.
119     
120
121     /**
122      * Generated prior to session Shutdown, after the last participating vamsas
123      * client has finalized.
124      *  TODO: decide on purpose of this ?  is this for benefit of multi-session Apps only ?
125     public static final String SESSION_SHUTDOWN = "uk.ac.vamsas.client.events.SessionShutdownEvent";
126      */
127
128     /**
129      * Generated for all clients when any client calls IClient.storeDocument() to
130      * allow them to store any updates before an offline copy of the session is
131      * created. Any client that handles this should call the
132      * IClient.getDocument(), update and then IClient.updateDocument in the same
133      * handler thread.
134      * EventName: <Vamsas-session URN>
135      * NewValue: uk.ac.vamsas.client.IClient for session.
136      *
137     public static final String DOCUMENT_FINALIZEAPPDATA = "uk.ac.vamsas.client.events.DocumentFinalizeAppData";
138 */
139     // watch for finalization semaphore (last finalised sessionFile).
140     
141     /**
142      * Generated by Vorba stub after the penultimate client makes a call to
143      * closeDocument(). Sequence is as follows : 1. All other vamsas clients have
144      * called closeDocument() 2. Final living client monitors closures, and
145      * realises that it is last. 3. Final client generates event to prompt
146      * associated application to inquire if the user wishes to save the document
147      * for future reference.
148      *  * Any call to closeDocument in a thread other than the registered
149      * EventListener will block until the RequestToClose handler has exited.
150      * 
151      */
152     //    public static final String DOCUMENT_REQUESTTOCLOSE = "org.vamas.client.DocumentRequestToCloseEvent";
153
154     return raised;
155   }
156   
157   private void initEvents() {
158     
159   }
160   /**
161    * Events raised by IClient and propagated to others in session
162    */
163   
164   /**
165    * number of milliseconds between any file state check.
166    */
167   long POLL_UNIT = 20;
168   protected void wait(int u) {
169     if (u<=0)
170       u=1;
171     long l = System.currentTimeMillis()+POLL_UNIT*u;
172       while (System.currentTimeMillis()<l)
173         ;
174   }
175     
176   
177   private boolean block_document_updates=false;
178   int STORE_WAIT=5; // how many units before we decide all clients have finalized their appdatas
179   
180   /**
181    * client App requests offline storage of vamsas data. 
182    * Call blocks whilst other apps do any appData finalizing
183    * and then returns (after locking the vamsasDocument in the session)
184    * Note - the calling app may also receive events through the EventGeneratorThread for document updates.
185    * 
186    * @return Lock for session.vamArchive 
187    * @param STORE_WAIT indicates how lock the call will block for when nothing appears to be happening to the session.
188    */
189   protected Lock want_to_store() {
190     log.debug("Setting flag for document_update requests to be ignored");
191     block_document_updates=true;
192     log.debug("Waiting for other apps to do FinalizeApp handling.");
193     try {
194       session.addStoreDocumentRequest(client.getClientHandle(), client.getUserHandle());
195     } catch (Exception e) {
196       log.warn("Whilst writing StoreDocumentRequest for "+client.getClientHandle().getClientUrn()+" "+client.getUserHandle(),
197           e);
198       log.info("trying to continue.");
199     }
200     // LATER: refine this semaphore process 
201     // to make a robust signalling mechanism:
202     // app1 requests, app1..n do something (or don't - they may be dead), 
203     // app1 realises all apps have done their thing, it then continues with synchronized data.
204     // this probably needs two files - a request file, 
205     //  and a response file which is acknowledged by the app1 requestor for each app.
206     //  eventually, no more responses are received for the request, and the app can then only continue with its store.
207     int units = 0;
208     while (units<STORE_WAIT) {
209       wait(1);
210       if (storeFile.hasChanged() || vamsasfile.hasChanged())
211         units=0;
212       else
213         units++;
214     }
215     
216     block_document_updates=false;
217     log.debug("Cleared flag for ignoring document_update requests");
218     // wait around again (until our own watcher has woken up and synchronized).
219     while (units<STORE_WAIT) {
220       wait(1);
221       if (storeFile.hasChanged() || vamsasfile.hasChanged())
222         units=0;
223       else
224         units++;
225     }
226     
227     
228     log.debug("finished waiting.");
229     return session.vamArchive.getLock();
230   }
231   /**
232    * count handlers for a particular vamsas event 
233    * @param event string enumeration from uk.ac.vamsas.client.Events
234    * @return -1 for an invalid event, otherwise the number of handlers
235    */
236   protected int countHandlersFor(String event) {
237     if (handlers.containsKey(event)) {
238       PropertyChangeSupport handler = (PropertyChangeSupport) handlers.get(event);
239       PropertyChangeListener[] listeners;
240       if (handler!=null)
241         return ((listeners=handler.getPropertyChangeListeners())==null) 
242         ? -1 : listeners.length;
243     }
244     return -1;
245   }
246   /**
247    * probably don't need any of these below.
248    */
249   /* (non-Javadoc)
250    * @see java.lang.Thread#destroy()
251    */
252   public void destroy() {
253     super.destroy();
254   }
255   /* (non-Javadoc)
256    * @see java.lang.Thread#interrupt()
257    */
258   public void interrupt() {
259     // TODO Auto-generated method stub
260     super.interrupt();
261   }
262   /* (non-Javadoc)
263    * @see java.lang.Thread#isInterrupted()
264    */
265   public boolean isInterrupted() {
266     // TODO Auto-generated method stub
267     return super.isInterrupted();
268   }
269   /* (non-Javadoc)
270    * @see java.lang.Thread#run()
271    */
272   public void run() {
273     // TODO Auto-generated method stub
274     super.run();
275   }
276   
277
278 }