1 package uk.ac.vamsas.client.simpleclient;
3 import java.io.BufferedWriter;
5 import java.io.IOException;
6 import java.io.PrintStream;
7 import java.io.PrintWriter;
8 import java.io.RandomAccessFile;
11 import org.apache.commons.logging.Log;
12 import org.apache.commons.logging.LogFactory;
13 import org.apache.log4j.Appender;
14 import org.apache.log4j.Logger;
15 import org.apache.log4j.FileAppender;
17 import uk.ac.vamsas.client.ClientHandle;
18 import uk.ac.vamsas.client.UserHandle;
20 * Does all the IO operations for a SimpleClient instance accessing
21 * a SimpleClient vamsas session.
23 * Basically, it defines the various standard names for the files
24 * in the session directory (that maps to the sessionUrn),
25 * provides constructors for the file handlers and watchers of
26 * those file entities, and some higher level methods
27 * to check and change the state flags for the session.
29 * TODO: move the stuff below to the SimpleClientFactory documentation.
30 * much may not be valid now :
31 * Vamsas client is intialised with a path to create live session directories.
32 * This path may contain a vamsas.properties file
33 * that sets additional parameters (otherwise client
34 * just uses the one on the classpath).
36 * A vamsas session consists of :
37 * SessionDir - translates to urn of a live session.
38 * Contains: Vamsas Document (as a jar), Session client list file,
39 * both of which may be locked, and additional
40 * temporary versions of these files when write
41 * operations are taking place.
44 * - vamsasdocument.xml : core info
46 * - <applicationname>.version.sessionnumber.raw (string given in vamsasdocument.xml applicationData entry)
49 * - filename given in the vamsasdocument.xml. Should be checked for validity by any client and rewritten if necessary.
50 * The lockfile can point to the jar itself.
52 * Initially - documentHandler either:
53 * - creates a zip for a new session for the client
54 * - connect to an existing session zip
55 * 1. reads session urn file
57 * 3. examines session - decide whether to create new application data slice or connect to one stored in session.
58 * 4. writes info into session file
59 * 5. releases lock and generates local client events.
60 * 6. Creates Watcher thread to generate events.
63 * - Update watcher checks for file change -
65 * Procedures for file based session message exchange
66 * - session document modification flag
70 public class VamsasSession {
72 * indicator file for informing other processes that
73 * they should finalise their vamsas datasets for
74 * storing into a vamsas archive.
76 public static final String CLOSEANDSAVE_FILE="stored.log";
78 * session file storing the last_stored_stat data
80 public static final String MODIFIEDDOC_FILE="modified";
83 * called to clear update flag after a successful offline storage event
85 protected void clearUnsavedFlag() {
86 SessionFlagFile laststored = new SessionFlagFile(new File(sessionDir, MODIFIEDDOC_FILE));
87 if (!laststored.clearFlag())
88 log.warn("Unsaved flag was not cleared for "+sessionDir);
91 * called to indicate session document has been modified.
94 protected void setUnsavedFlag() {
95 SessionFlagFile laststored = new SessionFlagFile(new File(sessionDir, MODIFIEDDOC_FILE));
96 if (!laststored.setFlag())
97 log.warn("Couldn't set the Unsaved flag for "+sessionDir);
101 * @return true if session document has been modified since last offline storage event
103 protected boolean getUnsavedFlag() {
104 SessionFlagFile laststored = new SessionFlagFile(new File(sessionDir, MODIFIEDDOC_FILE));
105 return laststored.checkFlag();
110 public static final String SESSION_LOG="Log.txt";
111 private static Log log = LogFactory.getLog(VamsasSession.class);
112 protected Logger slog = Logger.getLogger("uk.ac.vamsas.client.SessionLog");
114 * setup the sessionLog using Log4j.
115 * @throws IOException
117 private void initLog() throws IOException {
118 // LATER: make dedicated appender format for session log.
119 Appender app = slog.getAppender("SESSION_LOG");
120 // slog.addAppender(new FileAppender(app.getLayout(), new File(sessionDir, SESSION_LOG).getAbsolutePath()));
122 //Appender app = slog.getAppender("SESSION_LOG");
123 if (app == null) log.info("No appender for SESSION_LOG");
125 if (slog!= null && app != null)
127 if (app instanceof FileAppender)
129 File sessionLogFile = new File(this.sessionDir, ((FileAppender)app).getFile());
130 slog.addAppender(new FileAppender(app.getLayout(), sessionLogFile.getAbsolutePath()));
132 // slog.removeAppender("SESSION_LOG");
137 * the sessionDir is given as the session location for new clients.
139 protected File sessionDir;
141 * holds the list of attached clients
144 public static final String CLIENT_LIST="Clients.obj";
148 VamsasFile vamArchive;
149 public static final String VAMSAS_OBJ="VamDoc.jar";
152 * sets up the vamsas session files and watchers in sessionDir
155 protected VamsasSession(File sessionDir) throws IOException {
156 if (sessionDir==null)
157 throw new Error("Null directory for VamsasSession.");
158 if (sessionDir.exists()) {
159 if (!sessionDir.isDirectory() || !sessionDir.canWrite() || !sessionDir.canRead())
160 throw new IOException("Cannot access '"+sessionDir+"' as a read/writable Directory.");
161 if (checkSessionFiles(sessionDir)) {
162 createSessionFiles();
164 // session files exist in the directory
165 this.sessionDir = sessionDir;
166 initSessionObjects();
167 slog.debug("Initialising additional VamsasSession instance");
168 log.debug("Attached to VamsasSession in "+sessionDir);
171 // start from scratch
172 if (!sessionDir.mkdir())
173 throw new IOException("Failed to make VamsasSession directory in "+sessionDir);
174 this.sessionDir = sessionDir;
175 createSessionFiles();
176 initSessionObjects();
177 slog.debug("Session directory created.");
178 log.debug("Initialised VamsasSession in "+sessionDir);
182 * tests presence of existing sessionfiles files in dir
186 private boolean checkSessionFiles(File dir) throws IOException {
187 File c_file = new File(dir,CLIENT_LIST);
188 File v_doc = new File(dir,VAMSAS_OBJ);
189 if (c_file.exists() && v_doc.exists())
194 * create new empty files in dir
197 private void createSessionFiles() throws IOException {
198 if (sessionDir==null)
199 throw new IOException("Invalid call to createSessionFiles() with null sessionDir");
200 File c_file = new File(sessionDir,CLIENT_LIST);
201 File v_doc = new File(sessionDir,VAMSAS_OBJ);
202 if (c_file.createNewFile())
203 log.debug("Created new ClientFile "+c_file); // don't care if this works or not
204 if (v_doc.createNewFile())
205 log.debug("Created new Vamsas Session Document File "+v_doc);
208 * construct SessionFile objects and watchers for each
210 private void initSessionObjects() throws IOException {
211 if (clist!=null || vamArchive!=null)
212 throw new IOException("initSessionObjects called for initialised VamsasSession object.");
213 clist = new ClientsFile(new File(sessionDir,CLIENT_LIST));
214 vamArchive = new VamsasFile(new File(sessionDir,VAMSAS_OBJ));
218 * make a new watcher object for the clientFile
219 * @return new ClientFile watcher instance
221 public FileWatcher getClientWatcher() {
222 return new FileWatcher(clist.sessionFile);
224 FileWatcher session_doc_watcher=null;
226 * make a new watcher object for the vamsas Document
227 * @return new ClientFile watcher instance
229 public FileWatcher getDocWatcher() {
230 if (session_doc_watcher==null)
231 return session_doc_watcher = new FileWatcher(vamArchive.sessionFile);
232 return new FileWatcher(vamArchive.sessionFile);
234 FileWatcher store_doc_file=null;
236 * make a new watcher object for the messages file
237 * (thread safe - keeps a reference to the first watcher)
238 * @return new watcher instance
240 public FileWatcher getStoreWatcher() {
241 if (store_doc_file==null)
242 return store_doc_file = new FileWatcher(new File(CLOSEANDSAVE_FILE));
243 return new FileWatcher(new File(CLOSEANDSAVE_FILE));
247 * write to the StoreWatcher file to indicate that a storeDocumentRequest has been made.
248 * The local client's storeWatcher FileWatcher object is updated so the initial change is not registered.
253 public void addStoreDocumentRequest(ClientHandle client, UserHandle user) throws IOException {
254 SessionFile sfw = new SessionFile(new File(sessionDir, CLOSEANDSAVE_FILE));
255 while (!sfw.lockFile())
256 log.debug("Trying to get lock for "+CLOSEANDSAVE_FILE);
257 RandomAccessFile sfwfile=sfw.fileLock.getRaFile();
258 sfwfile.setLength(0); // wipe out any old info.
259 // TODO: rationalise what gets written to this file (ie do we want other clients to read the id of the requestor?)
260 sfwfile.writeUTF(client.getClientUrn()+":"+user.getFullName()+"@"+user.getOrganization());
262 if (store_doc_file!=null)
263 store_doc_file.setState();
264 slog.info("FinalizeAppData request from "+user.getFullName()+" using "+client.getClientUrn()+"");
267 * create a new session with an existing vamsas Document - by copying it into the session.
270 public void setVamsasDocument(File archive) throws IOException {
271 log.debug("Transferring vamsas data from "+archive+" to session:"+vamArchive.sessionFile);
272 SessionFile xtantdoc = new SessionFile(archive);
273 vamArchive.updateFrom(null, xtantdoc);
274 // LATER: decide if session archive provenance should be updated to reflect access.
275 // TODO: soon! do a proper import objects from external file
276 log.debug("Transfer complete.");
279 * write session as a new vamsas Document (this will overwrite any existing file without warning)
281 * TODO: verify that lock should be released for vamsas document.
284 protected void writeVamsasDocument(File destarchive, Lock extlock) throws IOException {
285 log.debug("Transferring vamsas data from "+vamArchive.sessionFile+" to session:"+destarchive);
286 SessionFile newdoc = new SessionFile(destarchive);
287 if (extlock==null && !vamArchive.lockFile())
288 while (!vamArchive.lockFile())
289 log.info("Trying to get lock for "+vamArchive.sessionFile);
290 // TODO: LATER: decide if session archive provenance should be written in vamsasDocument file for this export.
291 newdoc.updateFrom(extlock, vamArchive);
292 // 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).
295 log.debug("Transfer complete.");
299 * Creates a VamsasArchive Vobject for accessing and updating document
300 * Note: this will lock the Vamsas Document for exclusive access to the client.
301 * @return session vamsas document
302 * @throws IOException if locks fail or vamsas document read fails.
304 protected VamsasArchive getVamsasDocument() throws IOException {
305 // TODO: check we haven't already done this once
306 if (!vamArchive.lockFile())
307 throw new IOException("Failed to get lock for vamsas archive.");
309 VamsasArchive va = new VamsasArchive(vamArchive.sessionFile, false, true, vamArchive);
314 * create a uniquely named file in the session Directory
315 * @see java.io.File.createTempFile
316 * @param pref Prefix for name
317 * @param suff Suffix for name
318 * @return SessionFile object configured for the new file (of length zero)
319 * @throws IOException
321 protected SessionFile getTempSessionFile(String pref, String suff) throws IOException {
322 File tfile = File.createTempFile(pref,suff,sessionDir);
323 SessionFile tempFile = new SessionFile(tfile);