You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by cr...@locus.apache.org on 2000/01/31 06:29:04 UTC
cvs commit: jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/session StandardManager.java
craigmcc 00/01/30 21:29:04
Modified: proposals/catalina/src/share/org/apache/tomcat/session
StandardManager.java
Log:
Add support for optional session persistence (to a configurable disk file
pathname) across server restarts. This can also be used to provide
persistence across auto-reloads of a web application when that is
supported.
Revision Changes Path
1.4 +205 -7 jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/session/StandardManager.java
Index: StandardManager.java
===================================================================
RCS file: /home/cvs/jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/session/StandardManager.java,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- StandardManager.java 2000/01/29 07:46:35 1.3
+++ StandardManager.java 2000/01/31 05:29:04 1.4
@@ -1,7 +1,7 @@
/*
- * $Header: /home/cvs/jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/session/StandardManager.java,v 1.3 2000/01/29 07:46:35 craigmcc Exp $
- * $Revision: 1.3 $
- * $Date: 2000/01/29 07:46:35 $
+ * $Header: /home/cvs/jakarta-tomcat/proposals/catalina/src/share/org/apache/tomcat/session/StandardManager.java,v 1.4 2000/01/31 05:29:04 craigmcc Exp $
+ * $Revision: 1.4 $
+ * $Date: 2000/01/31 05:29:04 $
*
* ====================================================================
*
@@ -64,7 +64,14 @@
package org.apache.tomcat.session;
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
@@ -80,11 +87,16 @@
/**
* Standard implementation of the <b>Manager</b> interface that provides
- * no session persistence or distributable capabilities, but does support
- * an optional, configurable, maximum number of active sessions allowed.
+ * simple session persistence across restarts of this component (such as
+ * when the entire server is shut down and restarted, or when a particular
+ * web application is reloaded.
+ * <p>
+ * <b>IMPLEMENTATION NOTE</b>: Correct behavior of session storing and
+ * reloading depends upon external calls to the <code>start()</code> and
+ * <code>stop()</code> methods of this class at the correct times.
*
* @author Craig R. McClanahan
- * @version $Revision: 1.3 $ $Date: 2000/01/29 07:46:35 $
+ * @version $Revision: 1.4 $ $Date: 2000/01/31 05:29:04 $
*/
public final class StandardManager
@@ -110,10 +122,18 @@
/**
* The maximum number of active Sessions allowed, or -1 for no limit.
*/
- protected int maxActiveSessions = -1;
+ private int maxActiveSessions = -1;
/**
+ * Path name of the disk file in which active sessions are saved
+ * when we stop, and from which these sessions are loaded when we start.
+ * A <code>null</code> value indicates that no persistence is desired.
+ */
+ private String pathname = null;
+
+
+ /**
* The string manager for this package.
*/
private StringManager sm =
@@ -213,6 +233,31 @@
}
+ /**
+ * Return the session persistence pathname, if any.
+ */
+ public String getPathname() {
+
+ return (this.pathname);
+
+ }
+
+
+ /**
+ * Set the session persistence pathname to the specified value. If no
+ * persistence support is desired, set the pathname to <code>null</code>.
+ *
+ * @param pathname New session persistence pathname
+ */
+ public void setPathname(String pathname) {
+
+ String oldPathname = this.pathname;
+ this.pathname = pathname;
+ support.firePropertyChange("pathname", oldPathname, this.pathname);
+
+ }
+
+
// --------------------------------------------------------- Public Methods
@@ -259,6 +304,9 @@
(sm.getString("standardManager.alreadyStarted"));
started = true;
+ // Load any previously persisted sessions
+ load();
+
// Start the background reaper thread
threadStart();
@@ -285,6 +333,9 @@
// Stop the background reaper thread
threadStop();
+ // Save any currently active sessions
+ unload();
+
// Expire all active sessions
Session sessions[] = findSessions();
for (int i = 0; i < sessions.length; i++) {
@@ -301,6 +352,83 @@
/**
+ * Load any currently active sessions that were previously unloaded
+ * to the specified persistence file, if any.
+ *
+ * @exception LifecycleException if a fatal error is encountered
+ */
+ private void load() throws LifecycleException {
+
+ // Initialize our internal data structures
+ recycled.removeAllElements();
+ sessions.clear();
+
+ // Open an input stream to the specified pathname, if any
+ if (pathname == null)
+ return;
+ FileInputStream fis = null;
+ ObjectInputStream ois = null;
+ try {
+ fis = new FileInputStream(pathname);
+ ois = new ObjectInputStream(new BufferedInputStream(fis));
+ } catch (FileNotFoundException e) {
+ return;
+ } catch (IOException e) {
+ if (ois != null) {
+ try {
+ ois.close();
+ } catch (IOException f) {
+ ;
+ }
+ ois = null;
+ }
+ throw new LifecycleException("load/open: IOException", e);
+ }
+
+ // Load the previously unloaded active sessions
+ synchronized (sessions) {
+ try {
+ Integer count = (Integer) ois.readObject();
+ int n = count.intValue();
+ for (int i = 0; i < n; i++) {
+ Session session = (Session) ois.readObject();
+ sessions.put(session.getId(), session);
+ }
+ } catch (ClassNotFoundException e) {
+ if (ois != null) {
+ try {
+ ois.close();
+ } catch (IOException f) {
+ ;
+ }
+ ois = null;
+ }
+ throw new LifecycleException
+ ("load/read: ClassNotFoundException", e);
+ } catch (IOException e) {
+ if (ois != null) {
+ try {
+ ois.close();
+ } catch (IOException f) {
+ ;
+ }
+ ois = null;
+ }
+ throw new LifecycleException("load/read: IOException", e);
+ }
+ }
+
+ // Close the input stream
+ try {
+ ois.close();
+ } catch (IOException f) {
+ ;
+ }
+
+ }
+
+
+ /**
* Invalidate all sessions that have expired.
*/
private void processExpires() {
@@ -320,6 +448,76 @@
if (timeIdle >= maxInactiveInterval)
session.expire();
}
+ }
+
+
+ /**
+ * Save any currently active sessions in the specified persistence file,
+ * if any.
+ *
+ * @exception LifecycleException if a fatal error is encountered
+ */
+ private void unload() throws LifecycleException {
+
+ // Open an output stream to the specified pathname, if any
+ if (pathname == null)
+ return;
+ FileOutputStream fos = null;
+ ObjectOutputStream oos = null;
+ try {
+ fos = new FileOutputStream(pathname);
+ oos = new ObjectOutputStream(new BufferedOutputStream(fos));
+ } catch (IOException e) {
+ if (oos != null) {
+ try {
+ oos.close();
+ } catch (IOException f) {
+ ;
+ }
+ oos = null;
+ }
+ throw new LifecycleException("unload/open: IOException", e);
+ }
+
+ // Write the number of active sessions, followed by the details
+ synchronized (sessions) {
+ try {
+ oos.writeObject(new Integer(sessions.size()));
+ Enumeration elements = sessions.elements();
+ while (elements.hasMoreElements()) {
+ Session session = (Session) elements.nextElement();
+ oos.writeObject(session);
+ }
+ } catch (IOException e) {
+ if (oos != null) {
+ try {
+ oos.close();
+ } catch (IOException f) {
+ ;
+ }
+ oos = null;
+ }
+ throw new LifecycleException("unload/write: IOException", e);
+ }
+ }
+
+ // Flush and close the output stream
+ try {
+ oos.flush();
+ oos.close();
+ oos = null;
+ } catch (IOException e) {
+ if (oos != null) {
+ try {
+ oos.close();
+ } catch (IOException f) {
+ ;
+ }
+ oos = null;
+ }
+ throw new LifecycleException("unload/close: IOException", e);
+ }
+
}