fc.web.servlet
Class JDBCSession

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

public final class JDBCSession
extends 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 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 sessiondata ( 
  session_id    varchar(50),
  name         varchar(100) not null,
  value        text  -- [or some other character/text type]
  );

create table 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 sessiondata add 
         FOREIGN KEY (session_id) REFERENCES sessionmaster (session_id) 
         on delete cascade;

-- The following if a user table is present
alter table sessionmaster add
         FOREIGN KEY (user_id) REFERENCES 
          NAME_OF_USER_TABLE;

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. The table names sessionmaster and sessiondata come from final static variables in this class. If you want to use different names, change these variables and recompile this class.

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 separate 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.
 
Field Summary
static String SESSIONDATA_TABLE
           
static String SESSIONMASTER_TABLE
           
 
Method Summary
 void add(Connection con, String sessionID, String key, String value)
          Saves the tuple [sessionID, key, value] in the database.
 void addAll(Connection con, String sessionID, Map data)
          Adds all [key, value] pairs in the specified map to the session with the specified sessionID.
 void create(Connection con, String sessionID)
          Creates a new session.
 void create(Connection con, String sessionID, String userID)
          Creates a new session.
static void createJDBCTables(Connection con, String userTableName, boolean mysql)
          Creates database tables for storing session data.
 void delete(Connection con, String sessionID, String key)
          Deletes both the key and value specified by the sessionID and key.
 boolean exists(Connection con, String sessionID)
          Returns true is the specified sessionID is valid (i.e., the specified sessionID exists in the database and has not expired).
 void expire(Connection con, String sessionID)
          Expires the session.
 void expireInactiveSessions(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.
 String get(Connection con, String sessionID, String key)
          Returns the value associated with the specified sessionID and key.
 Map getAll(Connection con, String sessionID)
          Returns a map of all [key, value] pairs associated with the specified sessionID.
 int getExpireTime()
          Returns the current expire interval (seconds after which sessions can be considered eligible for removal).
 List getForUser(Connection con, int userID)
          Same as getForUser(Connection, String) but takes a numeric userID.
 List getForUser(Connection con, String userID)
          Returns a List containing session information about all sessions associated with the specified ID.
static JDBCSession getInstance()
          Returns an instance of JDBCSession.
static JDBCSession getInstance(Log logger)
          Returns an instance of JDBCSession.
static void main(String[] args)
           
 void put(Connection con, String sessionID, String key, String value)
          An alias for the #add method.
 void putAll(Connection con, String sessionID, Map data)
          An alias for the addAll method.
 JDBCSession.Info sessionInfo(Connection con, String sessionID)
          Returns session information from the session master table.
 void setDeleteExpiredSessions(boolean val)
          By default expired sessions are deleted from the db.
 void setExpireTime(int seconds)
          Sessions inactive for greater than these number of seconds will be eligible for expiry.
 void tieToUser(Connection con, String sessionID, String userID)
          Associates the specified sessionID with the specified userID.
 String update(Connection con, String sessionID, String key, 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
 

Field Detail

SESSIONDATA_TABLE

public static final String SESSIONDATA_TABLE
See Also:
Constant Field Values

SESSIONMASTER_TABLE

public static final String SESSIONMASTER_TABLE
See Also:
Constant Field Values
Method Detail

getInstance

public static JDBCSession getInstance()
Returns an instance of JDBCSession.


getInstance

public static JDBCSession getInstance(Log logger)
Returns an instance of JDBCSession.

Parameters:
logger - the logging destination for methods in this class. Specify null to use a default logger.

setDeleteExpiredSessions

public 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 void create(Connection con,
                   String sessionID,
                   String userID)
            throws 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:
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 void create(Connection con,
                   String sessionID)
            throws 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:
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 void expire(Connection con,
                   String sessionID)
            throws 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:
SQLException

tieToUser

public void tieToUser(Connection con,
                      String sessionID,
                      String userID)
               throws 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:
SQLException

expireInactiveSessions

public void expireInactiveSessions(Connection con)
                            throws 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:
SQLException

setExpireTime

public 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 int getExpireTime()
Returns the current expire interval (seconds after which sessions can be considered eligible for removal).


exists

public boolean exists(Connection con,
                      String sessionID)
               throws 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 non-expired validity. Sessions should be expired as/when needed by calling the expire method.

Throws:
SQLException

sessionInfo

public JDBCSession.Info sessionInfo(Connection con,
                                    String sessionID)
                             throws 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:
SQLException

getForUser

public List getForUser(Connection con,
                       String userID)
                throws 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:
SQLException

getForUser

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

Throws:
SQLException

getAll

public Map getAll(Connection con,
                  String sessionID)
           throws SQLException
Returns a map of all [key, value] pairs associated with the specified sessionID. Returns null if the specified sessionID is not found in the database or if the specified session has expired.

Throws:
SQLException

get

public String get(Connection con,
                  String sessionID,
                  String key)
           throws 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:
SQLException

delete

public void delete(Connection con,
                   String sessionID,
                   String key)
            throws 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:
SQLException

add

public void add(Connection con,
                String sessionID,
                String key,
                String value)
         throws 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:
SQLException

addAll

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

Throws:
SQLException

put

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

Throws:
SQLException

putAll

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

Throws:
SQLException

update

public String update(Connection con,
                     String sessionID,
                     String key,
                     String newvalue)
              throws 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:
SQLException

main

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

createJDBCTables

public static void createJDBCTables(Connection con,
                                    String userTableName,
                                    boolean mysql)
                             throws 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:
Exception