locate simpleclientsession handle for any session URN
[vamsas.git] / src / uk / ac / vamsas / client / simpleclient / SimpleClientFactory.java
1 /*
2  * This file is part of the Vamsas Client version 0.1. 
3  * Copyright 2009 by Jim Procter, Iain Milne, Pierre Marguerite, 
4  *  Andrew Waterhouse and Dominik Lindner.
5  * 
6  * Earlier versions have also been incorporated into Jalview version 2.4 
7  * since 2008, and TOPALi version 2 since 2007.
8  * 
9  * The Vamsas Client is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *  
14  * The Vamsas Client is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU Lesser General Public License for more details.
18  * 
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with the Vamsas Client.  If not, see <http://www.gnu.org/licenses/>.
21  */
22 package uk.ac.vamsas.client.simpleclient;
23
24 import java.io.File;
25
26 import java.io.IOException;
27 import java.net.MalformedURLException;
28
29 import org.apache.commons.logging.Log;
30 import org.apache.commons.logging.LogFactory;
31
32 import uk.ac.vamsas.client.ClientHandle;
33 import uk.ac.vamsas.client.IClient;
34 import uk.ac.vamsas.client.IClientFactory;
35 import uk.ac.vamsas.client.InvalidSessionDocumentException;
36 import uk.ac.vamsas.client.InvalidSessionUrnException;
37 import uk.ac.vamsas.client.NoDefaultSessionException;
38 import uk.ac.vamsas.client.SessionHandle;
39 import uk.ac.vamsas.client.UserHandle;
40
41 /**
42  * 
43  * creates a session arena in the user home directory under .vamsas. Each
44  * session has its own subdirectory.
45  */
46 public class SimpleClientFactory implements IClientFactory {
47
48   private static Log log = LogFactory.getLog(SimpleClientFactory.class);
49
50   private File sessionArena = null;
51
52   private String vamsasSubdirectoryName = ".vamsas";
53
54   private SimpleSessionManager sessionManager = null;
55
56   private static final String SESSION_LIST = "sessions.obj";
57
58   // private String[] currentlyAvailableDessions = null;
59
60   /**
61    * default constructor - called by CreateClientFactory only.
62    * 
63    * Inits the sessionarena to the directory .vamsas of the user home directory.
64    * 
65    */
66   public SimpleClientFactory() throws IOException {
67     // sessionArena
68
69     // retrieves user home directory
70     String userHomeDirectory = System.getProperty("user.home");
71     if (userHomeDirectory == null || userHomeDirectory.length() < 1) {
72       new IOException("Unable to detect user home directory");
73     }
74     String sessionArenaPath = userHomeDirectory.concat(File.separator
75         .concat(this.vamsasSubdirectoryName));
76
77     this.initSessionArena(sessionArenaPath);
78     // this.initFactoryObjects();
79   }
80
81   /**
82    * Create a client factory that works with sessions at the given path.
83    * 
84    * @param path
85    *          path to directory called session arena, where will be created
86    *          session directories and session files.
87    */
88   public SimpleClientFactory(String path) throws IOException {
89     this.initSessionArena(path);
90   }
91
92   /**
93    * Inits sessionArena to a given path. checks if path is valid.
94    * 
95    * @param path
96    *          path to a directory to use
97    * @throws IOException
98    *           if the path is incorrect
99    */
100   private void initSessionArena(String path) throws IOException {
101     // Check path is valid and read/writeable.
102     File arenaFile = new File(path);
103     if (!arenaFile.exists()) {
104       if (!arenaFile.mkdirs()) {
105         this.sessionArena = null;
106         throw (new IOException("Unable to create a directory called " + path));
107       }
108     }
109     if (arenaFile.exists() && arenaFile.isDirectory() && arenaFile.canRead()
110         && arenaFile.canWrite()) {
111       this.sessionArena = arenaFile;
112     } else {
113       this.sessionArena = null;
114       throw (new IOException("Cannot read and write to a directory called "
115           + path));
116     }
117   }
118
119   /**
120    * @see uk.ac.vamsas.client.IClientFactory#getIClient(uk.ac.vamsas.client.ClientHandle)
121    * 
122    *      Creates a IClient object, using default UserHandle with system
123    *      variables:"user.name" or "USERNAME")), "host.name" or "HOSTNAME"
124    */
125   public IClient getIClient(ClientHandle applicationHandle)
126       throws NoDefaultSessionException {
127     // create a new session
128     // register new ClientHandle in session
129     // create SimpleClient instance
130     return this.getIClient(applicationHandle, (UserHandle) null);
131   }
132
133   /**
134    * the URN should be something like simpleclient:FILEPATH URL encoded
135    * 
136    * @see uk.ac.vamsas.client.IClientFactory#getIClient(uk.ac.vamsas.client.ClientHandle,
137    *      java.lang.String)
138    */
139   public IClient getIClient(ClientHandle applicationHandle, String sessionUrn) {
140     // locate session from Urn
141     // check that clientHandle is unique (with default user) - if not update the
142     // clientHandle urn to make it unique.
143     // wait for lock and attach to session
144     // create SimpleClient instance
145     log.debug("Trying to create session with URN " + sessionUrn);
146     return this.getIClient(applicationHandle, null, sessionUrn);
147
148   }
149
150   /**
151    * try to locate the sessionUrn within the simpleclient session arena
152    * @param sessionUrn
153    * @return
154    * @throws InvalidSessionUrnException
155    */
156   private File convertSessionUrnToFile(String sessionUrn)
157       throws InvalidSessionUrnException {
158     if (sessionUrn == null) {
159       log.debug("Incorrect URN: can not open session.");
160       throw new InvalidSessionUrnException("SessionUrn was null");
161     }
162
163     SessionUrn urn = new SessionUrn(sessionUrn);
164     SimpleSessionHandle[] sh = null;
165     try {
166       sh = getSessionManager().getSessionFor(urn);
167     } catch (IOException e)
168     {
169       log.warn("Ignored IO Exception when trying to access sessionlist.",e);
170     }
171     File sesfile = null;
172     if (sh!=null)
173     {
174       if (sh.length==1)
175       {
176         sesfile = new File(sh[0].getPhysLoc());
177         sh[0] = null;
178       } else {
179         log.error("Raising exception for multiple session files corresponding to single URN (was : "+sessionUrn+")");
180         throw new InvalidSessionUrnException("IMPLEMENTATION ERROR: Multiple session files available for URN ("+sessionUrn+")");
181       }
182     }
183     return sesfile;
184
185   }
186
187   /**
188    * @see uk.ac.vamsas.client.IClientFactory#getIClient(uk.ac.vamsas.client.ClientHandle,
189    *      uk.ac.vamsas.client.UserHandle, java.lang.String)
190    */
191   public IClient getIClient(ClientHandle applicationHandle, UserHandle userId,
192       String sessionUrn) {
193     // locate session from Urn
194     // check Uniqueness of user + ClientHandle in the session. Update
195     // clientHandle urn accordingly.
196     // wait for lock, attach to session
197     // create client instance
198     IClient client = null;
199
200     // TODO: implement 'opening stored session' opening mechanism
201     // 1. existing session document URL is vdoc://... ?
202     // 2. check for sessionUrn being of this form.
203     // 3. if it is - locate the file and pass to new VamsasSession
204
205     try {
206       File sessionDirectory = convertSessionUrnToFile(sessionUrn);
207       // create session
208       log
209           .debug("found session directory "
210               + sessionDirectory.getAbsolutePath());
211       VamsasSession vamsasSession = new VamsasSession(sessionDirectory);
212
213       /*
214        * if (userId == null) { //create a default userHandle //with current OS
215        * user and hostname userId = new
216        * UserHandle(System.getProperty("user.name",
217        * System.getProperty("USERNAME","Joe Doe")),
218        * System.getProperty("host.name",System.getProperty("HOSTNAME",
219        * "Unknown") ));// clientName, clientVersion, sessionPath); }
220        * 
221        * 
222        * //create simple client client = new SimpleClient(userId,
223        * applicationHandle, vamsasSession);
224        */
225       client = this.initClient(sessionDirectory, userId, applicationHandle,
226           null);
227     } catch (MalformedURLException e) {
228       log.error("error while creating new IClient: incorrect session urn", e);
229       client = null;
230     } catch (InvalidSessionDocumentException e) {
231       log
232           .error("error while creating new IClient: invalid session document",
233               e);
234       client = null;
235     } catch (InvalidSessionUrnException e) {
236       log.error("error while creating new IClient: incorrect session urn", e);
237       client = null;
238     } catch (IOException e) {
239       log.error("error while creating new IClient: file access error", e);
240       client = null;
241     }
242     return client;
243   }
244
245   /**
246    * initialise the vamsas session state and create a SimpleClient object to
247    * connect to it
248    * 
249    * @param sessdir
250    *          newly created or existing session directory
251    * @param userId
252    * @param clientHandle
253    * @param vamsasDocument
254    *          null or a document to pass to SimpleCLient to write into the
255    *          sessdir
256    * @return the client
257    * @throws IOException
258    *           if there are problems in session or client creation or if the
259    *           session already has a vamsasDocument
260    * @throws InvalidSessionUrnException
261    *           for a malformed sessdir
262    */
263   private IClient initClient(File sessdir, UserHandle userId,
264       ClientHandle clientHandle, File vamsasDocument) throws IOException,
265       InvalidSessionUrnException, InvalidSessionDocumentException {
266     IClient client = null;
267     // create session
268     VamsasSession vamsasSession = null;
269     if (vamsasDocument == null) {
270       vamsasSession = new VamsasSession(sessdir);
271     } else {
272       vamsasSession = new VamsasSession(sessdir, vamsasDocument);
273     }
274     getSessionManager().addSession(vamsasSession.getSessionUrn());
275     if (userId == null) {
276       // create a default userHandle
277       // userId = new UserHandle(System.getProperty("user.name",
278       // System.getProperty("USERNAME","Joe Doe")),
279       // System.getProperty("host.name",System.getProperty("HOSTNAME",
280       // "Unknown") ));// clientName, clientVersion, sessionPath);
281       userId = new UserHandle(null, null);
282     }
283
284     // FullName and organisation should not be null (otherwise UserHandle equals
285     // method raises an java.lang.NullPointerException )
286     // use current OS user and hostname, if null
287     if (userId.getFullName() == null) {
288       userId.setFullName(System.getProperty("user.name", System.getProperty(
289           "USERNAME", "Joe Doe")));
290     }
291
292     if (userId.getOrganization() == null) {
293       userId.setOrganization(System.getProperty("host.name", System
294           .getProperty("HOSTNAME", "Unknown")));
295     }
296
297     if (clientHandle == null)
298       clientHandle = new ClientHandle("SimpleVamsasClientApp", "0.1");
299     else {
300       if (clientHandle.getClientName() == null) {
301         clientHandle.setClientName("SimpleVamsasClientApp");
302       }
303       if (clientHandle.getVersion() == null) {
304         clientHandle.setVersion("0.1");
305       }
306     }
307
308     // create simple client
309     client = new SimpleClient(userId, clientHandle, vamsasSession);
310     vamsasSession.addClient((SimpleClient) client);
311     vamsasSession.setSessionManager(this.getSessionManager());
312     return client;
313   }
314
315   /**
316    * @see uk.ac.vamsas.client.IClientFactory#getIClient(uk.ac.vamsas.client.ClientHandle,
317    *      uk.ac.vamsas.client.UserHandle)
318    */
319   public IClient getIClient(ClientHandle applicationHandle, UserHandle userId)
320       throws NoDefaultSessionException {
321     // create new session
322     // register SimpleClient and UserHandles in session
323     // create client instance
324     IClient client = null;
325     if (this.sessionArena == null)
326       throw new Error(
327           "Improperly initialised SimpleClientFactory object - null sessionArena.");
328
329     ClientHandle clientHandle = applicationHandle;
330     // create default clientHandle with "SimpleVamsasClientApp","0.1",
331     if (clientHandle == null)
332       clientHandle = new ClientHandle("SimpleVamsasClientApp", "0.1");
333     else {
334       if (clientHandle.getClientName() == null) {
335         clientHandle.setClientName("SimpleVamsasClientApp");
336       }
337
338       if (clientHandle.getVersion() == null) {
339         clientHandle.setVersion("0.1");
340       }
341     }
342     // check if any available session(s)
343     String[] availableSessions = this.getCurrentSessions();
344     if (availableSessions != null) {// there are available sessions
345       if (availableSessions.length > 1) {// more than one session if
346                                          // available... can not choose
347
348         // represents list of session as String
349         StringBuffer sessionURNs = new StringBuffer("");
350         for (int i = 0; i < availableSessions.length; i++) {
351           sessionURNs.append(availableSessions[i] + " ");
352         }
353         throw new NoDefaultSessionException(
354             "Several sessions available, please pick one: " + sessionURNs);
355       }
356
357       // check if only one session available. if yes, open it
358       if (availableSessions.length == 1) {
359         // only one session available, open it.
360         return this.getIClient(clientHandle, availableSessions[0]);
361       } else {
362         log.debug("No active session found");
363       }
364     }
365     // no session available - create a new one
366     try {
367       client = clientInNewSession(userId, clientHandle, null);
368     } catch (Exception e) {
369       throw new Error(
370           "IMPLEMENTATION ERROR: unexpected exception when creating a new session to connect to.",
371           e);
372     }
373     return client;
374   }
375
376   /**
377    * create a new session directory and possibly import an existing document
378    * into it
379    * 
380    * @param userId
381    * @param clientHandle
382    * @param vamsasDocument
383    *          null or a document file to copy into the new session
384    * @return null or a valid IClient instance
385    */
386   private IClient clientInNewSession(UserHandle userId,
387       ClientHandle clientHandle, File vamsasDocument)
388       throws InvalidSessionDocumentException, InvalidSessionUrnException {
389
390     IClient client = null;
391     try {
392       // try and make a friendly session name
393       String sesspref = "";
394       if (vamsasDocument != null) {
395         sesspref = vamsasDocument.getName().replaceAll(
396             "([^-A-Za-z0-9]|\\.vdj)", "");
397       }
398       sesspref += (new java.util.Date()).toString().replaceAll("[^-A-Za-z0-9]",
399           "_");
400       // create sessionDirectory
401       File sessdir = new File(sessionArena, sesspref + ".simpleclient");
402       if (sessdir.exists()) {
403         // make a unique session name
404         sessdir = File.createTempFile(sesspref, ".simpleclient", sessionArena);
405       } else {
406         if (!sessdir.createNewFile()) {
407           throw new Error(
408               "VAMSAS Implementation error : sesspref friendly session name is invalid on this platform - please tell the authors!");
409         }
410       }
411       log.debug("Creating new session  directory");
412       if (!(sessdir.delete() && sessdir.mkdir()))
413         throw new IOException("Could not make session directory " + sessdir);
414       client = initClient(sessdir, userId, clientHandle, vamsasDocument);
415     } catch (IOException e) {
416       log.error("error while creating new IClient", e);
417     } catch (InvalidSessionUrnException e) {
418       log.error(
419           "Unable to create new IClient. The new session urn is malformed.", e);
420     }
421
422     return client;
423   }
424
425   /**
426    * @see uk.ac.vamsas.client.IClientFactory#getCurrentSessions()
427    */
428   public String[] getCurrentSessions() {
429     String[] sessions = null;
430     try {
431       sessions = this.getSessionManager().getCurrentSessions();
432     } catch (IOException e) {
433       log.error("Unable to get available sessions", e);
434       sessions = null;
435     }
436     return sessions;
437   }
438
439   /**
440    * @return the sessionFile
441    */
442   private SimpleSessionManager getSessionManager() throws IOException {
443     if (this.sessionManager == null) {
444       this.sessionManager = new SimpleSessionManager(new File(
445           this.sessionArena, SESSION_LIST));
446     }
447     return this.sessionManager;
448   }
449
450   public IClient getNewSessionIClient(ClientHandle applicationHandle) {
451     try {
452       return clientInNewSession(null, applicationHandle, null);
453     } catch (Exception e) {
454       log.error("Failed to create new session for app with default user.", e);
455     }
456     return null;
457   }
458
459   public IClient getNewSessionIClient(ClientHandle applicationHandle,
460       UserHandle userId) {
461     try {
462       return clientInNewSession(userId, applicationHandle, null);
463     } catch (Exception e) {
464       log.error("Failed to create new session for app and user.", e);
465     }
466     return null;
467   }
468
469   private void checkImportedDocument(File vamsasDocument)
470       throws InvalidSessionDocumentException {
471     if (!vamsasDocument.exists()) {
472       throw new InvalidSessionDocumentException("File " + vamsasDocument
473           + " does not exist");
474     }
475     if (!vamsasDocument.canRead()) {
476       throw new InvalidSessionDocumentException("File " + vamsasDocument
477           + " does not exist");
478     }
479   }
480
481   public IClient openAsNewSessionIClient(ClientHandle applicationHandle,
482       File vamsasDocument) throws InvalidSessionDocumentException {
483     checkImportedDocument(vamsasDocument);
484     try {
485       return clientInNewSession(null, applicationHandle, vamsasDocument);
486     } catch (InvalidSessionUrnException e) {
487       throw new InvalidSessionDocumentException("Unexpected exception", e);
488     }
489   }
490
491   public IClient openAsNewSessionIClient(ClientHandle applicationHandle,
492       UserHandle userId, File vamsasDocument)
493       throws InvalidSessionDocumentException {
494     checkImportedDocument(vamsasDocument);
495     try {
496       return clientInNewSession(userId, applicationHandle, vamsasDocument);
497     } catch (InvalidSessionUrnException e) {
498       throw new InvalidSessionDocumentException("Unexpected exception", e);
499     }
500   }
501
502 }