1 package org.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;
16 import org.vamsas.client.ClientHandle;
17 import org.vamsas.client.UserHandle;
19 * Does all the IO operations for a SimpleClient instance accessing
20 * a SimpleClient vamsas session.
22 * Basically, it defines the various standard names for the files
23 * in the session directory (that maps to the sessionUrn),
24 * provides constructors for the file handlers and watchers of
25 * those file entities, and some higher level methods
26 * to check and change the state flags for the session.
28 * TODO: move the stuff below to the SimpleClientFactory documentation.
29 * much may not be valid now :
30 * Vamsas client is intialised with a path to create live session directories.
31 * This path may contain a vamsas.properties file
32 * that sets additional parameters (otherwise client
33 * just uses the one on the classpath).
35 * A vamsas session consists of :
36 * SessionDir - translates to urn of a live session.
37 * Contains: Vamsas Document (as a jar), Session client list file,
38 * both of which may be locked, and additional
39 * temporary versions of these files when write
40 * operations are taking place.
43 * - vamsasdocument.xml : core info
45 * - <applicationname>.version.sessionnumber.raw (string given in vamsasdocument.xml applicationData entry)
48 * - filename given in the vamsasdocument.xml. Should be checked for validity by any client and rewritten if necessary.
49 * The lockfile can point to the jar itself.
51 * Initially - documentHandler either:
52 * - creates a zip for a new session for the client
53 * - connect to an existing session zip
54 * 1. reads session urn file
56 * 3. examines session - decide whether to create new application data slice or connect to one stored in session.
57 * 4. writes info into session file
58 * 5. releases lock and generates local client events.
59 * 6. Creates Watcher thread to generate events.
62 * - Update watcher checks for file change -
64 * Procedures for file based session message exchange
65 * - session document modification flag
69 public class VamsasSession {
71 * indicator file for informing other processes that
72 * they should finalise their vamsas datasets for
73 * storing into a vamsas archive.
75 public static final String CLOSEANDSAVE_FILE="stored.log";
77 * session file storing the last_stored_stat data
79 public static final String MODIFIEDDOC_FILE="modified";
82 * called to clear update flag after a successful offline storage event
84 protected void clearUnsavedFlag() {
85 SessionFlagFile laststored = new SessionFlagFile(new File(sessionDir, MODIFIEDDOC_FILE));
86 if (!laststored.clearFlag())
87 log.warn("Unsaved flag was not cleared for "+sessionDir);
90 * called to indicate session document has been modified.
93 protected void setUnsavedFlag() {
94 SessionFlagFile laststored = new SessionFlagFile(new File(sessionDir, MODIFIEDDOC_FILE));
95 if (!laststored.setFlag())
96 log.warn("Couldn't set the Unsaved flag for "+sessionDir);
100 * @return true if session document has been modified since last offline storage event
102 protected boolean getUnsavedFlag() {
103 SessionFlagFile laststored = new SessionFlagFile(new File(sessionDir, MODIFIEDDOC_FILE));
104 return laststored.checkFlag();
109 public static final String SESSION_LOG="Log.txt";
110 private static Log log = LogFactory.getLog(VamsasSession.class);
111 protected Logger slog = Logger.getLogger("org.vamsas.client.SessionLog");
113 * setup the sessionLog using Log4j.
114 * @throws IOException
116 private void initLog() throws IOException {
117 // LATER: make dedicated appender format for session log.
118 Appender app = slog.getAppender("SESSION_LOG");
119 slog.addAppender(new FileAppender(app.getLayout(), new File(sessionDir, SESSION_LOG).getAbsolutePath()));
123 * the sessionDir is given as the session location for new clients.
125 protected File sessionDir;
127 * holds the list of attached clients
130 public static final String CLIENT_LIST="Clients.obj";
134 VamsasFile vamArchive;
135 public static final String VAMSAS_OBJ="VamDoc.jar";
138 * sets up the vamsas session files and watchers in sessionDir
141 protected VamsasSession(File sessionDir) throws IOException {
142 if (sessionDir==null)
143 throw new Error("Null directory for VamsasSession.");
144 if (sessionDir.exists()) {
145 if (!sessionDir.isDirectory() || !sessionDir.canWrite() || !sessionDir.canRead())
146 throw new IOException("Cannot access '"+sessionDir+"' as a read/writable Directory.");
147 if (checkSessionFiles(sessionDir)) {
148 // session files exist in the directory
149 this.sessionDir = sessionDir;
150 initSessionObjects();
151 slog.debug("Initialising additional VamsasSession instance");
152 log.debug("Attached to VamsasSession in "+sessionDir);
155 // start from scratch
156 if (!sessionDir.mkdir())
157 throw new IOException("Failed to make VamsasSession directory in "+sessionDir);
158 this.sessionDir = sessionDir;
159 createSessionFiles();
160 initSessionObjects();
161 slog.debug("Session directory created.");
162 log.debug("Initialised VamsasSession in "+sessionDir);
166 * tests presence of existing sessionfiles files in dir
170 private boolean checkSessionFiles(File dir) throws IOException {
171 File c_file = new File(dir,CLIENT_LIST);
172 File v_doc = new File(dir,VAMSAS_OBJ);
173 if (c_file.exists() && v_doc.exists())
178 * create new empty files in dir
181 private void createSessionFiles() throws IOException {
182 if (sessionDir==null)
183 throw new IOException("Invalid call to createSessionFiles() with null sessionDir");
184 File c_file = new File(sessionDir,CLIENT_LIST);
185 File v_doc = new File(sessionDir,VAMSAS_OBJ);
186 if (c_file.createNewFile())
187 log.debug("Created new ClientFile "+c_file); // don't care if this works or not
188 if (v_doc.createNewFile())
189 log.debug("Created new Vamsas Session Document File "+v_doc);
192 * construct SessionFile objects and watchers for each
194 private void initSessionObjects() throws IOException {
195 if (clist!=null || vamArchive!=null)
196 throw new IOException("initSessionObjects called for initialised VamsasSession object.");
197 clist = new ClientsFile(new File(sessionDir,CLIENT_LIST));
198 vamArchive = new VamsasFile(new File(sessionDir,VAMSAS_OBJ));
202 * make a new watcher object for the clientFile
203 * @return new ClientFile watcher instance
205 public FileWatcher getClientWatcher() {
206 return new FileWatcher(clist.sessionFile);
208 FileWatcher session_doc_watcher=null;
210 * make a new watcher object for the vamsas Document
211 * @return new ClientFile watcher instance
213 public FileWatcher getDocWatcher() {
214 if (session_doc_watcher==null)
215 return session_doc_watcher = new FileWatcher(vamArchive.sessionFile);
216 return new FileWatcher(vamArchive.sessionFile);
218 FileWatcher store_doc_file=null;
220 * make a new watcher object for the messages file
221 * (thread safe - keeps a reference to the first watcher)
222 * @return new watcher instance
224 public FileWatcher getStoreWatcher() {
225 if (store_doc_file==null)
226 return store_doc_file = new FileWatcher(new File(CLOSEANDSAVE_FILE));
227 return new FileWatcher(new File(CLOSEANDSAVE_FILE));
231 * write to the StoreWatcher file to indicate that a storeDocumentRequest has been made.
232 * The local client's storeWatcher FileWatcher object is updated so the initial change is not registered.
237 public void addStoreDocumentRequest(ClientHandle client, UserHandle user) throws IOException {
238 SessionFile sfw = new SessionFile(new File(sessionDir, CLOSEANDSAVE_FILE));
239 while (!sfw.lockFile())
240 log.debug("Trying to get lock for "+CLOSEANDSAVE_FILE);
241 RandomAccessFile sfwfile=sfw.fileLock.getRaFile();
242 sfwfile.setLength(0); // wipe out any old info.
243 // TODO: rationalise what gets written to this file (ie do we want other clients to read the id of the requestor?)
244 sfwfile.writeUTF(client.getClientUrn()+":"+user.getFullName()+"@"+user.getOrganization());
246 if (store_doc_file!=null)
247 store_doc_file.setState();
248 slog.info("FinalizeAppData request from "+user.getFullName()+" using "+client.getClientUrn()+"");
251 * create a new session with an existing vamsas Document - by copying it into the session.
254 public void setVamsasDocument(File archive) throws IOException {
255 log.debug("Transferring vamsas data from "+archive+" to session:"+vamArchive.sessionFile);
256 SessionFile xtantdoc = new SessionFile(archive);
257 vamArchive.updateFrom(null, xtantdoc);
258 // LATER: decide if session archive provenance should be updated to reflect access.
259 // TODO: soon! do a proper import objects from external file
260 log.debug("Transfer complete.");
263 * write session as a new vamsas Document (this will overwrite any existing file without warning)
265 * TODO: verify that lock should be released for vamsas document.
268 protected void writeVamsasDocument(File destarchive, Lock extlock) throws IOException {
269 log.debug("Transferring vamsas data from "+vamArchive.sessionFile+" to session:"+destarchive);
270 SessionFile newdoc = new SessionFile(destarchive);
271 if (extlock==null && !vamArchive.lockFile())
272 while (!vamArchive.lockFile())
273 log.info("Trying to get lock for "+vamArchive.sessionFile);
274 // TODO: LATER: decide if session archive provenance should be written in vamsasDocument file for this export.
275 newdoc.updateFrom(extlock, vamArchive);
276 // 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).
279 log.debug("Transfer complete.");
283 * Creates a VamsasArchive Vobject for accessing and updating document
284 * Note: this will lock the Vamsas Document for exclusive access to the client.
285 * @return session vamsas document
286 * @throws IOException if locks fail or vamsas document read fails.
288 protected VamsasArchive getVamsasDocument() throws IOException {
289 // TODO: check we haven't already done this once
290 if (!vamArchive.lockFile())
291 throw new IOException("Failed to get lock for vamsas archive.");
293 VamsasArchive va = new VamsasArchive(vamArchive.sessionFile, false, true, vamArchive);
298 * create a uniquely named file in the session Directory
299 * @see java.io.File.createTempFile
300 * @param pref Prefix for name
301 * @param suff Suffix for name
302 * @return SessionFile object configured for the new file (of length zero)
303 * @throws IOException
305 protected SessionFile getTempSessionFile(String pref, String suff) throws IOException {
306 File tfile = File.createTempFile(pref,suff,sessionDir);
307 SessionFile tempFile = new SessionFile(tfile);