Return-Path: Delivered-To: apmail-jakarta-tomcat-dev-archive@www.apache.org Received: (qmail 1532 invoked from network); 14 Nov 2003 20:08:10 -0000 Received: from daedalus.apache.org (HELO mail.apache.org) (208.185.179.12) by minotaur-2.apache.org with SMTP; 14 Nov 2003 20:08:10 -0000 Received: (qmail 83564 invoked by uid 500); 14 Nov 2003 20:07:44 -0000 Delivered-To: apmail-jakarta-tomcat-dev-archive@jakarta.apache.org Received: (qmail 83527 invoked by uid 500); 14 Nov 2003 20:07:43 -0000 Mailing-List: contact tomcat-dev-help@jakarta.apache.org; run by ezmlm Precedence: bulk List-Unsubscribe: List-Subscribe: List-Help: List-Post: List-Id: "Tomcat Developers List" Reply-To: "Tomcat Developers List" Delivered-To: mailing list tomcat-dev@jakarta.apache.org Received: (qmail 83514 invoked by uid 500); 14 Nov 2003 20:07:43 -0000 Received: (qmail 83510 invoked from network); 14 Nov 2003 20:07:43 -0000 Received: from unknown (HELO minotaur.apache.org) (209.237.227.194) by daedalus.apache.org with SMTP; 14 Nov 2003 20:07:43 -0000 Received: (qmail 1469 invoked by uid 1602); 14 Nov 2003 20:07:52 -0000 Date: 14 Nov 2003 20:07:52 -0000 Message-ID: <20031114200752.1468.qmail@minotaur.apache.org> From: fhanik@apache.org To: jakarta-tomcat-catalina-cvs@apache.org Subject: cvs commit: jakarta-tomcat-catalina/modules/cluster/src/share/org/apache/catalina/cluster/session DeltaManager.java DeltaSession.java DeltaSessionFacade.java X-Spam-Rating: daedalus.apache.org 1.6.2 0/1000/N X-Spam-Rating: minotaur-2.apache.org 1.6.2 0/1000/N fhanik 2003/11/14 12:07:52 Added: modules/cluster/src/share/org/apache/catalina/cluster/session DeltaManager.java DeltaSession.java DeltaSessionFacade.java Log: I'm gonna implement a Delta Manager that only replicates deltas, this will be a configurable option I using the StandardManager code and not extending it, to have more control of what actually goes on when different things happen. Similar way the PersistentManager did the work. This is not finished, so don't try to use yet :) Revision Changes Path 1.1 jakarta-tomcat-catalina/modules/cluster/src/share/org/apache/catalina/cluster/session/DeltaManager.java Index: DeltaManager.java =================================================================== /* * $Header: /home/cvs/jakarta-tomcat-catalina/modules/cluster/src/share/org/apache/catalina/cluster/session/DeltaManager.java,v 1.1 2003/11/14 20:07:52 fhanik Exp $ * $Revision: 1.1 $ * $Date: 2003/11/14 20:07:52 $ * * ==================================================================== * * 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 * . * * [Additional notices, if required by prior licensing conditions] * */ package org.apache.catalina.cluster.session; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; 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.security.AccessController; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import java.util.ArrayList; import java.util.Iterator; import javax.servlet.ServletContext; import org.apache.catalina.Container; import org.apache.catalina.Context; import org.apache.catalina.Globals; import org.apache.catalina.Lifecycle; import org.apache.catalina.LifecycleException; import org.apache.catalina.LifecycleListener; import org.apache.catalina.Loader; import org.apache.catalina.Session; import org.apache.catalina.util.CustomObjectInputStream; import org.apache.catalina.util.LifecycleSupport; import org.apache.catalina.session.ManagerBase; /** * The DeltaManager manages replicated sessions by only * replicating the deltas in data. For applications written * to handle this, the DeltaManager is the optimal way of replicating data. * * This code is almost identical to StandardManager with a difference in * how it persists sessions and some modifications to it. * * IMPLEMENTATION NOTE: Correct behavior of session storing and * reloading depends upon external calls to the start() and * stop() methods of this class at the correct times. * * @author Filip Hanik * @author Craig R. McClanahan * @author Jean-Francois Arcand * @version $Revision: 1.1 $ $Date: 2003/11/14 20:07:52 $ */ public class DeltaManager extends ManagerBase implements Lifecycle, PropertyChangeListener { // ---------------------------------------------------- Security Classes private class PrivilegedDoLoad implements PrivilegedExceptionAction { PrivilegedDoLoad() { } public Object run() throws Exception{ doLoad(); return null; } } private class PrivilegedDoUnload implements PrivilegedExceptionAction { PrivilegedDoUnload() { } public Object run() throws Exception{ doUnload(); return null; } } // ----------------------------------------------------- Instance Variables /** * The descriptive information about this implementation. */ private static final String info = "StandardManager/1.0"; /** * The lifecycle event support for this component. */ protected LifecycleSupport lifecycle = new LifecycleSupport(this); /** * The maximum number of active Sessions allowed, or -1 for no limit. */ private int maxActiveSessions = -1; /** * The descriptive name of this Manager implementation (for logging). */ protected static String name = "StandardManager"; /** * 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 null value indicates that no persistence is desired. * If this pathname is relative, it will be resolved against the * temporary working directory provided by our context, available via * the javax.servlet.context.tempdir context attribute. */ private String pathname = "SESSIONS.ser"; /** * Has this component been started yet? */ private boolean started = false; int rejectedSessions=0; int expiredSessions=0; long processingTime=0; // ------------------------------------------------------------- Properties /** * Set the Container with which this Manager has been associated. If * it is a Context (the usual case), listen for changes to the session * timeout property. * * @param container The associated Container */ public void setContainer(Container container) { // De-register from the old Container (if any) if ((this.container != null) && (this.container instanceof Context)) ((Context) this.container).removePropertyChangeListener(this); // Default processing provided by our superclass super.setContainer(container); // Register with the new Container (if any) if ((this.container != null) && (this.container instanceof Context)) { setMaxInactiveInterval ( ((Context) this.container).getSessionTimeout()*60 ); ((Context) this.container).addPropertyChangeListener(this); } } /** * Return descriptive information about this Manager implementation and * the corresponding version number, in the format * <description>/<version>. */ public String getInfo() { return (this.info); } /** * Return the maximum number of active Sessions allowed, or -1 for * no limit. */ public int getMaxActiveSessions() { return (this.maxActiveSessions); } /** Number of session creations that failed due to maxActiveSessions * * @return */ public int getRejectedSessions() { return rejectedSessions; } public void setRejectedSessions(int rejectedSessions) { this.rejectedSessions = rejectedSessions; } /** Number of sessions that expired. * * @return */ public int getExpiredSessions() { return expiredSessions; } public void setExpiredSessions(int expiredSessions) { this.expiredSessions = expiredSessions; } public long getProcessingTime() { return processingTime; } public void setProcessingTime(long processingTime) { this.processingTime = processingTime; } /** * Set the maximum number of actives Sessions allowed, or -1 for * no limit. * * @param max The new maximum number of sessions */ public void setMaxActiveSessions(int max) { int oldMaxActiveSessions = this.maxActiveSessions; this.maxActiveSessions = max; support.firePropertyChange("maxActiveSessions", new Integer(oldMaxActiveSessions), new Integer(this.maxActiveSessions)); } /** * Return the descriptive short name of this Manager implementation. */ public String getName() { return (name); } /** * 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 null. * * @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 /** * Construct and return a new session object, based on the default * settings specified by this Manager's properties. The session * id will be assigned by this method, and available via the getId() * method of the returned session. If a new session cannot be created * for any reason, return null. * * @exception IllegalStateException if a new session cannot be * instantiated for any reason * * Construct and return a new session object, based on the default * settings specified by this Manager's properties. The session * id will be assigned by this method, and available via the getId() * method of the returned session. If a new session cannot be created * for any reason, return null. * * @exception IllegalStateException if a new session cannot be * instantiated for any reason */ public Session createSession() { if ((maxActiveSessions >= 0) && (sessions.size() >= maxActiveSessions)) { rejectedSessions++; throw new IllegalStateException (sm.getString("standardManager.createSession.ise")); } // Recycle or create a Session instance Session session = getNewDeltaSession(); // Initialize the properties of the new session and return it session.setNew(true); session.setValid(true); session.setCreationTime(System.currentTimeMillis()); session.setMaxInactiveInterval(this.maxInactiveInterval); String sessionId = generateSessionId(); String jvmRoute = getJvmRoute(); // @todo Move appending of jvmRoute generateSessionId()??? if (jvmRoute != null) { sessionId += '.' + jvmRoute; } synchronized (sessions) { while (sessions.get(sessionId) != null) { // Guarantee uniqueness duplicates++; sessionId = generateSessionId(); // @todo Move appending of jvmRoute generateSessionId()??? if (jvmRoute != null) { sessionId += '.' + jvmRoute; } } } session.setId(sessionId); sessionCounter++; return (session); } /** * Get new session class to be used in the doLoad() method. */ protected DeltaSession getNewDeltaSession() { return new DeltaSession(this); } /** * Load any currently active sessions that were previously unloaded * to the appropriate persistence mechanism, if any. If persistence is not * supported, this method returns without doing anything. * * @exception ClassNotFoundException if a serialized class cannot be * found during the reload * @exception IOException if an input/output error occurs */ public void load() throws ClassNotFoundException, IOException { if (System.getSecurityManager() != null){ try{ AccessController.doPrivileged( new PrivilegedDoLoad() ); } catch (PrivilegedActionException ex){ Exception exception = ex.getException(); if (exception instanceof ClassNotFoundException){ throw (ClassNotFoundException)exception; } else if (exception instanceof IOException){ throw (IOException)exception; } if (log.isDebugEnabled()) log.debug("Unreported exception in load() " + exception); } } else { doLoad(); } } /** * Load any currently active sessions that were previously unloaded * to the appropriate persistence mechanism, if any. If persistence is not * supported, this method returns without doing anything. * * @exception ClassNotFoundException if a serialized class cannot be * found during the reload * @exception IOException if an input/output error occurs */ private void doLoad() throws ClassNotFoundException, IOException { if (log.isDebugEnabled()) log.debug("Start: Loading persisted sessions"); // Initialize our internal data structures sessions.clear(); // Open an input stream to the specified pathname, if any File file = file(); if (file == null) return; if (log.isDebugEnabled()) log.debug(sm.getString("standardManager.loading", pathname)); FileInputStream fis = null; ObjectInputStream ois = null; Loader loader = null; ClassLoader classLoader = null; try { fis = new FileInputStream(file.getAbsolutePath()); BufferedInputStream bis = new BufferedInputStream(fis); if (container != null) loader = container.getLoader(); if (loader != null) classLoader = loader.getClassLoader(); if (classLoader != null) { if (log.isDebugEnabled()) log.debug("Creating custom object input stream for class loader "); ois = new CustomObjectInputStream(bis, classLoader); } else { if (log.isDebugEnabled()) log.debug("Creating standard object input stream"); ois = new ObjectInputStream(bis); } } catch (FileNotFoundException e) { if (log.isDebugEnabled()) log.debug("No persisted data file found"); return; } catch (IOException e) { log.error(sm.getString("standardManager.loading.ioe", e), e); if (ois != null) { try { ois.close(); } catch (IOException f) { ; } ois = null; } throw e; } // Load the previously unloaded active sessions synchronized (sessions) { try { Integer count = (Integer) ois.readObject(); int n = count.intValue(); if (log.isDebugEnabled()) log.debug("Loading " + n + " persisted sessions"); for (int i = 0; i < n; i++) { DeltaSession session = getNewDeltaSession(); session.readObjectData(ois); session.setManager(this); sessions.put(session.getId(), session); session.activate(); } } catch (ClassNotFoundException e) { log.error(sm.getString("standardManager.loading.cnfe", e), e); if (ois != null) { try { ois.close(); } catch (IOException f) { ; } ois = null; } throw e; } catch (IOException e) { log.error(sm.getString("standardManager.loading.ioe", e), e); if (ois != null) { try { ois.close(); } catch (IOException f) { ; } ois = null; } throw e; } finally { // Close the input stream try { if (ois != null) ois.close(); } catch (IOException f) { // ignored } // Delete the persistent storage file if (file != null && file.exists() ) file.delete(); } } if (log.isDebugEnabled()) log.debug("Finish: Loading persisted sessions"); } /** * Save any currently active sessions in the appropriate persistence * mechanism, if any. If persistence is not supported, this method * returns without doing anything. * * @exception IOException if an input/output error occurs */ public void unload() throws IOException { if (System.getSecurityManager() != null){ try{ AccessController.doPrivileged( new PrivilegedDoUnload() ); } catch (PrivilegedActionException ex){ Exception exception = ex.getException(); if (exception instanceof IOException){ throw (IOException)exception; } if (log.isDebugEnabled()) log.debug("Unreported exception in unLoad() " + exception); } } else { doUnload(); } } /** * Save any currently active sessions in the appropriate persistence * mechanism, if any. If persistence is not supported, this method * returns without doing anything. * * @exception IOException if an input/output error occurs */ private void doUnload() throws IOException { if (log.isDebugEnabled()) log.debug("Unloading persisted sessions"); // Open an output stream to the specified pathname, if any File file = file(); if (file == null) return; if (log.isDebugEnabled()) log.debug(sm.getString("standardManager.unloading", pathname)); FileOutputStream fos = null; ObjectOutputStream oos = null; try { fos = new FileOutputStream(file.getAbsolutePath()); oos = new ObjectOutputStream(new BufferedOutputStream(fos)); } catch (IOException e) { log.error(sm.getString("standardManager.unloading.ioe", e), e); if (oos != null) { try { oos.close(); } catch (IOException f) { ; } oos = null; } throw e; } // Write the number of active sessions, followed by the details ArrayList list = new ArrayList(); synchronized (sessions) { if (log.isDebugEnabled()) log.debug("Unloading " + sessions.size() + " sessions"); try { oos.writeObject(new Integer(sessions.size())); Iterator elements = sessions.values().iterator(); while (elements.hasNext()) { DeltaSession session = (DeltaSession) elements.next(); list.add(session); ((DeltaSession) session).passivate(); session.writeObjectData(oos); } } catch (IOException e) { log.error(sm.getString("standardManager.unloading.ioe", e), e); if (oos != null) { try { oos.close(); } catch (IOException f) { ; } oos = null; } throw 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 e; } // Expire all the sessions we just wrote if (log.isDebugEnabled()) log.debug("Expiring " + list.size() + " persisted sessions"); Iterator expires = list.iterator(); while (expires.hasNext()) { DeltaSession session = (DeltaSession) expires.next(); try { session.expire(false); } catch (Throwable t) { ; } } if (log.isDebugEnabled()) log.debug("Unloading complete"); } // ------------------------------------------------------ Lifecycle Methods /** * Add a lifecycle event listener to this component. * * @param listener The listener to add */ public void addLifecycleListener(LifecycleListener listener) { lifecycle.addLifecycleListener(listener); } /** * Get the lifecycle listeners associated with this lifecycle. If this * Lifecycle has no listeners registered, a zero-length array is returned. */ public LifecycleListener[] findLifecycleListeners() { return lifecycle.findLifecycleListeners(); } /** * Remove a lifecycle event listener from this component. * * @param listener The listener to remove */ public void removeLifecycleListener(LifecycleListener listener) { lifecycle.removeLifecycleListener(listener); } /** * Prepare for the beginning of active use of the public methods of this * component. This method should be called after configure(), * and before any of the public methods of the component are utilized. * * @exception LifecycleException if this component detects a fatal error * that prevents this component from being used */ public void start() throws LifecycleException { if( ! initialized ) init(); // Validate and update our current component state if (started) { return; } lifecycle.fireLifecycleEvent(START_EVENT, null); started = true; // Force initialization of the random number generator if (log.isDebugEnabled()) log.debug("Force random number initialization starting"); String dummy = generateSessionId(); if (log.isDebugEnabled()) log.debug("Force random number initialization completed"); // Load unloaded sessions, if any try { load(); } catch (Throwable t) { log.error(sm.getString("standardManager.managerLoad"), t); } } /** * Gracefully terminate the active use of the public methods of this * component. This method should be the last one called on a given * instance of this component. * * @exception LifecycleException if this component detects a fatal error * that needs to be reported */ public void stop() throws LifecycleException { if (log.isDebugEnabled()) log.debug("Stopping"); // Validate and update our current component state if (!started) throw new LifecycleException (sm.getString("standardManager.notStarted")); lifecycle.fireLifecycleEvent(STOP_EVENT, null); started = false; // Write out sessions try { unload(); } catch (IOException e) { log.error(sm.getString("standardManager.managerUnload"), e); } // Expire all active sessions Session sessions[] = findSessions(); for (int i = 0; i < sessions.length; i++) { DeltaSession session = (DeltaSession) sessions[i]; if (!session.isValid()) continue; try { session.expire(); } catch (Throwable t) { ; } } // Require a new random number generator if we are restarted this.random = null; if( initialized ) { destroy(); } } // ----------------------------------------- PropertyChangeListener Methods /** * Process property change events from our associated Context. * * @param event The property change event that has occurred */ public void propertyChange(PropertyChangeEvent event) { // Validate the source of this event if (!(event.getSource() instanceof Context)) return; Context context = (Context) event.getSource(); // Process a relevant property change if (event.getPropertyName().equals("sessionTimeout")) { try { setMaxInactiveInterval ( ((Integer) event.getNewValue()).intValue()*60 ); } catch (NumberFormatException e) { log.error(sm.getString("standardManager.sessionTimeout", event.getNewValue().toString())); } } } // -------------------------------------------------------- Private Methods /** * Return a File object representing the pathname to our * persistence file, if any. */ private File file() { if ((pathname == null) || (pathname.length() == 0)) return (null); File file = new File(pathname); if (!file.isAbsolute()) { if (container instanceof Context) { ServletContext servletContext = ((Context) container).getServletContext(); File tempdir = (File) servletContext.getAttribute(Globals.WORK_DIR_ATTR); if (tempdir != null) file = new File(tempdir, pathname); } } // if (!file.isAbsolute()) // return (null); return (file); } /** * Invalidate all sessions that have expired. */ public void processExpires() { long timeNow = System.currentTimeMillis(); Session sessions[] = findSessions(); for (int i = 0; i < sessions.length; i++) { DeltaSession session = (DeltaSession) sessions[i]; if (!session.isValid()) { try { expiredSessions++; session.expire(); } catch (Throwable t) { log.error(sm.getString ("standardManager.expireException"), t); } } } long timeEnd = System.currentTimeMillis(); processingTime += ( timeEnd - timeNow ); } } 1.1 jakarta-tomcat-catalina/modules/cluster/src/share/org/apache/catalina/cluster/session/DeltaSession.java Index: DeltaSession.java =================================================================== /* * $Header: /home/cvs/jakarta-tomcat-catalina/modules/cluster/src/share/org/apache/catalina/cluster/session/DeltaSession.java,v 1.1 2003/11/14 20:07:52 fhanik Exp $ * $Revision: 1.1 $ * $Date: 2003/11/14 20:07:52 $ * * ==================================================================== * * 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 * . * * [Additional notices, if required by prior licensing conditions] * */ package org.apache.catalina.cluster.session; import java.beans.PropertyChangeSupport; import java.io.IOException; import java.io.NotSerializableException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.lang.reflect.Method; import java.security.AccessController; import java.security.Principal; import java.security.PrivilegedAction; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import javax.servlet.ServletContext; import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSessionActivationListener; import javax.servlet.http.HttpSessionAttributeListener; import javax.servlet.http.HttpSessionBindingEvent; import javax.servlet.http.HttpSessionBindingListener; import javax.servlet.http.HttpSessionContext; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; import org.apache.catalina.Context; import org.apache.catalina.Manager; import org.apache.catalina.Session; import org.apache.catalina.SessionEvent; import org.apache.catalina.SessionListener; import org.apache.catalina.util.Enumerator; import org.apache.catalina.util.StringManager; import org.apache.catalina.session.ManagerBase; import org.apache.catalina.session.Constants; /** * * Similar to the StandardSession, this code is identical, but for update * and some small issues, simply copied in the first release. * This session will keep track of deltas during a request. *

* IMPLEMENTATION NOTE: An instance of this class represents both the * internal (Session) and application level (HttpSession) view of the session. * However, because the class itself is not declared public, Java logic outside * of the org.apache.catalina.session package cannot cast an * HttpSession view of this instance back to a Session view. *

* IMPLEMENTATION NOTE: If you add fields to this class, you must * make sure that you carry them over in the read/writeObject methods so * that this class is properly serialized. * * @author Filip Hanik * @author Craig R. McClanahan * @author Sean Legassick * @author Jon S. Stevens * @version $Revision: 1.1 $ $Date: 2003/11/14 20:07:52 $ */ public class DeltaSession implements HttpSession, Session, Serializable { // ----------------------------------------------------------- Constructors /** * Construct a new Session associated with the specified Manager. * * @param manager The manager with which this Session is associated */ public DeltaSession(Manager manager) { super(); this.manager = manager; if (manager instanceof ManagerBase) this.debug = ((ManagerBase) manager).getDebug(); } // ----------------------------------------------------- Instance Variables /** * The dummy attribute value serialized when a NotSerializableException is * encountered in writeObject(). */ private static final String NOT_SERIALIZED = "___NOT_SERIALIZABLE_EXCEPTION___"; /** * The collection of user data attributes associated with this Session. */ private HashMap attributes = new HashMap(); /** * The authentication type used to authenticate our cached Principal, * if any. NOTE: This value is not included in the serialized * version of this object. */ private transient String authType = null; /** * The java.lang.Method for the * fireContainerEvent() method of the * org.apache.catalina.core.StandardContext method, * if our Context implementation is of this class. This value is * computed dynamically the first time it is needed, or after * a session reload (since it is declared transient). */ private transient Method containerEventMethod = null; /** * The method signature for the fireContainerEvent method. */ private static final Class containerEventTypes[] = { String.class, Object.class }; /** * The time this session was created, in milliseconds since midnight, * January 1, 1970 GMT. */ private long creationTime = 0L; /** * The debugging detail level for this component. NOTE: This value * is not included in the serialized version of this object. */ private transient int debug = 0; /** * We are currently processing a session expiration, so bypass * certain IllegalStateException tests. NOTE: This value is not * included in the serialized version of this object. */ private transient boolean expiring = false; /** * The facade associated with this session. NOTE: This value is not * included in the serialized version of this object. */ private transient DeltaSessionFacade facade = null; /** * The session identifier of this Session. */ private String id = null; /** * Descriptive information describing this Session implementation. */ private static final String info = "StandardSession/1.0"; /** * The last accessed time for this Session. */ private long lastAccessedTime = creationTime; /** * The session event listeners for this Session. */ private transient ArrayList listeners = new ArrayList(); /** * The Manager with which this Session is associated. */ private Manager manager = null; /** * The maximum time interval, in seconds, between client requests before * the servlet container may invalidate this session. A negative time * indicates that the session should never time out. */ private int maxInactiveInterval = -1; /** * Flag indicating whether this session is new or not. */ private boolean isNew = false; /** * Flag indicating whether this session is valid or not. */ private boolean isValid = false; /** * Internal notes associated with this session by Catalina components * and event listeners. IMPLEMENTATION NOTE: This object is * not saved and restored across session serializations! */ private transient HashMap notes = new HashMap(); /** * The authenticated Principal associated with this session, if any. * IMPLEMENTATION NOTE: This object is not saved and * restored across session serializations! */ private transient Principal principal = null; /** * The string manager for this package. */ private static StringManager sm = StringManager.getManager(Constants.Package); /** * The HTTP session context associated with this session. */ private static HttpSessionContext sessionContext = null; /** * The property change support for this component. NOTE: This value * is not included in the serialized version of this object. */ private transient PropertyChangeSupport support = new PropertyChangeSupport(this); /** * The current accessed time for this session. */ private long thisAccessedTime = creationTime; // ----------------------------------------------------- Session Properties /** * Return the authentication type used to authenticate our cached * Principal, if any. */ public String getAuthType() { return (this.authType); } /** * Set the authentication type used to authenticate our cached * Principal, if any. * * @param authType The new cached authentication type */ public void setAuthType(String authType) { String oldAuthType = this.authType; this.authType = authType; support.firePropertyChange("authType", oldAuthType, this.authType); } /** * Set the creation time for this session. This method is called by the * Manager when an existing Session instance is reused. * * @param time The new creation time */ public void setCreationTime(long time) { this.creationTime = time; this.lastAccessedTime = time; this.thisAccessedTime = time; } /** * Return the session identifier for this session. */ public String getId() { return (this.id); } /** * Set the session identifier for this session. * * @param id The new session identifier */ public void setId(String id) { if ((this.id != null) && (manager != null)) manager.remove(this); this.id = id; if (manager != null) manager.add(this); tellNew(); } /** * Inform the listeners about the new session. * */ public void tellNew() { // Notify interested session event listeners fireSessionEvent(Session.SESSION_CREATED_EVENT, null); // Notify interested application event listeners Context context = (Context) manager.getContainer(); Object listeners[] = context.getApplicationLifecycleListeners(); if (listeners != null) { HttpSessionEvent event = new HttpSessionEvent(getSession()); for (int i = 0; i < listeners.length; i++) { if (!(listeners[i] instanceof HttpSessionListener)) continue; HttpSessionListener listener = (HttpSessionListener) listeners[i]; try { fireContainerEvent(context, "beforeSessionCreated", listener); listener.sessionCreated(event); fireContainerEvent(context, "afterSessionCreated", listener); } catch (Throwable t) { try { fireContainerEvent(context, "afterSessionCreated", listener); } catch (Exception e) { ; } // FIXME - should we do anything besides log these? log(sm.getString("standardSession.sessionEvent"), t); } } } } /** * Return descriptive information about this Session implementation and * the corresponding version number, in the format * <description>/<version>. */ public String getInfo() { return (this.info); } /** * Return the last time the client sent a request associated with this * session, as the number of milliseconds since midnight, January 1, 1970 * GMT. Actions that your application takes, such as getting or setting * a value associated with the session, do not affect the access time. */ public long getLastAccessedTime() { if ( !isValid ) { throw new IllegalStateException( sm.getString("standardSession.getLastAccessedTime")); } return (this.lastAccessedTime); } /** * Return the Manager within which this Session is valid. */ public Manager getManager() { return (this.manager); } /** * Set the Manager within which this Session is valid. * * @param manager The new Manager */ public void setManager(Manager manager) { this.manager = manager; } /** * Return the maximum time interval, in seconds, between client requests * before the servlet container will invalidate the session. A negative * time indicates that the session should never time out. */ public int getMaxInactiveInterval() { return (this.maxInactiveInterval); } /** * Set the maximum time interval, in seconds, between client requests * before the servlet container will invalidate the session. A negative * time indicates that the session should never time out. * * @param interval The new maximum interval */ public void setMaxInactiveInterval(int interval) { this.maxInactiveInterval = interval; if (isValid && interval == 0) { expire(); } } /** * Set the isNew flag for this session. * * @param isNew The new value for the isNew flag */ public void setNew(boolean isNew) { this.isNew = isNew; } /** * Return the authenticated Principal that is associated with this Session. * This provides an Authenticator with a means to cache a * previously authenticated Principal, and avoid potentially expensive * Realm.authenticate() calls on every request. If there * is no current associated Principal, return null. */ public Principal getPrincipal() { return (this.principal); } /** * Set the authenticated Principal that is associated with this Session. * This provides an Authenticator with a means to cache a * previously authenticated Principal, and avoid potentially expensive * Realm.authenticate() calls on every request. * * @param principal The new Principal, or null if none */ public void setPrincipal(Principal principal) { Principal oldPrincipal = this.principal; this.principal = principal; support.firePropertyChange("principal", oldPrincipal, this.principal); } /** * Return the HttpSession for which this object * is the facade. */ public HttpSession getSession() { if (facade == null){ if (System.getSecurityManager() != null){ final DeltaSession fsession = this; facade = (DeltaSessionFacade)AccessController.doPrivileged(new PrivilegedAction(){ public Object run(){ return new DeltaSessionFacade(fsession); } }); } else { facade = new DeltaSessionFacade(this); } } return (facade); } /** * Return the isValid flag for this session. */ public boolean isValid() { if (this.expiring){ return true; } if (!this.isValid ) { return false; } if (maxInactiveInterval >= 0) { long timeNow = System.currentTimeMillis(); int timeIdle = (int) ((timeNow - lastAccessedTime) / 1000L); if (timeIdle >= maxInactiveInterval) { expire(true); } } return (this.isValid); } /** * Set the isValid flag for this session. * * @param isValid The new value for the isValid flag */ public void setValid(boolean isValid) { this.isValid = isValid; } // ------------------------------------------------- Session Public Methods /** * Update the accessed time information for this session. This method * should be called by the context when a request comes in for a particular * session, even if the application does not reference it. */ public void access() { this.isNew = false; this.lastAccessedTime = this.thisAccessedTime; this.thisAccessedTime = System.currentTimeMillis(); evaluateIfValid(); } /** * Add a session event listener to this component. */ public void addSessionListener(SessionListener listener) { synchronized (listeners) { listeners.add(listener); } } /** * Perform the internal processing required to invalidate this session, * without triggering an exception if the session has already expired. */ public void expire() { expire(true); } /** * Perform the internal processing required to invalidate this session, * without triggering an exception if the session has already expired. * * @param notify Should we notify listeners about the demise of * this session? */ public void expire(boolean notify) { // Mark this session as "being expired" if needed if (expiring) return; synchronized (this) { if (manager == null) return; expiring = true; // Notify interested application event listeners // FIXME - Assumes we call listeners in reverse order Context context = (Context) manager.getContainer(); Object listeners[] = context.getApplicationLifecycleListeners(); if (notify && (listeners != null)) { HttpSessionEvent event = new HttpSessionEvent(getSession()); for (int i = 0; i < listeners.length; i++) { int j = (listeners.length - 1) - i; if (!(listeners[j] instanceof HttpSessionListener)) continue; HttpSessionListener listener = (HttpSessionListener) listeners[j]; try { fireContainerEvent(context, "beforeSessionDestroyed", listener); listener.sessionDestroyed(event); fireContainerEvent(context, "afterSessionDestroyed", listener); } catch (Throwable t) { try { fireContainerEvent(context, "afterSessionDestroyed", listener); } catch (Exception e) { ; } // FIXME - should we do anything besides log these? log(sm.getString("standardSession.sessionEvent"), t); } } } setValid(false); // Remove this session from our manager's active sessions if (manager != null) manager.remove(this); // Unbind any objects associated with this session String keys[] = keys(); for (int i = 0; i < keys.length; i++) removeAttribute(keys[i], notify); // Notify interested session event listeners if (notify) { fireSessionEvent(Session.SESSION_DESTROYED_EVENT, null); } // We have completed expire of this session expiring = false; } } /** * Perform the internal processing required to passivate * this session. */ public void passivate() { // Notify ActivationListeners HttpSessionEvent event = null; String keys[] = keys(); for (int i = 0; i < keys.length; i++) { Object attribute = getAttributeInternal(keys[i]); if (attribute instanceof HttpSessionActivationListener) { if (event == null) event = new HttpSessionEvent(this); // FIXME: Should we catch throwables? ((HttpSessionActivationListener)attribute).sessionWillPassivate(event); } } } /** * Perform internal processing required to activate this * session. */ public void activate() { // Notify ActivationListeners HttpSessionEvent event = null; String keys[] = keys(); for (int i = 0; i < keys.length; i++) { Object attribute = getAttributeInternal(keys[i]); if (attribute instanceof HttpSessionActivationListener) { if (event == null) event = new HttpSessionEvent(this); // FIXME: Should we catch throwables? ((HttpSessionActivationListener)attribute).sessionDidActivate(event); } } } /** * Return the object bound with the specified name to the internal notes * for this session, or null if no such binding exists. * * @param name Name of the note to be returned */ public Object getNote(String name) { synchronized (notes) { return (notes.get(name)); } } /** * Return an Iterator containing the String names of all notes bindings * that exist for this session. */ public Iterator getNoteNames() { synchronized (notes) { return (notes.keySet().iterator()); } } /** * Release all object references, and initialize instance variables, in * preparation for reuse of this object. */ public void recycle() { // Reset the instance variables associated with this Session attributes.clear(); setAuthType(null); creationTime = 0L; expiring = false; id = null; lastAccessedTime = 0L; maxInactiveInterval = -1; notes.clear(); setPrincipal(null); isNew = false; isValid = false; manager = null; } /** * Remove any object bound to the specified name in the internal notes * for this session. * * @param name Name of the note to be removed */ public void removeNote(String name) { synchronized (notes) { notes.remove(name); } } /** * Remove a session event listener from this component. */ public void removeSessionListener(SessionListener listener) { synchronized (listeners) { listeners.remove(listener); } } /** * Bind an object to a specified name in the internal notes associated * with this session, replacing any existing binding for this name. * * @param name Name to which the object should be bound * @param value Object to be bound to the specified name */ public void setNote(String name, Object value) { synchronized (notes) { notes.put(name, value); } } /** * Return a string representation of this object. */ public String toString() { StringBuffer sb = new StringBuffer(); sb.append("StandardSession["); sb.append(id); sb.append("]"); return (sb.toString()); } // ------------------------------------------------ Session Package Methods /** * Read a serialized version of the contents of this session object from * the specified object input stream, without requiring that the * StandardSession itself have been serialized. * * @param stream The object input stream to read from * * @exception ClassNotFoundException if an unknown class is specified * @exception IOException if an input/output error occurs */ public void readObjectData(ObjectInputStream stream) throws ClassNotFoundException, IOException { readObject(stream); } /** * Write a serialized version of the contents of this session object to * the specified object output stream, without requiring that the * StandardSession itself have been serialized. * * @param stream The object output stream to write to * * @exception IOException if an input/output error occurs */ public void writeObjectData(ObjectOutputStream stream) throws IOException { writeObject(stream); } // ------------------------------------------------- HttpSession Properties /** * Return the time when this session was created, in milliseconds since * midnight, January 1, 1970 GMT. * * @exception IllegalStateException if this method is called on an * invalidated session */ public long getCreationTime() { if (!isValid()) throw new IllegalStateException (sm.getString("standardSession.getCreationTime.ise")); return (this.creationTime); } /** * Return the ServletContext to which this session belongs. */ public ServletContext getServletContext() { if (manager == null) return (null); Context context = (Context) manager.getContainer(); if (context == null) return (null); else return (context.getServletContext()); } /** * Return the session context with which this session is associated. * * @deprecated As of Version 2.1, this method is deprecated and has no * replacement. It will be removed in a future version of the * Java Servlet API. */ public HttpSessionContext getSessionContext() { if (sessionContext == null) sessionContext = new StandardSessionContext(); return (sessionContext); } // ----------------------------------------------HttpSession Public Methods /** * Return the object bound with the specified name in this session, or * null if no object is bound with that name. * * @param name Name of the attribute to be returned * * @exception IllegalStateException if this method is called on an * invalidated session */ public Object getAttribute(String name) { if (!isValid()) throw new IllegalStateException (sm.getString("standardSession.getAttribute.ise")); synchronized (attributes) { return (attributes.get(name)); } } /** * Return an Enumeration of String objects * containing the names of the objects bound to this session. * * @exception IllegalStateException if this method is called on an * invalidated session */ public Enumeration getAttributeNames() { if (!isValid()) throw new IllegalStateException (sm.getString("standardSession.getAttributeNames.ise")); synchronized (attributes) { return (new Enumerator(attributes.keySet(), true)); } } /** * Return the object bound with the specified name in this session, or * null if no object is bound with that name. * * @param name Name of the value to be returned * * @exception IllegalStateException if this method is called on an * invalidated session * * @deprecated As of Version 2.2, this method is replaced by * getAttribute() */ public Object getValue(String name) { return (getAttribute(name)); } /** * Return the set of names of objects bound to this session. If there * are no such objects, a zero-length array is returned. * * @exception IllegalStateException if this method is called on an * invalidated session * * @deprecated As of Version 2.2, this method is replaced by * getAttributeNames() */ public String[] getValueNames() { if (!isValid()) throw new IllegalStateException (sm.getString("standardSession.getValueNames.ise")); return (keys()); } /** * Invalidates this session and unbinds any objects bound to it. * * @exception IllegalStateException if this method is called on * an invalidated session */ public void invalidate() { if (!isValid()) throw new IllegalStateException (sm.getString("standardSession.invalidate.ise")); // Cause this session to expire expire(); } /** * Return true if the client does not yet know about the * session, or if the client chooses not to join the session. For * example, if the server used only cookie-based sessions, and the client * has disabled the use of cookies, then a session would be new on each * request. * * @exception IllegalStateException if this method is called on an * invalidated session */ public boolean isNew() { if (!isValid()) throw new IllegalStateException (sm.getString("standardSession.isNew.ise")); return (this.isNew); } /** * Bind an object to this session, using the specified name. If an object * of the same name is already bound to this session, the object is * replaced. *

* After this method executes, and if the object implements * HttpSessionBindingListener, the container calls * valueBound() on the object. * * @param name Name to which the object is bound, cannot be null * @param value Object to be bound, cannot be null * * @exception IllegalStateException if this method is called on an * invalidated session * * @deprecated As of Version 2.2, this method is replaced by * setAttribute() */ public void putValue(String name, Object value) { setAttribute(name, value); } /** * Remove the object bound with the specified name from this session. If * the session does not have an object bound with this name, this method * does nothing. *

* After this method executes, and if the object implements * HttpSessionBindingListener, the container calls * valueUnbound() on the object. * * @param name Name of the object to remove from this session. * * @exception IllegalStateException if this method is called on an * invalidated session */ public void removeAttribute(String name) { removeAttribute(name, true); } /** * Remove the object bound with the specified name from this session. If * the session does not have an object bound with this name, this method * does nothing. *

* After this method executes, and if the object implements * HttpSessionBindingListener, the container calls * valueUnbound() on the object. * * @param name Name of the object to remove from this session. * @param notify Should we notify interested listeners that this * attribute is being removed? * * @exception IllegalStateException if this method is called on an * invalidated session */ public void removeAttribute(String name, boolean notify) { // Validate our current state if (!isValid()) throw new IllegalStateException (sm.getString("standardSession.removeAttribute.ise")); // Remove this attribute from our collection Object value = null; boolean found = false; synchronized (attributes) { found = attributes.containsKey(name); if (found) { value = attributes.get(name); attributes.remove(name); } else { return; } } // Do we need to do valueUnbound() and attributeRemoved() notification? if (!notify) { return; } // Call the valueUnbound() method if necessary HttpSessionBindingEvent event = new HttpSessionBindingEvent((HttpSession) this, name, value); if ((value != null) && (value instanceof HttpSessionBindingListener)) ((HttpSessionBindingListener) value).valueUnbound(event); // Notify interested application event listeners Context context = (Context) manager.getContainer(); Object listeners[] = context.getApplicationEventListeners(); if (listeners == null) return; for (int i = 0; i < listeners.length; i++) { if (!(listeners[i] instanceof HttpSessionAttributeListener)) continue; HttpSessionAttributeListener listener = (HttpSessionAttributeListener) listeners[i]; try { fireContainerEvent(context, "beforeSessionAttributeRemoved", listener); listener.attributeRemoved(event); fireContainerEvent(context, "afterSessionAttributeRemoved", listener); } catch (Throwable t) { try { fireContainerEvent(context, "afterSessionAttributeRemoved", listener); } catch (Exception e) { ; } // FIXME - should we do anything besides log these? log(sm.getString("standardSession.attributeEvent"), t); } } } /** * Remove the object bound with the specified name from this session. If * the session does not have an object bound with this name, this method * does nothing. *

* After this method executes, and if the object implements * HttpSessionBindingListener, the container calls * valueUnbound() on the object. * * @param name Name of the object to remove from this session. * * @exception IllegalStateException if this method is called on an * invalidated session * * @deprecated As of Version 2.2, this method is replaced by * removeAttribute() */ public void removeValue(String name) { removeAttribute(name); } /** * Bind an object to this session, using the specified name. If an object * of the same name is already bound to this session, the object is * replaced. *

* After this method executes, and if the object implements * HttpSessionBindingListener, the container calls * valueBound() on the object. * * @param name Name to which the object is bound, cannot be null * @param value Object to be bound, cannot be null * * @exception IllegalArgumentException if an attempt is made to add a * non-serializable object in an environment marked distributable. * @exception IllegalStateException if this method is called on an * invalidated session */ public void setAttribute(String name, Object value) { // Name cannot be null if (name == null) throw new IllegalArgumentException (sm.getString("standardSession.setAttribute.namenull")); // Null value is the same as removeAttribute() if (value == null) { removeAttribute(name); return; } // Validate our current state if (!isValid()) throw new IllegalStateException (sm.getString("standardSession.setAttribute.ise")); if ((manager != null) && manager.getDistributable() && !(value instanceof Serializable)) throw new IllegalArgumentException (sm.getString("standardSession.setAttribute.iae")); // Replace or add this attribute Object unbound = null; synchronized (attributes) { unbound = attributes.get(name); attributes.put(name, value); } // Call the valueUnbound() method if necessary if ((unbound != null) && (unbound instanceof HttpSessionBindingListener)) { ((HttpSessionBindingListener) unbound).valueUnbound (new HttpSessionBindingEvent((HttpSession) this, name)); } // Call the valueBound() method if necessary HttpSessionBindingEvent event = null; if (unbound != null) event = new HttpSessionBindingEvent ((HttpSession) this, name, unbound); else event = new HttpSessionBindingEvent ((HttpSession) this, name, value); if (value instanceof HttpSessionBindingListener) ((HttpSessionBindingListener) value).valueBound(event); // Notify interested application event listeners Context context = (Context) manager.getContainer(); Object listeners[] = context.getApplicationEventListeners(); if (listeners == null) return; for (int i = 0; i < listeners.length; i++) { if (!(listeners[i] instanceof HttpSessionAttributeListener)) continue; HttpSessionAttributeListener listener = (HttpSessionAttributeListener) listeners[i]; try { if (unbound != null) { fireContainerEvent(context, "beforeSessionAttributeReplaced", listener); listener.attributeReplaced(event); fireContainerEvent(context, "afterSessionAttributeReplaced", listener); } else { fireContainerEvent(context, "beforeSessionAttributeAdded", listener); listener.attributeAdded(event); fireContainerEvent(context, "afterSessionAttributeAdded", listener); } } catch (Throwable t) { try { if (unbound != null) { fireContainerEvent(context, "afterSessionAttributeReplaced", listener); } else { fireContainerEvent(context, "afterSessionAttributeAdded", listener); } } catch (Exception e) { ; } // FIXME - should we do anything besides log these? log(sm.getString("standardSession.attributeEvent"), t); } } } // -------------------------------------------- HttpSession Private Methods /** * Read a serialized version of this session object from the specified * object input stream. *

* IMPLEMENTATION NOTE: The reference to the owning Manager * is not restored by this method, and must be set explicitly. * * @param stream The input stream to read from * * @exception ClassNotFoundException if an unknown class is specified * @exception IOException if an input/output error occurs */ private void readObject(ObjectInputStream stream) throws ClassNotFoundException, IOException { // Deserialize the scalar instance variables (except Manager) authType = null; // Transient only creationTime = ((Long) stream.readObject()).longValue(); lastAccessedTime = ((Long) stream.readObject()).longValue(); maxInactiveInterval = ((Integer) stream.readObject()).intValue(); isNew = ((Boolean) stream.readObject()).booleanValue(); isValid = ((Boolean) stream.readObject()).booleanValue(); thisAccessedTime = ((Long) stream.readObject()).longValue(); principal = null; // Transient only // setId((String) stream.readObject()); id = (String) stream.readObject(); if (debug >= 2) log("readObject() loading session " + id); // Deserialize the attribute count and attribute values if (attributes == null) attributes = new HashMap(); int n = ((Integer) stream.readObject()).intValue(); boolean isValidSave = isValid; isValid = true; for (int i = 0; i < n; i++) { String name = (String) stream.readObject(); Object value = (Object) stream.readObject(); if ((value instanceof String) && (value.equals(NOT_SERIALIZED))) continue; if (debug >= 2) log(" loading attribute '" + name + "' with value '" + value + "'"); synchronized (attributes) { attributes.put(name, value); } } isValid = isValidSave; } /** * Write a serialized version of this session object to the specified * object output stream. *

* IMPLEMENTATION NOTE: The owning Manager will not be stored * in the serialized representation of this Session. After calling * readObject(), you must set the associated Manager * explicitly. *

* IMPLEMENTATION NOTE: Any attribute that is not Serializable * will be unbound from the session, with appropriate actions if it * implements HttpSessionBindingListener. If you do not want any such * attributes, be sure the distributable property of the * associated Manager is set to true. * * @param stream The output stream to write to * * @exception IOException if an input/output error occurs */ private void writeObject(ObjectOutputStream stream) throws IOException { // Write the scalar instance variables (except Manager) stream.writeObject(new Long(creationTime)); stream.writeObject(new Long(lastAccessedTime)); stream.writeObject(new Integer(maxInactiveInterval)); stream.writeObject(new Boolean(isNew)); stream.writeObject(new Boolean(isValid)); stream.writeObject(new Long(thisAccessedTime)); stream.writeObject(id); if (debug >= 2) log("writeObject() storing session " + id); // Accumulate the names of serializable and non-serializable attributes String keys[] = keys(); ArrayList saveNames = new ArrayList(); ArrayList saveValues = new ArrayList(); for (int i = 0; i < keys.length; i++) { Object value = null; synchronized (attributes) { value = attributes.get(keys[i]); } if (value == null) continue; else if (value instanceof Serializable) { saveNames.add(keys[i]); saveValues.add(value); } } // Serialize the attribute count and the Serializable attributes int n = saveNames.size(); stream.writeObject(new Integer(n)); for (int i = 0; i < n; i++) { stream.writeObject((String) saveNames.get(i)); try { stream.writeObject(saveValues.get(i)); if (debug >= 2) log(" storing attribute '" + saveNames.get(i) + "' with value '" + saveValues.get(i) + "'"); } catch (NotSerializableException e) { log(sm.getString("standardSession.notSerializable", saveNames.get(i), id), e); stream.writeObject(NOT_SERIALIZED); if (debug >= 2) log(" storing attribute '" + saveNames.get(i) + "' with value NOT_SERIALIZED"); } } } private void evaluateIfValid() { /* * If this session has expired or is in the process of expiring or * will never expire, return */ if (!this.isValid || expiring || maxInactiveInterval < 0) return; if (!isValid()) { try { expire(); } catch (Throwable t) { log(sm.getString("standardSession.expireException"), t); } } } // -------------------------------------------------------- Private Methods /** * Fire container events if the Context implementation is the * org.apache.catalina.core.StandardContext. * * @param context Context for which to fire events * @param type Event type * @param data Event data * * @exception Exception occurred during event firing */ private void fireContainerEvent(Context context, String type, Object data) throws Exception { if (!"org.apache.catalina.core.StandardContext".equals (context.getClass().getName())) { return; // Container events are not supported } // NOTE: Race condition is harmless, so do not synchronize if (containerEventMethod == null) { containerEventMethod = context.getClass().getMethod("fireContainerEvent", containerEventTypes); } Object containerEventParams[] = new Object[2]; containerEventParams[0] = type; containerEventParams[1] = data; containerEventMethod.invoke(context, containerEventParams); } /** * Notify all session event listeners that a particular event has * occurred for this Session. The default implementation performs * this notification synchronously using the calling thread. * * @param type Event type * @param data Event data */ public void fireSessionEvent(String type, Object data) { if (listeners.size() < 1) return; SessionEvent event = new SessionEvent(this, type, data); SessionListener list[] = new SessionListener[0]; synchronized (listeners) { list = (SessionListener[]) listeners.toArray(list); } for (int i = 0; i < list.length; i++){ ((SessionListener) list[i]).sessionEvent(event); } } /** * Return the names of all currently defined session attributes * as an array of Strings. If there are no defined attributes, a * zero-length array is returned. */ private String[] keys() { String results[] = new String[0]; synchronized (attributes) { return ((String[]) attributes.keySet().toArray(results)); } } /** * Return the value of an attribute without a check for validity. */ private Object getAttributeInternal(String name) { synchronized (attributes) { return (attributes.get(name)); } } /** * Log a message on the Logger associated with our Manager (if any). * * @param message Message to be logged */ protected void log(String message) { //if ((manager != null) && (manager instanceof ManagerBase)) { //noop, implement proper logging //} else { System.out.println("DeltaSession: " + message); //} } /** * Log a message on the Logger associated with our Manager (if any). * * @param message Message to be logged * @param throwable Associated exception */ protected void log(String message, Throwable throwable) { //if ((manager != null) && (manager instanceof ManagerBase)) { // ((ManagerBase) manager).log(message, throwable); //} else { System.out.println("DeltaSession: " + message); throwable.printStackTrace(System.out); //} } } // -------------------------------------------------------------- Private Class /** * This class is a dummy implementation of the HttpSessionContext * interface, to conform to the requirement that such an object be returned * when HttpSession.getSessionContext() is called. * * @author Craig R. McClanahan * * @deprecated As of Java Servlet API 2.1 with no replacement. The * interface will be removed in a future version of this API. */ final class StandardSessionContext implements HttpSessionContext { private HashMap dummy = new HashMap(); /** * Return the session identifiers of all sessions defined * within this context. * * @deprecated As of Java Servlet API 2.1 with no replacement. * This method must return an empty Enumeration * and will be removed in a future version of the API. */ public Enumeration getIds() { return (new Enumerator(dummy)); } /** * Return the HttpSession associated with the * specified session identifier. * * @param id Session identifier for which to look up a session * * @deprecated As of Java Servlet API 2.1 with no replacement. * This method must return null and will be removed in a * future version of the API. */ public HttpSession getSession(String id) { return (null); } } 1.1 jakarta-tomcat-catalina/modules/cluster/src/share/org/apache/catalina/cluster/session/DeltaSessionFacade.java Index: DeltaSessionFacade.java =================================================================== /* * $Header: /home/cvs/jakarta-tomcat-catalina/modules/cluster/src/share/org/apache/catalina/cluster/session/DeltaSessionFacade.java,v 1.1 2003/11/14 20:07:52 fhanik Exp $ * $Revision: 1.1 $ * $Date: 2003/11/14 20:07:52 $ * * ==================================================================== * * 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 * . * * [Additional notices, if required by prior licensing conditions] * */ package org.apache.catalina.cluster.session; import java.util.Enumeration; import javax.servlet.ServletContext; import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSessionContext; /** * Facade for the DeltaSession object. * * @author Remy Maucherat * @author Filip Hanik * @version $Revision: 1.1 $ $Date: 2003/11/14 20:07:52 $ */ public class DeltaSessionFacade implements HttpSession { // ----------------------------------------------------------- Constructors /** * Construct a new session facade. */ public DeltaSessionFacade(DeltaSession session) { super(); this.session = (HttpSession) session; } /** * Construct a new session facade. */ public DeltaSessionFacade(HttpSession session) { super(); this.session = session; } // ----------------------------------------------------- Instance Variables /** * Wrapped session object. */ private HttpSession session = null; // ---------------------------------------------------- HttpSession Methods public long getCreationTime() { return session.getCreationTime(); } public String getId() { return session.getId(); } public long getLastAccessedTime() { return session.getLastAccessedTime(); } public ServletContext getServletContext() { // FIXME : Facade this object ? return session.getServletContext(); } public void setMaxInactiveInterval(int interval) { session.setMaxInactiveInterval(interval); } public int getMaxInactiveInterval() { return session.getMaxInactiveInterval(); } public HttpSessionContext getSessionContext() { return session.getSessionContext(); } public Object getAttribute(String name) { return session.getAttribute(name); } public Object getValue(String name) { return session.getAttribute(name); } public Enumeration getAttributeNames() { return session.getAttributeNames(); } public String[] getValueNames() { return session.getValueNames(); } public void setAttribute(String name, Object value) { session.setAttribute(name, value); } public void putValue(String name, Object value) { session.setAttribute(name, value); } public void removeAttribute(String name) { session.removeAttribute(name); } public void removeValue(String name) { session.removeAttribute(name); } public void invalidate() { session.invalidate(); } public boolean isNew() { return session.isNew(); } } --------------------------------------------------------------------- To unsubscribe, e-mail: tomcat-dev-unsubscribe@jakarta.apache.org For additional commands, e-mail: tomcat-dev-help@jakarta.apache.org