added simple semaphore file mechanism for session directory.
[vamsas.git] / src / org / vamsas / client / simpleclient / VamsasSession.java
1 package org.vamsas.client.simpleclient;
2
3 import java.io.BufferedWriter;
4 import java.io.File;
5 import java.io.IOException;
6 import java.io.PrintStream;
7 import java.io.PrintWriter;
8 import java.io.Writer;
9
10 import org.apache.commons.logging.Log;
11 import org.apache.commons.logging.LogFactory;
12 import org.apache.log4j.Appender;
13 import org.apache.log4j.Logger;
14 import org.apache.log4j.FileAppender;
15 import org.vamsas.client.ClientHandle;
16 import org.vamsas.client.UserHandle;
17 /**
18  * Holds the file handlers and watchers for a session.
19  * 
20  * TODO: delete the stuff below - prolly irrelevant now
21  * Vamsas client is intialised with a path to create live session directories. 
22  * This path may contain a vamsas.properties file 
23  * that sets additional parameters (otherwise client 
24  * just uses the one on the classpath).
25  * 
26  * A vamsas session consists of :
27  *  SessionDir - translates to urn of a live session.
28  *  Contains: Vamsas Document (as a jar), Session client list file, 
29  *  both of which may be locked, and additional 
30  *  temporary versions of these files when write 
31  *  operations are taking place.
32  * 
33  * Zip file entries
34  *  - vamsasdocument.xml : core info
35  *  one or more:
36  *  - <applicationname>.version.sessionnumber.raw (string given in vamsasdocument.xml applicationData entry)
37  *  
38  * Lockfile
39  *  - filename given in the vamsasdocument.xml. Should be checked for validity by any client and rewritten if necessary. 
40  *    The lockfile can point to the jar itself.
41  * Mode of operation.
42  * Initially - documentHandler either:
43  *  - creates a zip for a new session for the client
44  *  - connect to an existing session zip 
45  *   1. reads session urn file
46  *   2. waits for lock
47  *   3. examines session - decide whether to create new application data slice or connect to one stored in session.
48  *   4. writes info into session file
49  *   5. releases lock and generates local client events.
50  *   6. Creates Watcher thread to generate events.
51  * 
52  * During the session
53  *  - Update watcher checks for file change - 
54  * 
55  */
56
57 public class VamsasSession {
58   /**
59    * indicator file for informing other processes that 
60    * they should finalise their vamsas datasets for 
61    * storing into a vamsas archive.
62    */
63   public static final String CLOSEANDSAVE_FILE="stored.log";
64   /**
65    * state of vamsas document when it was last stored outside the session directory
66    */
67   protected long last_stored_stat[]=null;
68   protected void updateLastStoredStat() {
69     
70   }
71   /**
72    * log file location
73    */
74   public static final String SESSION_LOG="Log.txt";
75   private static Log log = LogFactory.getLog(VamsasSession.class);
76   protected Logger slog = Logger.getLogger("org.vamsas.client.SessionLog");
77   /**
78    * setup the sessionLog using Log4j.
79    * @throws IOException
80    */
81   private void initLog() throws IOException {
82     // TODO: LATER: make dedicated appender format for session log.
83     Appender app = slog.getAppender("SESSION_LOG");
84     slog.addAppender(new FileAppender(app.getLayout(), new File(sessionDir, SESSION_LOG).getAbsolutePath()));
85   }
86   
87   /**
88    * the sessionDir is given as the session location for new clients.
89    */
90   File sessionDir;
91   /**
92    * holds the list of attached clients
93    */
94   ClientsFile clist;
95   public static final String CLIENT_LIST="Clients.obj";
96   /**
97    * holds the data
98    */
99   VamsasFile vamArchive; 
100   public static final String VAMSAS_OBJ="VamDoc.jar";
101   
102   /**
103    * sets up the vamsas session files and watchers in sessionDir
104    * @param sessionDir
105    */
106   protected VamsasSession(File sessionDir) throws IOException {
107     if (sessionDir==null)
108       throw new Error("Null directory for VamsasSession.");
109     if (sessionDir.exists()) {
110       if (!sessionDir.isDirectory() || !sessionDir.canWrite() || sessionDir.canRead())
111         throw new IOException("Cannot access '"+sessionDir+"' as a read/writable Directory.");
112       if (checkSessionFiles(sessionDir)) {
113         // session files exist in the directory
114         this.sessionDir = sessionDir;
115         initSessionObjects();
116         slog.debug("Initialising additional VamsasSession instance");
117         log.debug("Attached to VamsasSession in "+sessionDir);
118       } 
119     } else {
120       // start from scratch
121       if (!sessionDir.mkdir())
122         throw new IOException("Failed to make VamsasSession directory in "+sessionDir);
123       this.sessionDir = sessionDir; 
124       createSessionFiles();
125       initSessionObjects();
126       slog.debug("Session directory created.");
127       log.debug("Initialised VamsasSession in "+sessionDir);
128     }
129   }
130   /**
131    * tests presence of existing sessionfiles files in dir
132    * @param dir
133    * @return
134    */
135   private boolean checkSessionFiles(File dir) throws IOException {
136     File c_file = new File(dir,CLIENT_LIST);
137     File v_doc = new File(dir,VAMSAS_OBJ);
138     if (c_file.exists() && v_doc.exists())
139       return true;
140     return false;
141   }
142   /**
143    * create new empty files in dir
144    *
145    */
146   private void createSessionFiles() throws IOException {
147     if (sessionDir==null)
148       throw new IOException("Invalid call to createSessionFiles() with null sessionDir");
149     File c_file = new File(sessionDir,CLIENT_LIST);
150     File v_doc = new File(sessionDir,VAMSAS_OBJ);
151     if (c_file.createNewFile())
152       log.debug("Created new ClientFile "+c_file); // don't care if this works or not
153     if (v_doc.createNewFile())
154       log.debug("Created new Vamsas Session Document File "+v_doc); 
155   }
156   /**
157    * construct SessionFile objects and watchers for each
158    */
159   private void initSessionObjects() throws IOException {
160     if (clist!=null || vamArchive!=null)
161       throw new IOException("initSessionObjects called for initialised VamsasSession object.");
162     clist = new ClientsFile(new File(sessionDir,CLIENT_LIST));
163     vamArchive = new VamsasFile(new File(sessionDir,VAMSAS_OBJ));
164     initLog();
165   }
166   /**
167    * make a new watcher object for the clientFile
168    * @return new ClientFile watcher instance
169    */
170   public FileWatcher getClientWatcher() {
171     return new FileWatcher(clist.sessionFile);
172   }
173   /**
174    * make a new watcher object for the vamsas Document
175    * @return new ClientFile watcher instance
176    */
177   public FileWatcher getDocWatcher() {
178     return new FileWatcher(vamArchive.sessionFile);
179   }
180   FileWatcher store_doc_file=null;
181   /**
182    * make a new watcher object for the messages file
183    * @return new watcher instance
184    */
185   public FileWatcher getStoreWatcher() {
186     return store_doc_file = new FileWatcher(new File(CLOSEANDSAVE_FILE));
187   }
188   /**
189    * write to the StoreWatcher file to indicate that a storeDocumentRequest has been made.
190    * The local client's storeWatcher FileWatcher object is updated so the initial change is not registered.
191    * @param client
192    * @param user
193    * @return
194    */
195   public void addStoreDocumentRequest(ClientHandle client, UserHandle user) throws IOException {
196     SessionFile sfw = new SessionFile(new File(sessionDir, CLOSEANDSAVE_FILE));
197     while (!sfw.lockFile())
198       log.debug("Trying to get lock for "+CLOSEANDSAVE_FILE);
199     sfw.fileLock.rafile.setLength(0); // wipe out any old info.
200     // TODO: rationalise what gets written to this file (ie do we want other clients to read the id of the requestor?)
201     sfw.fileLock.rafile.writeUTF(client.getClientUrn()+":"+user.getFullName()+"@"+user.getOrganization());
202     sfw.unlockFile();
203     if (store_doc_file!=null)
204       store_doc_file.setState();
205     slog.info("FinalizeAppData request from "+user.getFullName()+" using "+client.getClientUrn()+"");
206   }
207   /**
208    * create a new session with an existing vamsas Document - by copying it into the session.
209    * @param archive
210    */
211   protected void setVamsasDocument(File archive) throws IOException {
212     log.debug("Transferring vamsas data from "+archive+" to session:"+vamArchive.sessionFile);
213     SessionFile xtantdoc = new SessionFile(archive);
214     vamArchive.updateFrom(null, xtantdoc);
215     // LATER: decide if session archive provenance should be updated to reflect access.
216     // TODO: soon! do a proper import objects from external file 
217     log.debug("Transfer complete.");
218   }
219   /**
220    * write session as a new vamsas Document (this will overwrite any existing file without warning)
221    * TODO: test
222    * TODO: verify that lock should be released for vamsas document.
223    * @param destarchive
224    */
225   protected void writeVamsasDocument(File destarchive, Lock extlock) throws IOException {
226     log.debug("Transferring vamsas data from "+vamArchive.sessionFile+" to session:"+destarchive);
227     SessionFile newdoc = new SessionFile(destarchive);
228     if (extlock==null && !vamArchive.lockFile())
229       while (!vamArchive.lockFile())
230         log.info("Trying to get lock for "+vamArchive.sessionFile);
231     // TODO: LATER: decide if session archive provenance should be written in vamsasDocument file for this export.
232     newdoc.updateFrom(extlock, vamArchive);
233     // TODO: SOON: fix use of updateFrom for file systems where locks cannot be made (because they don't have a lockManager, ie NFS/Unix, etc).
234     vamArchive.unLock();
235     newdoc.unlockFile();
236     log.debug("Transfer complete.");
237   }
238   
239   /**
240    * Creates a VamsasArchive object for accessing and updating document
241    * Note: this will lock the Vamsas Document for exclusive access to the client.
242    * @return session vamsas document
243    * @throws IOException if locks fail or vamsas document read fails.
244    */
245   protected VamsasArchive getVamsasDocument() throws IOException {
246     // TODO: check we haven't already done this once
247     if (!vamArchive.lockFile()) 
248       throw new IOException("Failed to get lock for vamsas archive.");
249     
250     VamsasArchive va = new VamsasArchive(vamArchive.sessionFile, false, true, vamArchive);
251     
252     return va;
253   }
254   
255 }
256
257