You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by bi...@apache.org on 2001/04/26 03:38:01 UTC
cvs commit: jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/session JDBCStore.java
bip 01/04/25 18:38:01
Added: catalina/src/share/org/apache/catalina/session
JDBCStore.java
Log:
A concrete Store implementation that uses a JDBC compatible RDBMS to
store Sessions.
Revision Changes Path
1.1 jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/session/JDBCStore.java
Index: JDBCStore.java
===================================================================
/*
* JDBCStore.java
* $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/session/JDBCStore.java,v 1.1 2001/04/26 01:37:59 bip Exp $
* $Revision: 1.1 $
* $Date: 2001/04/26 01:37:59 $
*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* [Additional notices, if required by prior licensing conditions]
*
*/
package org.apache.catalina.session;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamClass;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.apache.catalina.Container;
import org.apache.catalina.Loader;
import org.apache.catalina.Session;
import org.apache.catalina.Store;
import org.apache.catalina.util.CustomObjectInputStream;
/**
* Implementation of the <code>Store</code> interface that stores
* serialized session objects in a database. Sessions that are
* saved are still subject to being expired based on inactivity.
*
* @author Bip Thelin
* @version $Revision: 1.1 $, $Date: 2001/04/26 01:37:59 $
*/
public class JDBCStore
extends StoreBase implements Store {
/**
* The descriptive information about this implementation.
*/
protected static String info = "JDBCStore/1.0";
/**
* Name to register for this Store, used for logging.
*/
protected static String storeName = "JDBCStore";
/**
* Name to register for the background thread.
*/
protected String threadName = "JDBCStore";
/**
* Connection string to use when connecting to the DB.
*/
protected String connString = null;
/**
* The database connection.
*/
private Connection conn = null;
/**
* Driver to use.
*/
protected String driverName = null;
// ------------------------------------------------------------- Table & cols
/**
* Table to use.
*/
protected String sessionTable = "tomcat$sessions";
/**
* Id column to use.
*/
protected String sessionIdCol = "id";
/**
* Data column to use.
*/
protected String sessionDataCol = "data";
/**
* Is Valid column to use.
*/
protected String sessionValidCol = "valid";
/**
* Max Inactive column to use.
*/
protected String sessionMaxInactiveCol = "maxinactive";
/**
* Last Accessed column to use.
*/
protected String sessionLastAccessedCol = "lastaccess";
// ------------------------------------------------------------- SQL Variables
/**
* Variable to hold the <code>getSize()</code> prepared statement.
*/
protected PreparedStatement preparedSizeSql = null;
/**
* Variable to hold the <code>keys()</code> prepared statement.
*/
protected PreparedStatement preparedKeysSql = null;
/**
* Variable to hold the <code>save()</code> prepared statement.
*/
protected PreparedStatement preparedSaveSql = null;
/**
* Variable to hold the <code>clear()</code> prepared statement.
*/
protected PreparedStatement preparedClearSql = null;
/**
* Variable to hold the <code>remove()</code> prepared statement.
*/
protected PreparedStatement preparedRemoveSql = null;
/**
* Variable to hold the <code>load()</code> prepared statement.
*/
protected PreparedStatement preparedLoadSql = null;
// ------------------------------------------------------------- Properties
/**
* Return the info for this Store.
*/
public String getInfo() {
return(info);
}
/**
* Return the thread name for this Store.
*/
public String getThreadName() {
return(threadName);
}
/**
* Return the name for this Store, used for logging.
*/
public String getStoreName() {
return(storeName);
}
/**
* Set the driver for this Store.
*
* @param driverName The new driver
*/
public void setDriverName(String driverName) {
String oldDriverName = this.driverName;
this.driverName = driverName;
support.firePropertyChange("driverName",
oldDriverName,
this.driverName);
this.driverName = driverName;
}
/**
* Return the driver for this Store.
*/
public String getDriverName() {
return(this.driverName);
}
/**
* Set the Connection URL for this Store.
*
* @param connectionURL The new Connection URL
*/
public void setConnectionURL(String connectionURL) {
String oldConnString = this.connString;
this.connString = connectionURL;
support.firePropertyChange("connString",
oldConnString,
this.connString);
}
/**
* Return the Connection URL for this Store.
*/
public String getConnectionURL() {
return(this.connString);
}
/**
* Set the table for this Store.
*
* @param sessionTable The new table
*/
public void setSessionTable(String sessionTable) {
String oldSessionTable = this.sessionTable;
this.sessionTable = sessionTable;
support.firePropertyChange("sessionTable",
oldSessionTable,
this.sessionTable);
}
/**
* Return the table for this Store.
*/
public String getSessionTable() {
return(this.sessionTable);
}
/**
* Set the Id column for the table.
*
* @param sessionIdCol the column name
*/
public void setSessionIdCol(String sessionIdCol) {
String oldSessionIdCol = this.sessionIdCol;
this.sessionIdCol = sessionIdCol;
support.firePropertyChange("sessionIdCol",
oldSessionIdCol,
this.sessionIdCol);
}
/**
* Return the Id column for the table.
*/
public String getSessionIdCol() {
return(this.sessionIdCol);
}
/**
* Set the Data column for the table
*
* @param sessionDataCol the column name
*/
public void setSessionDataCol(String sessionDataCol) {
String oldSessionDataCol = this.sessionDataCol;
this.sessionDataCol = sessionDataCol;
support.firePropertyChange("sessionDataCol",
oldSessionDataCol,
this.sessionDataCol);
}
/**
* Return the data column for the table
*/
public String getSessionDataCol() {
return(this.sessionDataCol);
}
/**
* Set the Is Valid column for the table
*
* @param sessionValidCol The column name
*/
public void setSessionValidCol(String sessionValidCol) {
String oldSessionValidCol = this.sessionValidCol;
this.sessionValidCol = sessionValidCol;
support.firePropertyChange("sessionValidCol",
oldSessionValidCol,
this.sessionValidCol);
}
/**
* Return the Is Valid column
*/
public String getSessionValidCol() {
return(this.sessionValidCol);
}
/**
* Set the Max Inactive column for the table
*
* @param sessionMaxInactiveCol The column name
*/
public void setSessionMaxInactiveCol(String sessionMaxInactiveCol) {
String oldSessionMaxInactiveCol = this.sessionMaxInactiveCol;
this.sessionMaxInactiveCol = sessionMaxInactiveCol;
support.firePropertyChange("sessionMaxInactiveCol",
oldSessionMaxInactiveCol,
this.sessionMaxInactiveCol);
}
/**
* Return the Max Inactive column
*/
public String getSessionMaxInactiveCol() {
return(this.sessionMaxInactiveCol);
}
/**
* Set the Last Accessed column for the table
*
* @param sessionLastAccessedCol The column name
*/
public void setSessionLastAccessedCol(String sessionLastAccessedCol) {
String oldSessionLastAccessedCol = this.sessionLastAccessedCol;
this.sessionLastAccessedCol = sessionLastAccessedCol;
support.firePropertyChange("sessionLastAccessedCol",
oldSessionLastAccessedCol,
this.sessionLastAccessedCol);
}
/**
* Return the Last Accessed column
*/
public String getSessionLastAccessedCol() {
return(this.sessionLastAccessedCol);
}
// --------------------------------------------------------- Public Methods
/**
* Return an array containing the session identifiers of all Sessions
* currently saved in this Store. If there are no such Sessions, a
* zero-length array is returned.
*
* @exception IOException if an input/output error occurred
*/
public String[] keys() throws IOException {
String keysSql =
"SELECT c.size, s."+sessionIdCol+
" FROM "+sessionTable+" s, "+
"(SELECT COUNT("+sessionIdCol+
") AS size FROM "+sessionTable+") c";
Connection _conn = getConnection();
ResultSet rst = null;
String keys[] = null;
int i;
if(_conn == null)
return(new String[0]);
try {
if(preparedKeysSql == null)
preparedKeysSql = _conn.prepareStatement(keysSql);
rst = preparedKeysSql.executeQuery();
if (rst != null && rst.next()) {
keys = new String[rst.getInt(1)];
keys[0] = rst.getString(2);
i=1;
while(rst.next())
keys[i++] = rst.getString(2);
} else {
keys = new String[0];
}
} catch(SQLException e) {
log(sm.getString(getStoreName()+".SQLException", e));
} finally {
try {
if(rst != null)
rst.close();
} catch(SQLException e) {
;
}
release(_conn);
_conn = null;
}
return(keys);
}
/**
* Return an integer containing a count of all Sessions
* currently saved in this Store. If there are no Sessions,
* <code>0</code> is returned.
*
* @exception IOException if an input/output error occurred
*/
public int getSize() throws IOException {
int size = 0;
String sizeSql = "SELECT COUNT("+sessionIdCol+
") FROM ".concat(sessionTable);
Connection _conn = getConnection();
ResultSet rst = null;
if(_conn == null)
return(size);
try {
if(preparedSizeSql == null)
preparedSizeSql = _conn.prepareStatement(sizeSql);
rst = preparedSizeSql.executeQuery();
if (rst.next())
size = rst.getInt(1);
} catch(SQLException e) {
log(sm.getString(getStoreName()+".SQLException", e));
} finally {
try {
if(rst != null)
rst.close();
} catch(SQLException e) {
;
}
release(_conn);
_conn = null;
}
return(size);
}
/**
* Load the Session associated with the id <code>id</code>.
* If no such session is found <code>null</code> is returned.
*
* @param id a value of type <code>String</code>
* @return the stored <code>Session</code>
* @exception ClassNotFoundException if an error occurs
* @exception IOException if an input/output error occurred
*/
public Session load(String id)
throws ClassNotFoundException, IOException {
ResultSet rst = null;
Connection _conn = getConnection();
StandardSession _session = null;
Loader loader = null;
ClassLoader classLoader = null;
ObjectInputStream ois = null;
BufferedInputStream bis = null;
Container container = manager.getContainer();
String loadSql = "SELECT "+sessionIdCol+
", "+sessionDataCol+" FROM "+sessionTable+
" WHERE "+sessionIdCol+" = ?";
if(_conn == null)
return(null);
try {
if(preparedLoadSql == null)
preparedLoadSql = _conn.prepareStatement(loadSql);
preparedLoadSql.setString(1, id);
rst = preparedLoadSql.executeQuery();
if (rst.next()) {
bis = new BufferedInputStream(rst.getBinaryStream(2));
if (container != null)
loader = container.getLoader();
if (loader != null)
classLoader = loader.getClassLoader();
if (classLoader != null)
ois = new CustomObjectInputStream(bis,
classLoader);
else
ois = new ObjectInputStream(bis);
} else if (debug > 0) {
log(getStoreName()+": No persisted data object found");
}
} catch(SQLException e) {
log(sm.getString(getStoreName()+".SQLException", e));
} finally {
try {
if(rst != null)
rst.close();
} catch(SQLException e) {
;
}
release(_conn);
_conn = null;
}
try {
_session = (StandardSession) manager.createSession();
_session.readObjectData(ois);
_session.setManager(manager);
} finally {
if (ois != null) {
try {
ois.close();
bis = null;
} catch (IOException e) {
;
}
}
}
if (debug > 0)
log(sm.getString(getStoreName()+".loading",
id, sessionTable));
return(_session);
}
/**
* Remove the Session with the specified session identifier from
* this Store, if present. If no such Session is present, this method
* takes no action.
*
* @param id Session identifier of the Session to be removed
*
* @exception IOException if an input/output error occurs
*/
public void remove(String id) throws IOException {
Connection _conn = getConnection();
String removeSql = "DELETE FROM "+sessionTable+" WHERE "+
sessionIdCol+" = ?";
if(_conn == null)
return;
try {
if(preparedRemoveSql == null)
preparedRemoveSql = _conn.prepareStatement(removeSql);
preparedRemoveSql.setString(1, id);
preparedRemoveSql.execute();
} catch(SQLException e) {
log(sm.getString(getStoreName()+".SQLException", e));
} finally {
release(_conn);
_conn = null;
}
if (debug > 0)
log(sm.getString(getStoreName()+".removing", id, sessionTable));
}
/**
* Remove all of the Sessions in this Store.
*
* @exception IOException if an input/output error occurs
*/
public void clear() throws IOException {
Connection _conn = getConnection();
String clearSql = "DELETE FROM ".concat(sessionTable);
if(_conn == null)
return;
try {
if(preparedClearSql == null)
preparedClearSql = _conn.prepareStatement(clearSql);
preparedClearSql.execute();
} catch(SQLException e) {
log(sm.getString(getStoreName()+".SQLException", e));
} finally {
release(_conn);
_conn = null;
}
}
/**
* Save a session to the Store.
*
* @param session the session to be stored
* @exception IOException if an input/output error occurs
*/
public void save(Session session) throws IOException {
String saveSql = "INSERT INTO "+sessionTable+" ("+
sessionIdCol+", "+
sessionDataCol+", "+
sessionValidCol+", "+
sessionMaxInactiveCol+", "+
sessionLastAccessedCol+") VALUES (?, ?, ?, ?, ?)";
Connection _conn = getConnection();
ObjectOutputStream oos = null;
ByteArrayOutputStream bos = null;
ByteArrayInputStream bis = null;
InputStream in = null;
if(_conn == null)
return;
// If sessions already exist in DB, remove and insert again.
// TODO:
// * Check if ID exists in database and if so use UPDATE.
remove(session.getId());
try {
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(new BufferedOutputStream(bos));
((StandardSession)session).writeObjectData(oos);
oos.close();
byte[] obs = bos.toByteArray();
int size = obs.length;
bis = new ByteArrayInputStream(obs, 0, size);
in = new BufferedInputStream(bis, size);
if(preparedSaveSql == null)
preparedSaveSql = _conn.prepareStatement(saveSql);
preparedSaveSql.setString(1, session.getId());
preparedSaveSql.setBinaryStream(2, in, size);
preparedSaveSql.setString(3, session.isValid()?"1":"0");
preparedSaveSql.setInt(4, session.getMaxInactiveInterval());
preparedSaveSql.setLong(5, session.getLastAccessedTime());
preparedSaveSql.execute();
} catch(SQLException e) {
log(sm.getString(getStoreName()+".SQLException", e));
} catch (IOException e) {
;
} finally {
if(bis != null)
bis.close();
if(in != null)
in.close();
bis = null;
bos = null;
oos = null;
in = null;
release(_conn);
_conn = null;
}
if (debug > 0)
log(sm.getString(getStoreName()+".saving",
session.getId(), sessionTable));
}
// --------------------------------------------------------- Protected Methods
/**
* Check the connection associated with this store, if it's
* <code>null</code> or closed try to reopen it.
* Returns <code>null</code> if the connection could not be established.
*
* @return <code>Connection</code> if the connection suceeded
*/
protected Connection getConnection(){
try {
if(conn == null || conn.isClosed()) {
Class.forName(driverName);
log(sm.getString(getStoreName()+".checkConnectionDBClosed"));
conn = DriverManager.getConnection(connString);
conn.setAutoCommit(true);
if(conn == null || conn.isClosed())
log(sm.getString(getStoreName()+".checkConnectionDBReOpenFail"));
}
} catch (SQLException ex){
log(sm.getString(getStoreName()+".checkConnectionSQLException",
ex.toString()));
} catch (ClassNotFoundException ex) {
log(sm.getString(getStoreName()+".checkConnectionClassNotFoundException",
ex.toString()));
}
return conn;
}
/**
* Release the connection, not needed here since the
* connection is not associated with a connection pool.
*
* @param conn The connection to be released
*/
protected void release(Connection conn) {
;
}
/**
* Called once when this Store is first started.
*/
public void storeStart() {
// Open connection to the database
this.conn = getConnection();
}
/**
* Gracefully terminate everything associated with our db.
* Called once when this Store is stoping.
*
*/
protected void storeStop() {
// Close and release everything associated with our db.
if(conn != null) {
try {
conn.commit();
} catch (SQLException e) {
;
}
try {
preparedSizeSql.close();
} catch (SQLException e) {
;
}
try {
preparedKeysSql.close();
} catch (SQLException e) {
;
}
try {
preparedSaveSql.close();
} catch (SQLException e) {
;
}
try {
preparedClearSql.close();
} catch (SQLException e) {
;
}
try {
preparedRemoveSql.close();
} catch (SQLException e) {
;
}
try {
preparedLoadSql.close();
} catch (SQLException e) {
;
}
try {
conn.close();
} catch (SQLException e) {
;
}
this.preparedSizeSql = null;
this.preparedKeysSql = null;
this.preparedSaveSql = null;
this.preparedClearSql = null;
this.preparedRemoveSql = null;
this.preparedLoadSql = null;
this.conn = null;
}
}
}