/*
* Atricore IDBus
*
* Copyright (c) 2009, Atricore Inc.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.atricore.idbus.idojos.dbsessionstore;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.atricore.idbus.kernel.main.store.session.AbstractSessionStore;
import org.atricore.idbus.kernel.main.session.exceptions.SSOSessionException;
import org.atricore.idbus.kernel.main.session.BaseSession;
import org.atricore.idbus.kernel.main.session.MutableBaseSession;
import java.sql.*;
import java.util.ArrayList;
import java.util.Date;
/**
* An abstraction of a SessionStore backed by a database.
* <p/>
* <p>
* Additional component properties include:
* <ul>
* <li>sizeQuery = The SQL Query used to add a new session to the store.</li>
* <li>keysQuery = The SQL Query used to retrieve all session ids. The first column for each row in the result set must be the session id.</li>
* <li>loadAllQuery = The SQL Query used to load all sessions from the store.</li>
* <li>loadQuery = The SQL Query used to load one session from the store based on its id.</li>
* <li>loadByUserNameQuery = The SQL Query used to load all sessions associated to a given user.</li>
* <li>loadByLastAccesstimeQuery = The SQL Query used to load all sessions last accessed before the given date.</li>
* <li>loadByValidQuery = The SQL Query used to load all sessions whose valid property is equals to the gvien argument.</li>
* <li>deleteDml = The SQL Query used to remove a session from the store.</li>
* <li>deletAllDml = The SQL Query used to remove ALL sessions from the store.</li>
* <li>insertDml = The SQL Query used to add a new session to the store.</li>
* </ul>
* </p>
* <p>
* The columns in the result set for all load methods must be in the following order :
* sessionId, userName, creationTime, lastAccessTime, accessCount, maxInactiveInterval, valid
* </p>
* <p>
* lastAccessTime and creationTime are treated as a longs, not dates.
* </p>
*
* TODO: Add support inserting and updating assertion instances
*
* @author Jeff Gutierrez (code@gutierrez.ph) ca
*/
public abstract class DbSessionStore extends AbstractSessionStore {
private static final Log __log = LogFactory.getLog(DbSessionStore.class);
private String _sizeQuery = null;
private String _keysQuery = null;
private String _loadAllQuery = null;
private String _loadQuery = null;
private String _loadByUserNameQuery = null;
private String _loadByLastAccessTimeQuery = null;
private String _loadByValidQuery = null;
private String _deleteDml = null;
private String _deleteAllDml = null;
private String _insertDml = null;
private String _updateDml = null;
// -------------------------------------
// DbSessinStore-specific
// -------------------------------------
/**
* Implementation classes implement this method.
*
* @return A database connection.
*/
protected abstract Connection getConnection() throws SQLException, SSOSessionException;
/**
* Close the given db connection.
*
* @param dbConnection
*/
protected void close(Connection dbConnection) throws SSOSessionException {
try {
if (dbConnection != null
&& !dbConnection.isClosed()) {
dbConnection.close();
}
}
catch (SQLException se) {
if (__log.isDebugEnabled())
__log.debug("Error while clossing connection");
throw new SSOSessionException("Error while clossing connection\n" + se.getMessage());
}
catch (Exception e) {
if (__log.isDebugEnabled())
__log.debug("Error while clossing connection");
throw new SSOSessionException("Error while clossing connection\n" + e.getMessage());
}
}
/**
* Close the given db connection.
*
* @param statement
*/
protected void close(Statement statement) throws SSOSessionException {
try {
if (statement != null) {
statement.close();
}
}
catch (SQLException se) {
if (__log.isDebugEnabled())
__log.debug("Error while clossing statement");
throw new SSOSessionException("Error while clossing statement\n" + se.getMessage());
}
catch (Exception e) {
if (__log.isDebugEnabled())
__log.debug("Error while clossing statement");
throw new SSOSessionException("Error while clossing statement\n" + e.getMessage());
}
}
// -------------------------------------------------
// Configuration properties
// -------------------------------------------------
/**
* The SQL Query used to add a new session to the store.
*
* @param query
*/
public void setInsertDml(String query) {
_insertDml = query;
}
public String getInsertDml() {
return _insertDml;
}
/**
* The SQL Query used to add a new session to the store.
*
* @param query
*/
public void setUpdateDml(String query) {
_updateDml = query;
}
public String getUpdateDml() {
return _updateDml;
}
/**
* The SQL Query used to remove ALL sessions from the store.
*/
public void setDeleteAllDml(String query) {
_deleteAllDml = query;
}
public String getDeleteAllDml() {
return _deleteAllDml;
}
/**
* The SQL Query used to remove a session from the store.
*/
public void setDeleteDml(String query) {
_deleteDml = query;
}
public String getDeleteDml() {
return _deleteDml;
}
/**
* The SQL Query used to load all sessions last accessed before the given date.
* <p/>
* The columns in the result set must be in the following order :
* sessionId, userName, creationTime, lastAccessTime, accessCount, maxInactiveInterval, valid
*/
public void setLoadByLastAccessTimeQuery(String query) {
_loadByLastAccessTimeQuery = query;
}
public String getLoadByLastAccessTimeQuery() {
return _loadByLastAccessTimeQuery;
}
/**
* The SQL Query used to load all sessions whose valid property is equals to the given valid.
* <p/>
* The columns in the result set must be in the following order :
* sessionId, userName, creationTime, lastAccessTime, accessCount, maxInactiveInterval, valid
*/
public void setLoadByValidQuery(String query) {
_loadByValidQuery = query;
}
public String getLoadByValidQuery() {
return _loadByValidQuery;
}
/**
* The SQL Query used to load all sessions associated to a given user.
* <p/>
* The columns in the result set must be in the following order :
* sessionId, userName, creationTime, lastAccessTime, accessCount, maxInactiveInterval, valid
*
* @param query
*/
public void setLoadByUserNameQuery(String query) {
_loadByUserNameQuery = query;
}
public String getLoadByUserNameQuery() {
return _loadByUserNameQuery;
}
/**
* The SQL query used to retrieve the number of sessions in the store.
* The first column of the first row in the result set must be the number of sessions.
*/
public void setSizeQuery(String query) {
_sizeQuery = query;
}
public String getSizeQuery() {
return _sizeQuery;
}
/**
* The SQL Query used to retrieve all session ids.
* The first column for each row in the result set must be the session id.
*/
public void setKeysQuery(String query) {
_keysQuery = query;
}
public String getKeysQuery() {
return _keysQuery;
}
/**
* The SQL Query used to load all sessions from the store.
* <p/>
* The columns in the result set must be in the following order :
* sessionId, userName, creationTime, lastAccessTime, accessCount, maxInactiveInterval, valid
*
* @param query
*/
public void setLoadAllQuery(String query) {
_loadAllQuery = query;
}
public String getLoadAllQuery() {
return _loadAllQuery;
}
/**
* The SQL Query used to load one session from the store based on its id.
* <p/>
* The columns in the result set must be in the following order :
* sessionId, userName, creationTime, lastAccessTime, accessCount, maxInactiveInterval, valid
* <p/>
* example : SELECT sessionId, userName, creationTime, lastAccessTime, accessCount, maxInactiveInterval, valid FROM JOSSO_SESSION WHERE sessionId = ?
*
* @param query
*/
public void setLoadQuery(String query) {
_loadQuery = query;
}
public String getLoadQuery() {
return _loadQuery;
}
// --------------------------------
// SessionStore implementation
// --------------------------------
/**
* This method returns the number of stored sessions.
* <p/>
* The first column of the first row in the result set must be the number of sessions.
*
* @return the number of sessions
* @throws SSOSessionException
* @see #setSizeQuery(String)
*/
public int getSize() throws SSOSessionException {
int retval = 0;
Connection conn = null;
Statement stmt = null;
// - Submit query
// - First column of the first row is the number of sessions.
//
try {
conn = getConnection();
stmt = conn.createStatement();
final ResultSet rs = stmt.executeQuery(_sizeQuery);
if (rs != null && rs.next()) {
retval = rs.getInt(1);
}
if ( rs != null )
rs.close();
}
catch (Exception e) {
if (__log.isDebugEnabled())
__log.debug(e, e);
throw new SSOSessionException(e);
}
finally {
close(stmt);
close(conn);
}
if (__log.isDebugEnabled())
__log.debug("Returning " + retval);
return retval;
}
/**
* Returns all session keys (ids)
* <p/>
* The first column for each row in the result set must be the session id.
*
* @return The session keys
* @throws SSOSessionException
* @see #setKeysQuery(String)
*/
public String[] keys() throws SSOSessionException {
String[] retval = null;
Connection conn = null;
Statement stmt = null;
// - Submit query
// - First column for each row is the session id.
//
try {
conn = getConnection();
stmt = conn.createStatement();
final ResultSet rs = stmt.executeQuery(_keysQuery);
final ArrayList bucket = new ArrayList();
while (rs.next())
bucket.add(rs.getString(1));
rs.close();
retval = new String[bucket.size()];
bucket.toArray(retval);
}
catch (Exception e) {
if (__log.isDebugEnabled())
__log.debug(e, e);
throw new SSOSessionException(e);
}
finally {
close(stmt);
close(conn);
}
return retval;
}
/**
* Loads all stored sessions.
* <p/>
* The columns in the result set must be in the following order :
* sessionId, userName, creationTime, lastAccessTime, accessCount, maxInactiveInterval, valid
*
* @see #setLoadAllQuery(String)
*/
public BaseSession[] loadAll() throws SSOSessionException {
BaseSession[] retval = null;
Connection conn = null;
Statement stmt = null;
// - Submit query
// - Expected columns, in order:
// sessionId, userName, creationTime, lastAccessTime, accessCount, maxInactiveInterval, valid
//
try {
conn = getConnection();
stmt = conn.createStatement();
final ResultSet rs = stmt.executeQuery(_loadAllQuery);
retval = getSessions(rs);
rs.close();
}
catch (Exception e) {
if (__log.isDebugEnabled())
__log.debug(e, e);
throw new SSOSessionException(e);
}
finally {
close(stmt);
close(conn);
}
return retval;
}
/**
* Loads a session based on its id.
* <p/>
* The columns in the result set must be in the following order :
* sessionId, userName, creationTime, lastAccessTime, accessCount, maxInactiveInterval, valid
*
* @param id
* @return
* @throws SSOSessionException
* @see #setLoadQuery(String)
*/
public BaseSession load(String id) throws SSOSessionException {
BaseSession retval = null;
Connection conn = null;
PreparedStatement stmt = null;
try {
conn = getConnection();
stmt = conn.prepareStatement(_loadQuery);
stmt.setString(1, id);
final ResultSet rs = stmt.executeQuery();
if (rs.next())
retval = createFromResultSet(rs);
rs.close();
}
catch (Exception e) {
if (__log.isDebugEnabled())
__log.debug(e, e);
throw new SSOSessionException(e);
}
finally {
close( stmt );
close(conn);
}
id = (retval == null)
? "NOT FOUND"
: retval.getId();
if (__log.isDebugEnabled())
__log.debug("Loaded session: " + id);
return retval;
}
/**
* Loads all sessions based on the associated username
* <p/>
* The columns in the result set must be in the following order :
* sessionId, userName, creationTime, lastAccessTime, accessCount, maxInactiveInterval, valid
*
* @see #setLoadByUserNameQuery(String)
*/
public BaseSession[] loadByUsername(String userName) throws SSOSessionException {
BaseSession[] retval = null;
Connection conn = null;
PreparedStatement stmt = null;
try {
conn = getConnection();
stmt = conn.prepareStatement(_loadByUserNameQuery);
stmt.setString(1, userName);
final ResultSet rs = stmt.executeQuery();
retval = getSessions(rs);
rs.close();
}
catch (Exception e) {
if (__log.isDebugEnabled())
__log.debug(e, e);
throw new SSOSessionException(e);
}
finally {
close(stmt);
close(conn);
}
return retval;
}
/**
* Loads all sessions last accessed before the given date.
* <p/>
* The date is converted to java.sql.Date when setting up the prepared statement.
* <p/>
* The columns in the result set must be in the following order :
* sessionId, userName, creationTime, lastAccessTime, accessCount, maxInactiveInterval, valid
*
* @see #setLoadByLastAccessTimeQuery(String)
*/
public BaseSession[] loadByLastAccessTime(Date date)
throws SSOSessionException {
BaseSession[] retval = null;
Connection conn = null;
PreparedStatement stmt = null;
try {
conn = getConnection();
stmt = conn.prepareStatement(_loadByLastAccessTimeQuery);
stmt.setLong(1, date.getTime());
final ResultSet rs = stmt.executeQuery();
retval = getSessions(rs);
rs.close();
}
catch (Exception e) {
if (__log.isDebugEnabled())
__log.debug(e, e);
throw new SSOSessionException(e);
}
finally {
close(stmt);
close(conn);
}
return retval;
}
/**
* Loads all sessions whose valid property is equals to the received argument.
*
* @see #setLoadByValidQuery(String)
*/
public BaseSession[] loadByValid(boolean valid)
throws SSOSessionException {
BaseSession[] retval = null;
Connection conn = null;
PreparedStatement stmt = null;
try {
conn = getConnection();
stmt = conn.prepareStatement(_loadByValidQuery);
stmt.setBoolean(1, valid);
final ResultSet rs = stmt.executeQuery();
retval = getSessions(rs);
rs.close();
}
catch (Exception e) {
if (__log.isDebugEnabled())
__log.debug(e, e);
throw new SSOSessionException(e);
}
finally {
close(stmt);
close(conn);
}
return retval;
}
/**
* Removes a session from the store based on its id
*
* @throws SSOSessionException
* @see #setDeleteDml(String)
*/
public void remove(String id) throws SSOSessionException {
Connection conn = null;
try {
conn = getConnection();
delete(conn, id);
conn.commit();
}
catch (Exception e) {
if (__log.isDebugEnabled())
__log.debug(e, e);
throw new SSOSessionException(e);
}
finally {
close(conn);
}
}
/**
* Removes ALL stored sessions
*
* @throws SSOSessionException
* @see #setDeleteAllDml(String)
*/
public void clear() throws SSOSessionException {
Connection conn = null;
Statement stmt = null;
try {
conn = getConnection();
stmt = conn.createStatement();
stmt.execute(_deleteAllDml);
conn.commit();
}
catch (Exception e) {
if (__log.isDebugEnabled())
__log.debug(e, e);
throw new SSOSessionException(e);
}
finally {
close(stmt);
close(conn);
}
}
/**
* Stores a session in the DB. This method opens a transaccion, removes the old session if present, the creates
* it again using the configured savenDml Query and commits the transaction.
* <p/>
* Session attributes will be passed to the prepared statemetn in the following order :
* sessionId, userName, creationTime, lastAccessTime, accessCount, maxInactiveInterval, valid
*
* @param session
* @throws SSOSessionException
* @see #setInsertDml(String)
*/
public void save(BaseSession session) throws SSOSessionException {
Connection conn = null;
// - Expected columns, in order:
// sessionId, userName, creationTime, lastAccessTime, accessCount, maxInactiveInterval, valid
try {
conn = getConnection();
if (load(session.getId()) != null) {
update(conn, session);
} else {
insert(conn, session);
}
conn.commit();
if (__log.isDebugEnabled())
__log.debug("Session committed: " + session.getId());
}
catch (Exception e) {
if (__log.isDebugEnabled())
__log.debug(e, e);
if (conn != null) {
try {
conn.rollback();
} catch (SQLException sqle) {
if (__log.isDebugEnabled())
__log.debug("Error during ROLLBACK ", sqle);
}
}
throw new SSOSessionException(e);
}
finally {
close(conn);
}
if (__log.isDebugEnabled())
__log.debug("Saved session: " + session.getId());
}
// ---------------------------
// Private Methods
// ---------------------------
/**
* This removes a session, using the value of the removeDml property as prepared statement.
*
* @param conn
* @param sessionId
* @throws SQLException
*/
protected void delete(Connection conn, String sessionId) throws SQLException {
final PreparedStatement ps = conn.prepareStatement(_deleteDml);
ps.setString(1, sessionId);
ps.execute();
if (__log.isDebugEnabled())
__log.debug("Session Removed: " + sessionId);
}
protected void insert(Connection conn, BaseSession session) throws SQLException {
final PreparedStatement ps = conn.prepareStatement(_insertDml);
ps.setString(1, session.getId());
ps.setString(2, session.getUsername());
ps.setLong(3, session.getCreationTime());
ps.setLong(4, session.getLastAccessTime());
ps.setInt(5, (int) session.getAccessCount());
ps.setInt(6, session.getMaxInactiveInterval());
ps.setBoolean(7, session.isValid());
ps.execute();
if (__log.isDebugEnabled())
__log.debug("Creation, LastAccess: " + session.getCreationTime() + ", " + session.getCreationTime());
if (__log.isDebugEnabled())
__log.debug("Session inserted: " + session.getId());
}
protected void update(Connection conn, BaseSession session) throws SQLException {
final PreparedStatement ps = conn.prepareStatement(_updateDml);
ps.setString(1, session.getUsername());
ps.setLong(2, session.getCreationTime());
ps.setLong(3, session.getLastAccessTime());
ps.setInt(4, (int) session.getAccessCount());
ps.setInt(5, session.getMaxInactiveInterval());
ps.setBoolean(6, session.isValid());
ps.setString(7, session.getId());
ps.execute();
if (__log.isDebugEnabled())
__log.debug("Creation, LastAccess: " + session.getCreationTime() + ", " + session.getCreationTime());
if (__log.isDebugEnabled())
__log.debug("Session updated: " + session.getId());
}
/**
*
*/
protected BaseSession[] getSessions(ResultSet rs) throws SQLException {
final ArrayList bucket = new ArrayList();
// - Collect the sessions
// - Return the sessions in an array.
//
while (rs.next())
bucket.add(createFromResultSet(rs));
final BaseSession[] retval = new BaseSession[bucket.size()];
bucket.toArray(retval);
return retval;
}
/**
* This method builds a session instance based on a result set.
* <p/>
* Expected columns, in order:
* sessionId, userName, creationTime, lastAccessTime, accessCount, maxInactiveInterval, valid
*
* @param rs
* @return
* @throws SQLException
*/
protected BaseSession createFromResultSet(ResultSet rs) throws SQLException {
// - Expected columns, in order:
// sessionId, userName, creationTime, lastAccessTime, accessCount, maxInactiveInterval, valid
final MutableBaseSession bsi = new MutableBaseSession();
bsi.setId(rs.getString(1));
bsi.setUsername(rs.getString(2));
bsi.setCreationTime(rs.getLong(3));
bsi.setLastAccessedTime(rs.getLong(4));
bsi.setAccessCount(rs.getLong(5));
bsi.setMaxInactiveInterval(rs.getInt(6));
bsi.setValid(rs.getBoolean(7));
return bsi;
}
}