When working with the Lotus Domino Java API special care has to be made not to use objects created in one (lotus.domino.)Session in another Session. When the application isn’t multi-threaded, such as normal Java agents running under Domino, this isn’t an issue but when writing servlets this can become an issue.
A much used approach is to initialize the Session in the doGet() or doPost() of the servlet and then pass the session around with you. While this approach works you’ll have to “polute” all your interfaces with a Session argument which is really bad in my opinion. When doing Domino development this Session should simply “be there” since it is the foundation you build on and hence you shouldn’t have to pass it around.
There are a number of solutions you could use to work around this. One solution is to have a Singleton that keeps track of which Session each user should use i.e. by using a HashMap and use the active username as the key to the Map mapping usernames to Sessions. While this works the approach can have issues if the same username can make multiple requests at the same it e.g. more users using the same username. Another solution would be to have the first object needing the Session create it and store it in the users javax.servlet.http.HttpSession. This is also bad since you have to pass around a pointer to the HttpSession or HttpServletRequest instead of the Session (hence you might as well pass the Session around). Using this approach there might also be issues with serialization of HttpSessions in clustered environments environments where the HttpSession is actually a facade to a database. I think Websphere use the latter approach to handle node failover.
Since I’m writing this I must have found a better approach right? Well… Yes…
java.lang.ThreadLocal is an object you can use where the JVM makes sure each thread has its own copy. Using ThreadLocal in combination with the Factory design pattern the Session becomes ubiquitous. Instead of passing the Session around you simply ask for your Session and that’s it. If it hasn’t been created yet it is initialized before it being handed of to you. Slick.
Below is an example of how such a SessionFactory using a ThreadLocal could look.
import lotus.domino.Session;
/**
* Class for providing access to Domino sessions. The class doesn't handle
* NotesThreads.
*
*/
public class SessionFactory {
// declarations
private static final ThreadLocal pSession = new ThreadLocal();
/**
* Method for returning a session to Domino. The returned session
* can be either local or remote.
*
* @return Session to Domino.
*/
public static Session getSession() throws Exception {
// declarations
Session session = null;
// look for a session in the thread local
session = (Session)pSession.get();
if (null != session) {
// lotus.domino.Session found in ThreadLocal - returning it;
}
return session;
}
try {
// get session from NotesFactory
...
...
} catch (Exception e) {
// log the error
throw new Exception("Unable to get session for some reason", e);
// rethrow
throw e;
}
}
}
To use the SessionFactory to get the Session you simply use a snippet like this:
Session session = SessionFactory.getSession();
I left out the code that actually goes out and creates a remote CORBA Session object using the NotesFactory if it doesn’t exist. I use an approach where I hold the information that varies (server hostname, username and password) in a properties file that I consult before going ahead and creating the Session. This allows me to reuse the class easily.
I hope this approach will ease your multi-threaded Domino Java API development.
Note: I explicitly mentioned that I’m doing a remote CORBA session since they don’t require handling NotesThreads. I’ll post later about an easy solution when using local Sessions and how you could handle those nasty threads transparantly…
Note: Make sure to recycle() the Session once the thread is done using it. The simplest way is to use a servlet filter approach.
Update on 01/Feb/06: Corrected the syntax of the web.xml file and added information on how to handle recycle() the Session using a servlet filter.
