fc.web.servlet
Class JDBCSession

java.lang.Object
  extended by fc.web.servlet.JDBCSession

public class JDBCSession
extends java.lang.Object

Stores session data into a database. This approach is almost always the right thing to do. Don't store session data in the servlet container memory (memory backed sessions) and don't use serialization based session persistence as implemented by various session containers. Use database sessions for storing secure and/or large amounts of data and store non-secure data directly in cookies.

Database sessions enable front-end webservers to scale easily. (No messy/brittle session migration hacks are required !). Scaling can be near infinite, but after a point (say a 100 front ends), the back-end databases may also have to be partitioned (and the appropriate partition/machine invoked for a given session-id).

Note, memory sessions are not needed even for maintaining html form state because each form's state can be uniquely created from an initial form state (common to all users) along with the per-usr data submitted per request. This is exactly how the form API works. However, memory sessions are useful for certain transient things/caches or for very quick'n'dirty apps, where using a database is more trouble than it's worth.

Given fast ethernet connections and forward marching processor/peripheral speeds, JDBC session data is typically retrieved on the order of 1-2 milliseconds (or even lesser).

Using this class requires the following database table schema in the database:

create table prefix_sessiondata (  
  session_id    varchar(50),
  name         varchar(100) not null,
  value        text  -- [or some other character/text type]
  );

create table prefix_sessionmaster (
  session_id    varchar(50) primary key,  -- the session id
  created      timestamp not null,   -- session create time
  accessed     timestamp not null,   -- session last accessed       
  is_expired   bool default 'f',     -- true if expired 
  -- a reference to the user table that associates a user_id
  -- with one or more session_id's. Optional so should be 
  -- nullable if used.
  user_id    <SQL_TYPE_FROM_YOUR_USER_TABLE>
  );

alter table prefix_sessiondata add 
         FOREIGN KEY (session_id) REFERENCES prefix_sessionmaster (session_id) 
         on delete cascade;

-- The following if a user table is present
alter table prefix_sessionmaster add
         FOREIGN KEY (user_id) REFERENCES 
          NAME_OF_USER_TABLE;
The prefix for the table names is arbitrary and must be specified in the init() for this class. When creating these tables, it is also advisable to index the session_id and user_id columns for faster performance.

The above tables can also be created by this class itself. Invoke the main method without any arguments to see usage information.

More than one [name, value] pair can be stored in the sessiondata table. This class will automatically store [name, value] pairs as seperate rows and can return a particular [name, value] pair or all [name, value] pairs for the specified session.

Note: This class allows saving data only as Strings/text. However, arbitrary binary data can also be stored but the caller should first base64 encode that data and then save it as a string. Upon retrieval, that data can be base64 decoded.

Note 2: Under some scenarios, it is important to have a seperate cleaner process that periodically deletes expired sessions from the database. This process should run via cron, some other stand-alone java code etc., and should delete sessions which are marked expired or whose create_time - last_used_time is greater than the session expiry time. Under other scenarios, sessions may be deleted after a set amount of time from the creation data regardless of when the session was last accessed.

