applied LGPLv3 and source code formatting.
[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   private File convertSessionUrnToFile(String sessionUrn)
151       throws InvalidSessionUrnException {
152     if (sessionUrn == null) {
153       log.debug("Incorrect URN: can not open session.");
154       throw new InvalidSessionUrnException();
155     }
156
157     SessionUrn urn = new SessionUrn(sessionUrn);
158     return urn.asFile();
159
160   }
161
162   /**
163    * @see uk.ac.vamsas.client.IClientFactory#getIClient(uk.ac.vamsas.client.ClientHandle,
164    *      uk.ac.vamsas.client.UserHandle, java.lang.String)
165    */
166   public IClient getIClient(ClientHandle applicationHandle, UserHandle userId,
167       String sessionUrn) {
168     // locate session from Urn
169     // check Uniqueness of user + ClientHandle in the session. Update
170     // clientHandle urn accordingly.
171     // wait for lock, attach to session
172     // create client instance
173     IClient client = null;
174
175     // TODO: implement 'opening stored session' opening mechanism
176     // 1. existing session document URL is vdoc://... ?
177     // 2. check for sessionUrn being of this form.
178     // 3. if it is - locate the file and pass to new VamsasSession
179
180     try {
181       File sessionDirectory = this.convertSessionUrnToFile(sessionUrn);
182       // create session
183       log
184           .debug("found session directory "
185               + sessionDirectory.getAbsolutePath());
186       VamsasSession vamsasSession = new VamsasSession(sessionDirectory);
187
188       /*
189        * if (userId == null) { //create a default userHandle //with current OS
190        * user and hostname userId = new
191        * UserHandle(System.getProperty("user.name",
192        * System.getProperty("USERNAME","Joe Doe")),
193        * System.getProperty("host.name",System.getProperty("HOSTNAME",
194        * "Unknown") ));// clientName, clientVersion, sessionPath); }
195        * 
196        * 
197        * //create simple client client = new SimpleClient(userId,
198        * applicationHandle, vamsasSession);
199        */
200       client = this.initClient(sessionDirectory, userId, applicationHandle,
201           null);
202     } catch (MalformedURLException e) {
203       log.error("error while creating new IClient: incorrect session urn", e);
204       client = null;
205     } catch (InvalidSessionDocumentException e) {
206       log
207           .error("error while creating new IClient: invalid session document",
208               e);
209       client = null;
210     } catch (InvalidSessionUrnException e) {
211       log.error("error while creating new IClient: incorrect session urn", e);
212       client = null;
213     } catch (IOException e) {
214       log.error("error while creating new IClient: file access error", e);
215       client = null;
216     }
217     return client;
218   }
219
220   /**
221    * initialise the vamsas session state and create a SimpleClient object to
222    * connect to it
223    * 
224    * @param sessdir
225    *          newly created or existing session directory
226    * @param userId
227    * @param clientHandle
228    * @param vamsasDocument
229    *          null or a document to pass to SimpleCLient to write into the
230    *          sessdir
231    * @return the client
232    * @throws IOException
233    *           if there are problems in session or client creation or if the
234    *           session already has a vamsasDocument
235    * @throws InvalidSessionUrnException
236    *           for a malformed sessdir
237    */
238   private IClient initClient(File sessdir, UserHandle userId,
239       ClientHandle clientHandle, File vamsasDocument) throws IOException,
240       InvalidSessionUrnException, InvalidSessionDocumentException {
241     IClient client = null;
242     // create session
243     VamsasSession vamsasSession = null;
244     if (vamsasDocument == null) {
245       vamsasSession = new VamsasSession(sessdir);
246     } else {
247       vamsasSession = new VamsasSession(sessdir, vamsasDocument);
248     }
249
250     this.getSessionManager().addSession(
251         new SessionHandle(new SessionUrn(vamsasSession).getSessionUrn()));
252     if (userId == null) {
253       // create a default userHandle
254       // userId = new UserHandle(System.getProperty("user.name",
255       // System.getProperty("USERNAME","Joe Doe")),
256       // System.getProperty("host.name",System.getProperty("HOSTNAME",
257       // "Unknown") ));// clientName, clientVersion, sessionPath);
258       userId = new UserHandle(null, null);
259     }
260
261     // FullName and organisation should not be null (otherwise UserHandle equals
262     // method raises an java.lang.NullPointerException )
263     // use current OS user and hostname, if null
264     if (userId.getFullName() == null) {
265       userId.setFullName(System.getProperty("user.name", System.getProperty(
266           "USERNAME", "Joe Doe")));
267     }
268
269     if (userId.getOrganization() == null) {
270       userId.setOrganization(System.getProperty("host.name", System
271           .getProperty("HOSTNAME", "Unknown")));
272     }
273
274     if (clientHandle == null)
275       clientHandle = new ClientHandle("SimpleVamsasClientApp", "0.1");
276     else {
277       if (clientHandle.getClientName() == null) {
278         clientHandle.setClientName("SimpleVamsasClientApp");
279       }
280       if (clientHandle.getVersion() == null) {
281         clientHandle.setVersion("0.1");
282       }
283     }
284
285     // create simple client
286     client = new SimpleClient(userId, clientHandle, vamsasSession);
287     vamsasSession.addClient((SimpleClient) client);
288     vamsasSession.setSessionManager(this.getSessionManager());
289     return client;
290   }
291
292   /**
293    * @see uk.ac.vamsas.client.IClientFactory#getIClient(uk.ac.vamsas.client.ClientHandle,
294    *      uk.ac.vamsas.client.UserHandle)
295    */
296   public IClient getIClient(ClientHandle applicationHandle, UserHandle userId)
297       throws NoDefaultSessionException {
298     // create new session
299     // register SimpleClient and UserHandles in session
300     // create client instance
301     IClient client = null;
302     if (this.sessionArena == null)
303       throw new Error(
304           "Improperly initialised SimpleClientFactory object - null sessionArena.");
305
306     ClientHandle clientHandle = applicationHandle;
307     // create default clientHandle with "SimpleVamsasClientApp","0.1",
308     if (clientHandle == null)
309       clientHandle = new ClientHandle("SimpleVamsasClientApp", "0.1");
310     else {
311       if (clientHandle.getClientName() == null) {
312         clientHandle.setClientName("SimpleVamsasClientApp");
313       }
314
315       if (clientHandle.getVersion() == null) {
316         clientHandle.setVersion("0.1");
317       }
318     }
319     // check if any available session(s)
320     String[] availableSessions = this.getCurrentSessions();
321     if (availableSessions != null) {// there are available sessions
322       if (availableSessions.length > 1) {// more than one session if
323                                          // available... can not choose
324
325         // represents list of session as String
326         StringBuffer sessionURNs = new StringBuffer("");
327         for (int i = 0; i < availableSessions.length; i++) {
328           sessionURNs.append(availableSessions[i] + " ");
329         }
330         throw new NoDefaultSessionException(
331             "Several sessions available, please pick one: " + sessionURNs);
332       }
333
334       // check if only one session available. if yes, open it
335       if (availableSessions.length == 1) {
336         // only one session available, open it.
337         return this.getIClient(clientHandle, availableSessions[0]);
338       } else {
339         log.debug("No active session found");
340       }
341     }
342     // no session available - create a new one
343     try {
344       client = clientInNewSession(userId, clientHandle, null);
345     } catch (Exception e) {
346       throw new Error(
347           "IMPLEMENTATION ERROR: unexpected exception when creating a new session to connect to.",
348           e);
349     }
350     return client;
351   }
352
353   /**
354    * create a new session directory and possibly import an existing document
355    * into it
356    * 
357    * @param userId
358    * @param clientHandle
359    * @param vamsasDocument
360    *          null or a document file to copy into the new session
361    * @return null or a valid IClient instance
362    */
363   private IClient clientInNewSession(UserHandle userId,
364       ClientHandle clientHandle, File vamsasDocument)
365       throws InvalidSessionDocumentException, InvalidSessionUrnException {
366
367     IClient client = null;
368     try {
369       // try and make a friendly session name
370       String sesspref = "";
371       if (vamsasDocument != null) {
372         sesspref = vamsasDocument.getName().replaceAll(
373             "([^-A-Za-z0-9]|\\.vdj)", "");
374       }
375       sesspref += (new java.util.Date()).toString().replaceAll("[^-A-Za-z0-9]",
376           "_");
377       // create sessionDirectory
378       File sessdir = new File(sessionArena, sesspref + ".simpleclient");
379       if (sessdir.exists()) {
380         // make a unique session name
381         sessdir = File.createTempFile(sesspref, ".simpleclient", sessionArena);
382       } else {
383         if (!sessdir.createNewFile()) {
384           throw new Error(
385               "VAMSAS Implementation error : sesspref friendly session name is invalid on this platform - please tell the authors!");
386         }
387       }
388       log.debug("Creating new session  directory");
389       if (!(sessdir.delete() && sessdir.mkdir()))
390         throw new IOException("Could not make session directory " + sessdir);
391       client = initClient(sessdir, userId, clientHandle, vamsasDocument);
392     } catch (IOException e) {
393       log.error("error while creating new IClient", e);
394     } catch (InvalidSessionUrnException e) {
395       log.error(
396           "Unable to create new IClient. The new session urn is malformed.", e);
397     }
398
399     return client;
400   }
401
402   /**
403    * @see uk.ac.vamsas.client.IClientFactory#getCurrentSessions()
404    */
405   public String[] getCurrentSessions() {
406     String[] sessions = null;
407     try {
408       sessions = this.getSessionManager().getCurrentSessions();
409     } catch (IOException e) {
410       log.error("Unable to get available sessions", e);
411       sessions = null;
412     }
413     return sessions;
414   }
415
416   /**
417    * @return the sessionFile
418    */
419   private SimpleSessionManager getSessionManager() throws IOException {
420     if (this.sessionManager == null) {
421       this.sessionManager = new SimpleSessionManager(new File(
422           this.sessionArena, SESSION_LIST));
423     }
424     return this.sessionManager;
425   }
426
427   public IClient getNewSessionIClient(ClientHandle applicationHandle) {
428     try {
429       return clientInNewSession(null, applicationHandle, null);
430     } catch (Exception e) {
431       log.error("Failed to create new session for app with default user.", e);
432     }
433     return null;
434   }
435
436   public IClient getNewSessionIClient(ClientHandle applicationHandle,
437       UserHandle userId) {
438     try {
439       return clientInNewSession(userId, applicationHandle, null);
440     } catch (Exception e) {
441       log.error("Failed to create new session for app and user.", e);
442     }
443     return null;
444   }
445
446   private void checkImportedDocument(File vamsasDocument)
447       throws InvalidSessionDocumentException {
448     if (!vamsasDocument.exists()) {
449       throw new InvalidSessionDocumentException("File " + vamsasDocument
450           + " does not exist");
451     }
452     if (!vamsasDocument.canRead()) {
453       throw new InvalidSessionDocumentException("File " + vamsasDocument
454           + " does not exist");
455     }
456   }
457
458   public IClient openAsNewSessionIClient(ClientHandle applicationHandle,
459       File vamsasDocument) throws InvalidSessionDocumentException {
460     checkImportedDocument(vamsasDocument);
461     try {
462       return clientInNewSession(null, applicationHandle, vamsasDocument);
463     } catch (InvalidSessionUrnException e) {
464       throw new InvalidSessionDocumentException("Unexpected exception", e);
465     }
466   }
467
468   public IClient openAsNewSessionIClient(ClientHandle applicationHandle,
469       UserHandle userId, File vamsasDocument)
470       throws InvalidSessionDocumentException {
471     checkImportedDocument(vamsasDocument);
472     try {
473       return clientInNewSession(userId, applicationHandle, vamsasDocument);
474     } catch (InvalidSessionUrnException e) {
475       throw new InvalidSessionDocumentException("Unexpected exception", e);
476     }
477   }
478
479 }