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 [EMAIL PROTECTED]
   *
   * 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;
        }
      }
  }
  
  
  

Reply via email to