b24ef687cc92a476a3760151387c722e59ea59dc
[vamsas.git] / src / uk / ac / vamsas / client / simpleclient / VamsasSession.java
1 package uk.ac.vamsas.client.simpleclient;
2
3 import java.io.File;
4 import java.io.FileNotFoundException;
5 import java.io.IOException;
6 import java.io.RandomAccessFile;
7 import java.nio.channels.FileChannel;
8 import java.nio.channels.OverlappingFileLockException;
9
10 import org.apache.commons.logging.Log;
11 import org.apache.commons.logging.LogFactory;
12 import org.apache.log4j.FileAppender;
13 import org.apache.log4j.Logger;
14 import org.apache.log4j.PatternLayout;
15
16 import uk.ac.vamsas.client.ClientHandle;
17 import uk.ac.vamsas.client.Events;
18 import uk.ac.vamsas.client.IClient;
19 import uk.ac.vamsas.client.SessionHandle;
20 import uk.ac.vamsas.client.UserHandle;
21 /**
22  * Does all the IO operations for a SimpleClient instance accessing 
23  * a SimpleClient vamsas session.
24  * 
25  * Basically, it defines the various standard names for the files 
26  * in the session directory (that maps to the sessionUrn), 
27  * provides constructors for the file handlers and watchers of 
28  * those file entities, and some higher level methods 
29  * to check and change the state flags for the session.
30  * 
31  * TODO: move the stuff below to the SimpleClientFactory documentation.
32  * much may not be valid now :
33  * Vamsas client is intialised with a path to create live session directories. 
34  * This path may contain a vamsas.properties file 
35  * that sets additional parameters (otherwise client 
36  * just uses the one on the classpath).
37  * 
38  * A vamsas session consists of :
39  *  SessionDir - translates to urn of a live session.
40  *  Contains: Vamsas Document (as a jar), Session client list file, 
41  *  both of which may be locked, and additional 
42  *  temporary versions of these files when write 
43  *  operations are taking place.
44  * 
45  * Zip file entries
46  *  - vamsasdocument.xml : core info
47  *  one or more:
48  *  - <applicationname>.version.sessionnumber.raw (string given in vamsasdocument.xml applicationData entry)
49  *  
50  * Lockfile
51  *  - filename given in the vamsasdocument.xml. Should be checked for validity by any client and rewritten if necessary. 
52  *    The lockfile can point to the jar itself.
53  * Mode of operation.
54  * Initially - documentHandler either:
55  *  - creates a zip for a new session for the client
56  *  - connect to an existing session zip 
57  *   1. reads session urn file
58  *   2. waits for lock
59  *   3. examines session - decide whether to create new application data slice or connect to one stored in session.
60  *   4. writes info into session file
61  *   5. releases lock and generates local client events.
62  *   6. Creates Watcher thread to generate events.
63  * 
64  * During the session
65  *  - Update watcher checks for file change - 
66  * 
67  * Procedures for file based session message exchange
68  *  - session document modification flag
69  *    
70  */
71
72 public class VamsasSession {
73   /**
74    * indicator file for informing other processes that 
75    * they should finalise their vamsas datasets for 
76    * storing into a vamsas archive.
77    */
78   public static final String CLOSEANDSAVE_FILE="stored.log";
79   /**
80    * session file storing the last_stored_stat data 
81    */
82   public static final String MODIFIEDDOC_FILE="modified";
83
84   
85   private SimpleSessionManager sessionManager = null;
86   
87   /**
88    * Count of cycles before considering the current client as the last one of the session (if no other client registered as active )
89    */
90   private final int watchCycleCountBeforeLastClient = 1220 ;
91   
92   /**
93    * time between checking 
94    */
95   public int WATCH_SLEEP=30; 
96   
97   protected String clientFileDirectory = "clients";
98   
99   /**
100    * called to clear update flag after a successful offline storage event
101    */
102   protected void clearUnsavedFlag() {
103     SessionFlagFile laststored = new SessionFlagFile(new File(sessionDir, MODIFIEDDOC_FILE));
104     if (!laststored.clearFlag())
105       log.warn("Unsaved flag was not cleared for "+sessionDir);
106   }
107   /**
108    * called to indicate session document has been modified.
109    *
110    */
111   protected void setUnsavedFlag() {
112     SessionFlagFile laststored = new SessionFlagFile(new File(sessionDir, MODIFIEDDOC_FILE));
113     if (!laststored.setFlag())
114       log.warn("Couldn't set the Unsaved flag for "+sessionDir);
115   }
116   /**
117    * 
118    * @return true if session document has been modified since last offline storage event 
119    */
120   protected boolean getUnsavedFlag() {
121     SessionFlagFile laststored = new SessionFlagFile(new File(sessionDir, MODIFIEDDOC_FILE));
122     return laststored.checkFlag();
123   }
124   /**
125    * log file location
126    */
127   public static final String SESSION_LOG="Log.txt";
128   private static Log log = LogFactory.getLog(VamsasSession.class);
129   protected Logger slog = Logger.getLogger("uk.ac.vamsas.client.SessionLog");
130   /**
131    * setup the sessionLog using Log4j.
132    * @throws IOException
133    */
134   private void initLog() throws IOException {
135     // TODO: fix session event logging
136     // LATER: make dedicated appender format for session log.
137     /*Appender app = slog.getAppender("log4j.appender.SESSIONLOG");
138     // slog.addAppender(new FileAppender(app.getLayout(), new File(sessionDir, SESSION_LOG).getAbsolutePath()));
139     // slog.addAppender(new FileAppender(app.getLayout(), new File(sessionDir, SESSION_LOG).getAbsolutePath()));
140     for (Enumeration e = slog.getAllAppenders() ; e.hasMoreElements() ;) {
141       System.out.println(e.nextElement());
142
143     }*/
144   
145     if (slog!= null ) {
146       File sessionLogFile =  new File(this.sessionDir, SESSION_LOG);
147       slog.addAppender(new FileAppender(new PatternLayout("%-4r [%t] %-5p %c %x - %m%n"), sessionLogFile.getAbsolutePath(), true));
148     } else {
149       log.info("No appender for SessionLog");
150     }
151   }
152   
153   /**
154    * the sessionDir is given as the session location for new clients.
155    */
156   protected File sessionDir;
157   /**
158    * holds the list of attached clients
159    */
160   ClientsFile clist;
161   public static final String CLIENT_LIST="Clients.obj";
162   /**
163    * holds the data
164    */
165   VamsasFile vamArchive; 
166   public static final String VAMSAS_OBJ="VamDoc.jar";
167   
168   /**
169    * sets up the vamsas session files and watchers in sessionDir
170    * @param sessionDir1
171    */
172   protected VamsasSession(File sessionDir1) throws IOException {
173     if (sessionDir1==null)
174       throw new Error("Null directory for VamsasSession.");
175     if (sessionDir1.exists()) {
176       if (!sessionDir1.isDirectory() || !sessionDir1.canWrite() || !sessionDir1.canRead())
177         throw new IOException("Cannot access '"+sessionDir1+"' as a read/writable Directory.");
178       if (!checkSessionFiles(sessionDir1))
179       log.warn("checkSessionFiles() returned false. Possible client implementation error");
180       this.sessionDir = sessionDir1; 
181       initSessionObjects();
182       slog.debug("Initialising additional VamsasSession instance");
183       log.debug("Attached to VamsasSession in "+sessionDir1);
184       //} 
185     } else {
186       // start from scratch
187       if (!sessionDir1.mkdir())
188         throw new IOException("Failed to make VamsasSession directory in "+sessionDir1);
189       createSessionFiles();
190       initSessionObjects();
191       slog.debug("Session directory created.");
192       log.debug("Initialised VamsasSession in "+sessionDir1);
193     }
194   }
195   /**
196    * tests presence of existing sessionfiles files in dir
197    * @param dir
198    * @return
199    */
200   private boolean checkSessionFiles(File dir) throws IOException {
201     File c_file = new File(dir,CLIENT_LIST);
202     File v_doc = new File(dir,VAMSAS_OBJ);
203     if (c_file.exists() && v_doc.exists())
204       return true;
205     return false;
206   }
207   /**
208    * create new empty files in dir
209    *
210    */
211   private void createSessionFiles() throws IOException {
212     if (sessionDir==null)
213       throw new IOException("Invalid call to createSessionFiles() with null sessionDir");
214     File c_file = new File(sessionDir,CLIENT_LIST);
215     File v_doc = new File(sessionDir,VAMSAS_OBJ);
216     if (!c_file.exists() && c_file.createNewFile())
217       log.debug("Created new ClientFile "+c_file); // don't care if this works or not
218     if (!v_doc.exists() && v_doc.createNewFile())
219       log.debug("Created new Vamsas Session Document File "+v_doc); 
220   }
221   /**
222    * construct SessionFile objects and watchers for each
223    */
224   private void initSessionObjects() throws IOException {
225     createSessionFiles();
226     if (clist!=null || vamArchive!=null)
227       throw new IOException("initSessionObjects called for initialised VamsasSession object.");
228     clist = new ClientsFile(new File(sessionDir,CLIENT_LIST));
229     vamArchive = new VamsasFile(new File(sessionDir,VAMSAS_OBJ));
230     storedocfile=new ClientsFile(new File(sessionDir, CLOSEANDSAVE_FILE));
231     initLog();
232   }
233   /**
234    * make a new watcher object for the clientFile
235    * @return new ClientFile watcher instance
236    */
237  public FileWatcher getClientWatcher() {
238     return new FileWatcher(clist.sessionFile);
239   }
240   /**
241    * make a new watcher object for the vamsas Document
242    * @return new ClientFile watcher instance
243    */
244   public FileWatcher getDocWatcher() {
245     return new FileWatcher(vamArchive.sessionFile);
246   }
247   FileWatcher store_doc_file=null;
248   public ClientsFile storedocfile=null;
249   /**
250    * make a new watcher object for the messages file
251    * @return new watcher instance
252    */
253   public FileWatcher getStoreWatcher() {
254     return new FileWatcher(new File(sessionDir,CLOSEANDSAVE_FILE));
255
256   }
257   /**
258    * write to the StoreWatcher file to indicate that a storeDocumentRequest has been made.
259    * The local client's storeWatcher FileWatcher object is updated so the initial change is not registered.
260    * @param client
261    * @param user
262    * @return
263    */
264   public void addStoreDocumentRequest(ClientHandle client, UserHandle user) throws IOException {
265     // TODO: replace this with clientsFile mechanism
266     SessionFile sfw = new SessionFile(new File(sessionDir, CLOSEANDSAVE_FILE));
267     while (!sfw.lockFile())
268       log.debug("Trying to get lock for "+CLOSEANDSAVE_FILE);
269     RandomAccessFile sfwfile=sfw.fileLock.getRaFile();
270     sfwfile.setLength(0); // wipe out any old info.
271     // TODO: rationalise what gets written to this file (ie do we want other clients to read the id of the requestor?)
272     sfwfile.writeUTF(client.getClientUrn()+":"+user.getFullName()+"@"+user.getOrganization());
273     sfw.unlockFile();
274     if (store_doc_file!=null)
275       store_doc_file.setState();
276     slog.info("FinalizeAppData request from "+user.getFullName()+" using "+client.getClientUrn()+"");
277   }
278   /**
279    * create a new session with an existing vamsas Document - by copying it into the session.
280    * @param archive
281    */
282   public void setVamsasDocument(File archive) throws IOException {
283     log.debug("Transferring vamsas data from "+archive+" to session:"+vamArchive.sessionFile);
284     SessionFile xtantdoc = new SessionFile(archive);
285     vamArchive.updateFrom(null, xtantdoc);
286     // LATER: decide if session archive provenance should be updated to reflect access.
287     // TODO: soon! do a proper import objects from external file 
288     log.debug("Transfer complete.");
289   }
290   /**
291    * write session as a new vamsas Document (this will overwrite any existing file without warning)
292    * TODO: test
293    * TODO: verify that lock should be released for vamsas document.
294    * @param destarchive
295    */
296   protected void writeVamsasDocument(File destarchive, Lock extlock) throws IOException {
297     log.debug("Transferring vamsas data from "+vamArchive.sessionFile+" to session:"+destarchive);
298     SessionFile newdoc = new SessionFile(destarchive);
299     if (extlock==null && !vamArchive.lockFile())
300       while (!vamArchive.lockFile())
301         log.info("Trying to get lock for "+vamArchive.sessionFile);
302     // TODO: LATER: decide if a provenance entry should be written in the exported document recording the export from the session
303     newdoc.updateFrom(extlock, vamArchive);
304     // LATER: LATER: fix use of updateFrom for file systems where locks cannot be made (because they don't have a lockManager, ie NFS/Unix, etc).
305     vamArchive.unLock();
306     newdoc.unlockFile();
307     log.debug("Transfer complete.");
308   }
309   /**
310          * extant archive IO handler
311          */
312   VamsasArchive _va=null;
313   /**
314    * Creates a VamsasArchive Vobject for accessing and updating document
315    * Note: this will lock the Vamsas Document for exclusive access to the client.
316    * @return session vamsas document
317    * @throws IOException if locks fail or vamsas document read fails.
318    */
319   protected VamsasArchive getVamsasDocument() throws IOException {
320     // check we haven't already done this once - probably should be done by caller
321     if (_va!=null)
322       return _va;
323     // patiently wait for a lock on the document. (from ArchiveClient.getUpdateable())
324     long tries=5000;
325     while (vamArchive.getLock()==null && --tries>0) {
326 //       Thread.sleep(1);
327         log.debug("Trying to get a document lock for the "+tries+"'th time.");
328       }
329     if (tries==0) 
330       throw new IOException("Failed to get lock for vamsas archive.");
331       
332     VamsasArchive va = new VamsasArchive(vamArchive.sessionFile, false, true, vamArchive);
333
334     return va;
335   }
336   /**
337    * Unlocks the vamsas archive session document after it has been closed.
338    * @throws IOException
339    */
340   protected void unlockVamsasDocument() throws IOException {
341     if (_va!=null)
342       _va.closeArchive();
343     _va=null;
344     if (vamArchive!=null)
345       vamArchive.unLock();
346     
347   }
348   /**
349    * create a uniquely named uk.ac.vamsas.client.simpleclient.ClientsFile.addClient(ClientHandle)ile in the session Directory
350    * @see java.io.File.createTempFile
351    * @param pref Prefix for name
352    * @param suff Suffix for name
353    * @return SessionFile object configured for the new file (of length zero)
354    * @throws IOException
355    */
356   protected SessionFile getTempSessionFile(String pref, String suff) throws IOException {
357     File tfile = File.createTempFile(pref,suff,sessionDir);
358     SessionFile tempFile = new SessionFile(tfile);
359     return tempFile;
360   }
361   
362   /**
363    * add a IClient to the session
364    * 
365    * add the client to the client list file
366    * @param client client to add to the session
367    */
368   protected void addClient(SimpleClient client)
369   {
370     if (client == null)
371       slog.error("Try to add a null client to the session ");
372     else {
373       log.debug("Adding client "+client.getClientHandle().getClientUrn());
374       getClientWatcherElement().haltWatch();
375       clist.addClient(client.getClientHandle());
376      
377       log.debug("Added.");
378       log.debug("Register Client as Active.");
379       try {
380         client.createActiveClientFile();
381       } catch (IOException e) {
382         log.debug("Error during  active client file creation.");
383       }
384       //tracks modification to the client list and readds client to the list
385       getClientWatcherElement().setHandler(new AddClientWatchCallBack(client));
386       getClientWatcherElement().enableWatch();
387      
388     }
389   }
390   
391   /**
392    * Handler for the client watcher.
393    * 
394    * If (the current client is not in the client list, it is added again;)
395    */  
396   private class AddClientWatchCallBack  implements WatcherCallBack
397   {
398    
399     private SimpleClient client ;
400     
401     /**
402     *Inits the handler with the client to check in the list
403      * @param client client to monitor in the client list
404      */
405     protected  AddClientWatchCallBack (SimpleClient client)
406       {
407         this.client = client;
408       }
409     
410       /**
411        * If the client list is modified, checks if the current is still in the list. otherwise, readds ti.
412        * @return true to enable watcher, or false to disable it in future WatcherThread cycles.
413        */
414       public boolean handleWatchEvent(WatcherElement watcher, Lock lock)
415         {
416           boolean isWatchEnable = watcher.isWatchEnabled();
417           if (lock== null)//no update on the list
418             return isWatchEnable;
419           log.debug("change on the client list ");
420           if (client != null)
421             {
422         
423             
424             //checks if the client is not already in the lists
425               ClientHandle[] cl = clist.retrieveClientList(lock);//clist.retrieveClientList();
426               boolean found = false;
427               if (cl != null)
428                 {
429                   for (int chi = cl.length-1; !found && chi > -1; chi--) {
430                     found =  cl[chi].equals(this.client.getClientHandle());
431                   }
432                  
433                 } 
434               if (! found) 
435                 {log.debug("client not in the list ");
436                   if( log.isDebugEnabled())
437                       log.debug("the client has not been found in the list. Adding it again :"+cl);
438                     addClient(client);
439               }
440               else
441                 log.debug("client is in the list");
442
443             }
444           log.debug("isWatchEnable "+isWatchEnable);
445           return isWatchEnable;
446         }
447       }
448   
449 /**
450  *  
451  * removes a client from the current session
452  *  removes the client from the session client list
453  *  if the client is the last one from the session  (ClientList), the current session is removed 
454  *  from active session list.
455  *  
456  *  The active should add them self to the client list. To insure to close the session,when the current client is the lact active client,
457  *  clears  the list of clients and when two cycles to insure there is no more active client, that otherwise would have readd themself to the list
458  *  
459  * @param client client to remove
460  */
461   protected void removeClient(SimpleClient client)//IClient client)
462   {
463     if (client == null)
464       {
465         log.error("Null client passed to removeClient");
466         return;
467       }
468     ClientSessionFileWatcherElement cwe=getClientWatcherElement();
469     if (cwe!=null && cwe.isWatchEnabled()) {
470       cwe.haltWatch();
471     };
472     //set handler to check is the the last active client of the session
473     //Wait for several watchers cycle to see if the current client was the last client active in the session.
474     //if yes, close the session
475     
476    // getClientWatcherElement().setHandler(new RemoveClientWatchCallBack (client));
477     getClientWatcherElement().setTimeoutBeforeLastCycle(this.watchCycleCountBeforeLastClient);
478     log.info("remove client from list");
479     clist.clearList();
480     log.info("list cleared");
481     if (cwe!=null) {
482       cwe.enableWatch();
483       
484       
485       log.debug("Stopping EventGenerator..");
486       client.evgen.stopWatching();
487       cwe.setHandler(null);
488       
489 //    ask to the client to copy application data into the document
490       client.evgen._raise(Events.DOCUMENT_FINALIZEAPPDATA, null, client,null);
491       
492       if ( this.isLastActiveClient(client))
493         {
494         log.debug("Raising request-to-save event");
495         client.evgen._raise(Events.DOCUMENT_REQUESTTOCLOSE, null, client, null);
496         //client.evgen._raise(Events.SESSION_SHUTDOWN, null, client.getSessionHandle(), null);
497         log.debug("Last active client: closing session");
498         log.info("Closing session");
499           getSessionManager().removeSession(client.getSessionHandle());
500         }
501       
502       try 
503         {
504           log.debug("Releasing  active client file");
505           client.releaseActiveClientFile();
506         }
507       catch (IOException e)
508         {
509           log.error("error during active file client release");
510          }
511     }
512    
513   
514    
515     /*clist.removeClient(client.getClientHandle(),null);
516     if (this.clist.retrieveClientList() == null|| this.clist.retrieveClientList().length<1)
517       {//assume it is the last client has been removed shutting down session
518         slog.info("last client removed: removing session");
519         log.debug("last client removed: removing session");
520         this.getSessionManager().removeSession(client.getSessionHandle());
521       }
522     else
523       {
524         int active=clist.retrieveClientList().length;
525         log.debug("Still "+active+" active clients");
526         slog.info("Still "+active+" active clients");
527       }*/
528    
529   }
530   
531   
532   private boolean isLastActiveClient(SimpleClient client)
533     {
534     log.debug("Testing if current client is the last one.");
535       boolean noOtherActiveClient = true;
536       //create, if need,  subdirectory to contain client files
537       File clientlockFileDir = new File (this.sessionDir, clientFileDirectory);
538       if( !clientlockFileDir.exists())
539         {
540           log.error("Something wrong the active client file does not exits... should not happen");
541           return false;
542         }
543       
544       try {
545     
546         //no check every file in the directory and try to get lock on it.
547         File [] clientFiles = clientlockFileDir.listFiles();
548         if(clientFiles == null || clientFiles.length==0)
549           {//there is not file on the directory. the current client should be the last one.
550             return true;
551           }
552        
553         for (int i = clientFiles.length - 1; i>-1&& noOtherActiveClient  ;i--)
554           {
555              File clientFile = clientFiles[i];
556              log.debug("testing file for lock: "+clientFile.getAbsolutePath());
557              if(client.getClientlockFile().equals(clientFile)) 
558                {
559                log.debug("current client file found");
560                  continue;
561                }
562              if (clientFile != null &&  clientFile.exists() )
563              {
564                try
565                  {
566                  log.debug("Try to acquire a lock on the file");
567                  // Get a file channel for the file
568                    FileChannel channel = new RandomAccessFile(clientFile, "rw").getChannel();
569          
570                  // Use the file channel to create a lock on the file.
571                  // This method blocks until it can retrieve the lock.
572                  //  java.nio.channels.FileLock activeClientFilelock = channel.lock();
573          
574                  // Try acquiring the lock without blocking. This method returns
575                  // null or throws an exception if the file is already locked.
576                    try
577                      {
578                      java.nio.channels.FileLock  activeClientFilelock = channel.tryLock();
579                      
580                        //the lock has been acquired. 
581                        //the file was not lock and so the corresponding application seems to have die
582                      if(activeClientFilelock != null)
583                        {
584                          log.debug("lock obtained : file must be from a crashed application");
585                          
586                      
587                          activeClientFilelock.release();
588                          log.debug("lock released");
589                          
590                          channel.close();
591                          log.debug("channel closed");
592                          
593                        //delete file 
594                          clientFile.delete();
595                          log.debug("crashed application file deleted");
596                          
597                        }
598                      else
599                        {
600                        noOtherActiveClient  = false;
601                          log.debug("lock not obtained : another application is active");
602                        }
603                      }
604                    catch (OverlappingFileLockException e) 
605                      {
606                      // File is already locked in this thread or virtual machine
607                      //that the expected behaviour
608                      log.debug("lock not accessible ",e);
609                      }
610                  } 
611                catch (Exception e) 
612                  {
613                  log.debug("error during lock testing ",e);
614                  }
615              }
616           }
617       
618       } catch (Exception e) {
619        log.error("error during counting active clients");
620       }
621       return noOtherActiveClient;
622     }
623   /**
624    * Handler for the client watcher. after a client have been removed
625    * 
626    * Checks if the client is not the last active one.
627    * 
628    * If (the current client is not in the client list readd it;)
629    */  
630   private class RemoveClientWatchCallBack  implements WatcherCallBack
631   {
632    
633     private SimpleClient client ;
634     private boolean manualCheckOfClientCount = false;
635     /**
636     *Inits the handler with the client to check in the list
637      * @param client client to monitor in the client list
638      */
639     protected  RemoveClientWatchCallBack (SimpleClient client)
640       {
641         this.client = client;
642       }
643     
644       /**
645        * If the client list is modified, checks if the current is still in the list. otherwise, readds ti.
646        * @return true to enable watcher, or false to disable it in future WatcherThread cycles.
647        */
648       public boolean handleWatchEvent(WatcherElement watcher, Lock lock)
649         { 
650         // if lock is null, no client has been added since last, clear.
651         //the client is then the last client
652           if (client != null)
653             {
654            
655               if (lock == null )
656               {
657             
658             //checks if the client is not already in the lists
659            //   ClientHandle[] cl = clist.retrieveClientList();//lock);//clist.retrieveClientList();
660
661               boolean islastClient =  true;
662               if (manualCheckOfClientCount)
663                 {
664                 log.debug("manual checking of count of client");
665   //checks if the client is not already in the lists
666                   ClientHandle[] cl = clist.retrieveClientList();//lock);//clist.retrieveClientList();
667                   if(cl == null || cl.length<1 )
668        //  {//no client has registered as active 
669                   {
670                     islastClient = true;
671                     log.debug("list is empty");
672                   }
673                   else
674                     islastClient = false;
675                   log.debug("list is not empty");
676                 }
677             //  if(cl == null || cl.length<1 )
678               //  {//no client has registered as active
679               if (islastClient)
680               {
681                 //the client is the last one, so close current session
682                   log.info("last client removed: closing session");
683                   closeSession(client);
684               }
685             }
686               else
687               {
688                 log.debug("not the last client found ");
689 //              ask to the client to cpoy application data into the document
690        //         client.evgen._raise(Events.DOCUMENT_FINALIZEAPPDATA, null, client,null);
691
692            //   /  }
693          
694             }
695               log.debug("Stopping EventGenerator..");
696           client.evgen.stopWatching();
697         }
698           watcher.setHandler(null);//Do not check if the client is the last client. watcher will shutdown anyway
699         //  watcher.haltWatch();
700          // watcher.
701           return false;
702         }
703       }
704
705   /**
706    * closes the current session, 
707    * and send an event to the last client to close the document
708    * @param client the last client of the client
709    */
710   private void closeSession(SimpleClient client)
711     {
712 //   close document 
713       client.evgen._raise(Events.DOCUMENT_REQUESTTOCLOSE, null, client,null);
714       log.debug("close document request done");
715       this.closeSession(client.getSessionHandle());
716     }
717   
718   /**
719    * CLoses the current session
720    * @param sessionHandle sessionHandle of the session to remove
721    */
722   private void closeSession(SessionHandle sessionHandle)
723     {
724       getSessionManager().removeSession(sessionHandle);
725       log.debug("Session removed");
726     }
727 /**
728  * @return the sessionManager
729  */
730 protected SimpleSessionManager getSessionManager() {
731   return sessionManager;
732 }
733 /**
734  * @param sessionManager the sessionManager to set
735  */
736 protected void setSessionManager(SimpleSessionManager sessionManager) {
737   this.sessionManager = sessionManager;
738 }
739 public ClientsFile getStoreDocFile() {
740   if (storedocfile==null) {
741     
742   }
743   return storedocfile;
744 }
745
746 ClientSessionFileWatcherElement clistWatchElement=null;
747 public ClientSessionFileWatcherElement getClientWatcherElement() {
748   if (clistWatchElement==null) {
749     clistWatchElement=new ClientSessionFileWatcherElement(clist,null);
750   }
751   return clistWatchElement;
752 }
753 /**
754  * writes a vector of vorba Ids to the session.
755  * @param modObjects 
756 public void setModObjectList(Vector modObjects) {
757   log.debug("Writing "+modObjects.size()+" ids to ModObjectList");
758   // TODO Auto-generated method stub
759 }
760 **
761  * get current list of modified objects.
762  * @return null or Vector of objects
763  *
764 public Vector getModObjectList() {
765   log.debug("Reading modObjectList");
766   return null;
767 }
768 */
769 }
770
771