Note 3: Typically, expired session data is simply deleted from the session tables in the database. (for example, amazon.com users have to persist their cart manually by clicking on a "save-for-later" button - which moves data to more persistent tables -- otherwise their cart session is deleted from the database when the session expires). It is however possible that instead of deleting sessions, the sessions are instead marked as "expired" but not deleted from the database. (this is done via the setDeleteExpiredSessions(boolean) method.


Nested Class Summary
static class JDBCSession.Info
          Information about a session.
 
Constructor Summary
JDBCSession()
           
 
Method Summary
static void add(java.sql.Connection con, java.lang.String sessionID, java.lang.String key, java.lang.String value)
          Saves the tuple [sessionID, key, value] in the database.
static void addAll(java.sql.Connection con, java.lang.String sessionID, java.util.Map data)
          Adds all [key, value] pairs in the specified map to the session with the specified sessionID.
static void create(java.sql.Connection con, java.lang.String sessionID)
          Creates a new session.
static void create(java.sql.Connection con, java.lang.String sessionID, java.lang.String userID)
          Creates a new session.
static void createJDBCTables(java.sql.Connection con, java.lang.String userTableName, java.lang.String prefix, boolean mysql)
          Creates database tables for storing session data.
static void delete(java.sql.Connection con, java.lang.String sessionID, java.lang.String key)
          Deletes both the key and value specified by the sessionID and key.
static boolean exists(java.sql.Connection con, java.lang.String sessionID)
          Returns true is the specified sessionID is valid (i.e., the specified sessionID exists in the database and has not expired).
static void expire(java.sql.Connection con, java.lang.String sessionID)
          Expires the session.
static void expireInactiveSessions(java.sql.Connection con)
          Utility method that deletes (or marked as expired depending on setDeleteExpiredSessions(boolean)) all sessions in the database that have exceeded the maximum inactive time.
static java.lang.String get(java.sql.Connection con, java.lang.String sessionID, java.lang.String key)
          Returns the value associated with the specified sessionID and key.
static java.util.Map getAll(java.sql.Connection con, java.lang.String sessionID)
          Returns a map of all [key, value] pairs associated with the specified sessionID.
static int getExpireTime()
          Returns the current expire interval (seconds after which sessions can be considered eligible for removal).
static java.util.List getForUser(java.sql.Connection con, int userID)
          Same as getForUser(Connection, String) but takes a numeric userID.
static java.util.List getForUser(java.sql.Connection con, java.lang.String userID)
          Returns a List containing session information about all sessions associated with the specified ID.
static void init(java.lang.String prefix)
           
static void init(java.lang.String prefix, Log logger)
          Initializes stuff internally within this class.
static void main(java.lang.String[] args)
           
static void put(java.sql.Connection con, java.lang.String sessionID, java.lang.String key, java.lang.String value)
          An alias for the #add method.
static void putAll(java.sql.Connection con, java.lang.String sessionID, java.util.Map data)
          An alias for the addAll method.
static JDBCSession.Info sessionInfo(java.sql.Connection con, java.lang.String sessionID)
          Returns session information from the session master table.
static void setDeleteExpiredSessions(boolean val)
          By default expired sessions are deleted from the db.
static void setExpireTime(int seconds)
          Sessions inactive for greater than these number of seconds will be eligible for expiry.
static void tieToUser(java.sql.Connection con, java.lang.String sessionID, java.lang.String userID)
          Associates the specified sessionID with the specified userID.
 java.lang.String update(java.sql.Connection con, java.lang.String sessionID, java.lang.String key, java.lang.String newvalue)
          Updates the value for the specified sessionID and key in the database.
 
Methods inherited from class java.lang.Object
equals, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Constructor Detail

JDBCSession

public JDBCSession()
Method Detail

init

public static void init(java.lang.String prefix)

init

public static void init(java.lang.String prefix,
                        Log logger)
Initializes stuff internally within this class. This method must be called once before any other method in this class.

Parameters:
prefix - arbitrary prefix to use for names of session tables in the db. Specify null for no prefix.
logger - the logging destination for methods in this class. Specify null to use a default logger.

setDeleteExpiredSessions

public static void setDeleteExpiredSessions(boolean val)
By default expired sessions are deleted from the db. If this is set to false, they are instead marked as expired in the db.


create

public static void create(java.sql.Connection con,
                          java.lang.String sessionID,
                          java.lang.String userID)
                   throws java.sql.SQLException
Creates a new session.

Parameters:
con - a jdbc connection
sessionID - value for sessionID, the SessionUtil.newSessionID() can be used to generate this. Should not be null. It is not recommended that the servlet container generated jsession_id cookie be used for this value. This sessionID is then later used to retrieve and work with the created session and this sessionID will typically be stored as a cookie or URL encoded on the client.
userID - a userID to associate with this session. Note, userID's in user tables are typically auto generated numerical sequences. Stringify numerical values before passing them into this method. This value should not be null, otherwise a runtime exception will be thrown.
Throws:
java.sql.SQLException - if a SQL error occurs. Note, also thrown if the session_id already exists in the database (since all session_id's must be unique).

create

public static void create(java.sql.Connection con,
                          java.lang.String sessionID)
                   throws java.sql.SQLException
Creates a new session. The specified session can later be optionally to a userID by invoking the tieToUser(java.sql.Connection, java.lang.String, java.lang.String) method.

Note, sessionID's are typically be stored as a cookie or URL encoded on the client and are thus unique per browser/client). A user is not required to login to have session data.

Parameters:
con - a jdbc connection
sessionID - value for sessionID, the SessionUtil.newSessionID() can be used to generate this. Should not be null. It is not recommended that the servlet container generated jsession_id cookie be used for this value. This sessionID is then later used to retrieve and work with the created session.
Throws:
java.sql.SQLException - if a SQL error occurs. Note, also thrown if the session_id already exists in the database (since all session_id's must be unique).

expire

public static void expire(java.sql.Connection con,
                          java.lang.String sessionID)
                   throws java.sql.SQLException
Expires the session. By default, deletes all session data associated with the specified sessionID from the database. If deleteExpiredSessions is set to true, then the session is marked as expired in the database but the rows are not deleted from the db.

Either way, after this method returns, the sessionID will not longer be valid.

Throws:
java.sql.SQLException

tieToUser

public static void tieToUser(java.sql.Connection con,
                             java.lang.String sessionID,
                             java.lang.String userID)
                      throws java.sql.SQLException
Associates the specified sessionID with the specified userID.

Note: Depending on the application, more than 1 sessionID can be associated with the same userID in the session master table.

Throws:
java.sql.SQLException

expireInactiveSessions

public static void expireInactiveSessions(java.sql.Connection con)
                                   throws java.sql.SQLException
Utility method that deletes (or marked as expired depending on setDeleteExpiredSessions(boolean)) all sessions in the database that have exceeded the maximum inactive time.

Throws:
java.sql.SQLException

setExpireTime

public static void setExpireTime(int seconds)
Sessions inactive for greater than these number of seconds will be eligible for expiry.

Note: these expired sessions will still not be expired until the expireInactiveSessions() method is invoked.

Defaults to 8 hours (=60*60*8 seconds)


getExpireTime

public static int getExpireTime()
Returns the current expire interval (seconds after which sessions can be considered eligible for removal).


exists

public static boolean exists(java.sql.Connection con,
                             java.lang.String sessionID)
                      throws java.sql.SQLException
Returns true is the specified sessionID is valid (i.e., the specified sessionID exists in the database and has not expired).

Note: this method does not expire the session itself or check for validity. Sessions should be expired as/when needed by calling the expire method.

Throws:
java.sql.SQLException

sessionInfo

public static JDBCSession.Info sessionInfo(java.sql.Connection con,
                                           java.lang.String sessionID)
                                    throws java.sql.SQLException
Returns session information from the session master table. This information is returned as a info object encapsulating the master row for the given sessionID.

Returns null if the given sessionID has expired and/or was not found in the database.

Throws:
java.sql.SQLException

getForUser

public static java.util.List getForUser(java.sql.Connection con,
                                        java.lang.String userID)
                                 throws java.sql.SQLException
Returns a List containing session information about all sessions associated with the specified ID. Returns an empty list if no sessions are found for the specified userID.

Note, the specified userID can be null in which case all sessions with null userID's will be returned.

Throws:
java.sql.SQLException

getForUser

public static java.util.List getForUser(java.sql.Connection con,
                                        int userID)
                                 throws java.sql.SQLException
Same as getForUser(Connection, String) but takes a numeric userID.

Throws:
java.sql.SQLException

getAll

public static java.util.Map getAll(java.sql.Connection con,
                                   java.lang.String sessionID)
                            throws java.sql.SQLException
Returns a map of all [key, value] pairs associated with the specified sessionID. The returned map will be empty is no [key, value] pairs are present or if the specified sessionID is not found in the database or if the specified session has expired.

Throws:
java.sql.SQLException

get

public static java.lang.String get(java.sql.Connection con,
                                   java.lang.String sessionID,
                                   java.lang.String key)
                            throws java.sql.SQLException
Returns the value associated with the specified sessionID and key.

Returns null if the specified session has expired, or the specified key does not exist in the database or exists but contains a null in the database.

Throws:
java.sql.SQLException

delete

public static void delete(java.sql.Connection con,
                          java.lang.String sessionID,
                          java.lang.String key)
                   throws java.sql.SQLException
Deletes both the key and value specified by the sessionID and key. Does nothing if the sessionID or key does not exist in the database.

Throws:
java.sql.SQLException

add

public static void add(java.sql.Connection con,
                       java.lang.String sessionID,
                       java.lang.String key,
                       java.lang.String value)
                throws java.sql.SQLException
Saves the tuple [sessionID, key, value] in the database.

The specified sessionID must exist and be valid in the database otherwise a SQLException will be thrown.

Throws:
java.sql.SQLException

addAll

public static void addAll(java.sql.Connection con,
                          java.lang.String sessionID,
                          java.util.Map data)
                   throws java.sql.SQLException
Adds all [key, value] pairs in the specified map to the session with the specified sessionID.

Throws:
java.sql.SQLException

put

public static void put(java.sql.Connection con,
                       java.lang.String sessionID,
                       java.lang.String key,
                       java.lang.String value)
                throws java.sql.SQLException
An alias for the #add method.

Throws:
java.sql.SQLException

putAll

public static void putAll(java.sql.Connection con,
                          java.lang.String sessionID,
                          java.util.Map data)
                   throws java.sql.SQLException
An alias for the addAll method.

Throws:
java.sql.SQLException

update

public java.lang.String update(java.sql.Connection con,
                               java.lang.String sessionID,
                               java.lang.String key,
                               java.lang.String newvalue)
                        throws java.sql.SQLException
Updates the value for the specified sessionID and key in the database.

The specified sessionID and keys must exist in the database prior to calling this method,otherwise a SQLException will be thrown.

Throws:
java.sql.SQLException

main

public static void main(java.lang.String[] args)
                 throws java.lang.Exception
Throws:
java.lang.Exception

createJDBCTables

public static void createJDBCTables(java.sql.Connection con,
                                    java.lang.String userTableName,
                                    java.lang.String prefix,
                                    boolean mysql)
                             throws java.lang.Exception
Creates database tables for storing session data. This method can be called programmatically but is typically invoked by the main method. Invoke the main method without any flags to get usage information.

Throws:
java.lang.Exception