verson 0.2 LGPL licensed source and jars
[vamsas.git] / src / uk / ac / vamsas / client / simpleclient / SimpleClientFactory.java
1 /*
2  * This file is part of the Vamsas Client version 0.2. 
3  * Copyright 2010 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,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    * @param preferredName - name to use instead of vamsasDocument path when creating new session URN 
257    * @return the client
258    * @throws IOException
259    *           if there are problems in session or client creation or if the
260    *           session already has a vamsasDocument
261    * @throws InvalidSessionUrnException
262    *           for a malformed sessdir
263    */
264   private IClient initClient(File sessdir, UserHandle userId,
265       ClientHandle clientHandle, File vamsasDocument, String preferredName) throws IOException,
266       InvalidSessionUrnException, InvalidSessionDocumentException {
267     IClient client = null;
268     // create session
269     VamsasSession vamsasSession = null;
270     if (vamsasDocument == null) {
271       vamsasSession = new VamsasSession(sessdir);
272     } else {
273       vamsasSession = new VamsasSession(sessdir, vamsasDocument,preferredName);
274     }
275     getSessionManager().addSession(vamsasSession.getSessionUrn());
276     if (userId == null) {
277       // create a default userHandle
278       // userId = new UserHandle(System.getProperty("user.name",
279       // System.getProperty("USERNAME","Joe Doe")),
280       // System.getProperty("host.name",System.getProperty("HOSTNAME",
281       // "Unknown") ));// clientName, clientVersion, sessionPath);
282       userId = new UserHandle(null, null);
283     }
284
285     // FullName and organisation should not be null (otherwise UserHandle equals
286     // method raises an java.lang.NullPointerException )
287     // use current OS user and hostname, if null
288     if (userId.getFullName() == null) {
289       userId.setFullName(System.getProperty("user.name", System.getProperty(
290           "USERNAME", "Joe Doe")));
291     }
292
293     if (userId.getOrganization() == null) {
294       userId.setOrganization(System.getProperty("host.name", System
295           .getProperty("HOSTNAME", "Unknown")));
296     }
297
298     if (clientHandle == null)
299       clientHandle = new ClientHandle("SimpleVamsasClientApp", "0.1");
300     else {
301       if (clientHandle.getClientName() == null) {
302         clientHandle.setClientName("SimpleVamsasClientApp");
303       }
304       if (clientHandle.getVersion() == null) {
305         clientHandle.setVersion("0.1");
306       }
307     }
308
309     // create simple client
310     client = new SimpleClient(userId, clientHandle, vamsasSession);
311     vamsasSession.addClient((SimpleClient) client);
312     vamsasSession.setSessionManager(this.getSessionManager());
313     return client;
314   }
315
316   /**
317    * @see uk.ac.vamsas.client.IClientFactory#getIClient(uk.ac.vamsas.client.ClientHandle,
318    *      uk.ac.vamsas.client.UserHandle)
319    */
320   public IClient getIClient(ClientHandle applicationHandle, UserHandle userId)
321       throws NoDefaultSessionException {
322     // create new session
323     // register SimpleClient and UserHandles in session
324     // create client instance
325     IClient client = null;
326     if (this.sessionArena == null)
327       throw new Error(
328           "Improperly initialised SimpleClientFactory object - null sessionArena.");
329
330     ClientHandle clientHandle = applicationHandle;
331     // create default clientHandle with "SimpleVamsasClientApp","0.1",
332     if (clientHandle == null)
333       clientHandle = new ClientHandle("SimpleVamsasClientApp", "0.1");
334     else {
335       if (clientHandle.getClientName() == null) {
336         clientHandle.setClientName("SimpleVamsasClientApp");
337       }
338
339       if (clientHandle.getVersion() == null) {
340         clientHandle.setVersion("0.1");
341       }
342     }
343     // check if any available session(s)
344     String[] availableSessions = this.getCurrentSessions();
345     if (availableSessions != null) {// there are available sessions
346       if (availableSessions.length > 1) {// more than one session if
347                                          // available... can not choose
348
349         // represents list of session as String
350         StringBuffer sessionURNs = new StringBuffer("");
351         for (int i = 0; i < availableSessions.length; i++) {
352           sessionURNs.append(availableSessions[i] + " ");
353         }
354         throw new NoDefaultSessionException(
355             "Several sessions available, please pick one: " + sessionURNs);
356       }
357
358       // check if only one session available. if yes, open it
359       if (availableSessions.length == 1) {
360         // only one session available, open it.
361         return this.getIClient(clientHandle, availableSessions[0]);
362       } else {
363         log.debug("No active session found");
364       }
365     }
366     // no session available - create a new one
367     try {
368       client = clientInNewSession(userId, clientHandle, null,null);
369     } catch (Exception e) {
370       throw new Error(
371           "IMPLEMENTATION ERROR: unexpected exception when creating a new session to connect to.",
372           e);
373     }
374     return client;
375   }
376
377   /**
378    * create a new session directory and possibly import an existing document
379    * into it
380    * 
381    * @param userId
382    * @param clientHandle
383    * @param vamsasDocument
384    *          null or a document file to copy into the new session
385    *          @param preferredName - name to be used as base for the new session URN
386    * @return null or a valid IClient instance
387    */
388   private IClient clientInNewSession(UserHandle userId,
389       ClientHandle clientHandle, File vamsasDocument, String preferredName)
390       throws InvalidSessionDocumentException, InvalidSessionUrnException {
391
392     IClient client = null;
393     try {
394       // try and make a friendly session name
395       String sesspref = "";
396       if (vamsasDocument != null) {
397         if (preferredName!=null)
398         {
399           sesspref = preferredName.replaceAll(
400               "([^-A-Za-z0-9]|\\.vdj)", "");
401         } else {
402           sesspref = vamsasDocument.getName().replaceAll(
403               "([^-A-Za-z0-9]|\\.vdj)", "");
404         }
405       }
406       sesspref += (new java.util.Date()).toString().replaceAll("[^-A-Za-z0-9]",
407           "_");
408       // create sessionDirectory
409       File sessdir = new File(sessionArena, sesspref + ".simpleclient");
410       if (sessdir.exists()) {
411         // make a unique session name
412         sessdir = File.createTempFile(sesspref, ".simpleclient", sessionArena);
413       } else {
414         if (!sessdir.createNewFile()) {
415           throw new Error(
416               "VAMSAS Implementation error : sesspref friendly session name is invalid on this platform - please tell the authors!");
417         }
418       }
419       log.debug("Creating new session  directory");
420       if (!(sessdir.delete() && sessdir.mkdir()))
421         throw new IOException("Could not make session directory " + sessdir);
422       client = initClient(sessdir, userId, clientHandle, vamsasDocument, preferredName);
423     } catch (IOException e) {
424       log.error("error while creating new IClient", e);
425     } catch (InvalidSessionUrnException e) {
426       log.error(
427           "Unable to create new IClient. The new session urn is malformed.", e);
428     }
429
430     return client;
431   }
432
433   /**
434    * @see uk.ac.vamsas.client.IClientFactory#getCurrentSessions()
435    */
436   public String[] getCurrentSessions() {
437     String[] sessions = null;
438     try {
439       sessions = this.getSessionManager().getCurrentSessions();
440     } catch (IOException e) {
441       log.error("Unable to get available sessions", e);
442       sessions = null;
443     }
444     return sessions;
445   }
446
447   /**
448    * @return the sessionFile
449    */
450   private SimpleSessionManager getSessionManager() throws IOException {
451     if (this.sessionManager == null) {
452       this.sessionManager = new SimpleSessionManager(new File(
453           this.sessionArena, SESSION_LIST));
454     }
455     return this.sessionManager;
456   }
457
458   public IClient getNewSessionIClient(ClientHandle applicationHandle) {
459     try {
460       return clientInNewSession(null, applicationHandle, null,null);
461     } catch (Exception e) {
462       log.error("Failed to create new session for app with default user.", e);
463     }
464     return null;
465   }
466
467   public IClient getNewSessionIClient(ClientHandle applicationHandle,
468       UserHandle userId) {
469     try {
470       return clientInNewSession(userId, applicationHandle, null,null);
471     } catch (Exception e) {
472       log.error("Failed to create new session for app and user.", e);
473     }
474     return null;
475   }
476
477   private void checkImportedDocument(File vamsasDocument)
478       throws InvalidSessionDocumentException {
479     if (!vamsasDocument.exists()) {
480       throw new InvalidSessionDocumentException("File " + vamsasDocument
481           + " does not exist");
482     }
483     if (!vamsasDocument.canRead()) {
484       throw new InvalidSessionDocumentException("File " + vamsasDocument
485           + " does not exist");
486     }
487   }
488
489   public IClient openAsNewSessionIClient(ClientHandle applicationHandle,
490       File vamsasDocument) throws InvalidSessionDocumentException {
491     checkImportedDocument(vamsasDocument);
492     try {
493       return clientInNewSession(null, applicationHandle, vamsasDocument,null);
494     } catch (InvalidSessionUrnException e) {
495       throw new InvalidSessionDocumentException("Unexpected exception", e);
496     }
497   }
498
499   public IClient openAsNewSessionIClient(ClientHandle applicationHandle,
500       UserHandle userId, File vamsasDocument)
501       throws InvalidSessionDocumentException {
502     checkImportedDocument(vamsasDocument);
503     try {
504       return clientInNewSession(userId, applicationHandle, vamsasDocument,null);
505     } catch (InvalidSessionUrnException e) {
506       throw new InvalidSessionDocumentException("Unexpected exception", e);
507     }
508   }
509
510   public IClient openAsNewSessionIClient(ClientHandle applicationHandle,
511       File vamsasDocument, String sessionName)
512       throws InvalidSessionDocumentException {
513     checkImportedDocument(vamsasDocument);
514     try {
515       return clientInNewSession(null, applicationHandle, vamsasDocument, sessionName);
516     } catch (InvalidSessionUrnException e) {
517       throw new InvalidSessionDocumentException("Unexpected exception", e);
518     }
519   }
520
521   public IClient openAsNewSessionIClient(ClientHandle applicationHandle,
522       UserHandle userId, File vamsasDocument, String sessionName)
523       throws InvalidSessionDocumentException {
524     checkImportedDocument(vamsasDocument);
525     try {
526       return clientInNewSession(userId, applicationHandle, vamsasDocument, sessionName);
527     } catch (InvalidSessionUrnException e) {
528       throw new InvalidSessionDocumentException("Unexpected exception", e);
529     }
530   }
531
532 }