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