/* * Copyright 1999,2004 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.catalina.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.Globals; 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; /** * Standard implementation of the Session interface. This object is * serializable, so that it can be stored in persistent storage or transferred * to a different JVM for distributable session support. *

* 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 Craig R. McClanahan * @author Sean Legassick * @author Jon S. Stevens * @version $Revision: 1.43 $ $Date: 2004/04/01 20:18:15 $ */ public class StandardSession 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 StandardSession(Manager manager) { super(); this.manager = manager; if (manager instanceof ManagerBase) this.debug = ((ManagerBase) manager).getDebug(); } // ----------------------------------------------------- Instance Variables /** * Type array. */ protected static final String EMPTY_ARRAY[] = new String[0]; /** * The dummy attribute value serialized when a NotSerializableException is * encountered in writeObject(). */ protected static final String NOT_SERIALIZED = "___NOT_SERIALIZABLE_EXCEPTION___"; /** * The collection of user data attributes associated with this Session. */ protected 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. */ protected 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). */ protected transient Method containerEventMethod = null; /** * The method signature for the fireContainerEvent method. */ protected static final Class containerEventTypes[] = { String.class, Object.class }; /** * The time this session was created, in milliseconds since midnight, * January 1, 1970 GMT. */ protected long creationTime = 0L; /** * The debugging detail level for this component. NOTE: This value * is not included in the serialized version of this object. */ protected transient int debug = 0; /** * Set of attribute names which are not allowed to be persisted. */ private static final String[] excludedAttributes = { Globals.SUBJECT_ATTR }; /** * 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. */ protected transient boolean expiring = false; /** * The facade associated with this session. NOTE: This value is not * included in the serialized version of this object. */ protected transient StandardSessionFacade facade = null; /** * The session identifier of this Session. */ protected String id = null; /** * Descriptive information describing this Session implementation. */ protected static final String info = "StandardSession/1.0"; /** * The last accessed time for this Session. */ protected long lastAccessedTime = creationTime; /** * The session event listeners for this Session. */ protected transient ArrayList listeners = new ArrayList(); /** * The Manager with which this Session is associated. */ protected transient 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. */ protected int maxInactiveInterval = -1; /** * Flag indicating whether this session is new or not. */ protected boolean isNew = false; /** * Flag indicating whether this session is valid or not. */ protected 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! */ protected 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! */ protected transient Principal principal = null; /** * The string manager for this package. */ protected static StringManager sm = StringManager.getManager(Constants.Package); /** * The HTTP session context associated with this session. */ protected static HttpSessionContext sessionContext = null; /** * The property change support for this component. NOTE: This value * is not included in the serialized version of this object. */ protected transient PropertyChangeSupport support = new PropertyChangeSupport(this); /** * The current accessed time for this session. */ protected long thisAccessedTime = creationTime; /** * The access count for this session. */ protected transient int accessCount = 0; // ----------------------------------------------------- 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) { ; } 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 (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.ise")); } 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 StandardSession fsession = this; facade = (StandardSessionFacade)AccessController.doPrivileged(new PrivilegedAction(){ public Object run(){ return new StandardSessionFacade(fsession); } }); } else { facade = new StandardSessionFacade(this); } } return (facade); } /** * Return the isValid flag for this session. */ public boolean isValid() { if (this.expiring) { return true; } if (!this.isValid ) { return false; } if (accessCount > 0) { return true; } if (maxInactiveInterval >= 0) { long timeNow = System.currentTimeMillis(); int timeIdle = (int) ((timeNow - thisAccessedTime) / 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.lastAccessedTime = this.thisAccessedTime; this.thisAccessedTime = System.currentTimeMillis(); evaluateIfValid(); accessCount++; } /** * End the access. */ public void endAccess() { isNew = false; accessCount--; } /** * Add a session event listener to this component. */ public void addSessionListener(SessionListener listener) { 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) { ; } log(sm.getString("standardSession.sessionEvent"), t); } } } accessCount = 0; setValid(false); // Remove this session from our manager's active sessions if (manager != null) manager.remove(this); // Notify interested session event listeners if (notify) { fireSessionEvent(Session.SESSION_DESTROYED_EVENT, null); } // We have completed expire of this session expiring = false; // Unbind any objects associated with this session String keys[] = keys(); for (int i = 0; i < keys.length; i++) removeAttributeInternal(keys[i], notify); } } /** * 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); try { ((HttpSessionActivationListener)attribute) .sessionWillPassivate(event); } catch (Throwable t) { log(sm.getString("standardSession.attributeEvent"), t); } } } } /** * 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); try { ((HttpSessionActivationListener)attribute) .sessionDidActivate(event); } catch (Throwable t) { log(sm.getString("standardSession.attributeEvent"), t); } } } } /** * 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) { return (notes.get(name)); } /** * Return an Iterator containing the String names of all notes bindings * that exist for this session. */ public Iterator getNoteNames() { 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; accessCount = 0; 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) { notes.remove(name); } /** * Remove a session event listener from this component. */ public void removeSessionListener(SessionListener listener) { 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) { 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")); Object value; synchronized(attributes){ value = attributes.get(name); } return value; } /** * 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")); Enumeration returnValue; synchronized(attributes){ returnValue = new Enumerator(attributes.keySet(), true); } return returnValue; } /** * 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")); removeAttributeInternal(name, notify); } /** * 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")); // Construct an event with the new value HttpSessionBindingEvent event = null; // Call the valueBound() method if necessary if (value instanceof HttpSessionBindingListener) { event = new HttpSessionBindingEvent(this, name, value); try { ((HttpSessionBindingListener) value).valueBound(event); } catch (Throwable t){ log(sm.getString("standardSession.bindingEvent"), t); } } // Replace or add this attribute Object unbound ; synchronized (attributes) { unbound = attributes.put(name, value); } // Call the valueUnbound() method if necessary if ((unbound != null) && (unbound instanceof HttpSessionBindingListener)) { try { ((HttpSessionBindingListener) unbound).valueUnbound (new HttpSessionBindingEvent(this, name)); } catch (Throwable t) { log(sm.getString("standardSession.bindingEvent"), t); } } // 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); if (event == null) { event = new HttpSessionBindingEvent (this, name, unbound); } listener.attributeReplaced(event); fireContainerEvent(context, "afterSessionAttributeReplaced", listener); } else { fireContainerEvent(context, "beforeSessionAttributeAdded", listener); if (event == null) { event = new HttpSessionBindingEvent(this, name, value); } 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) { ; } log(sm.getString("standardSession.attributeEvent"), t); } } } // ------------------------------------------ HttpSession Protected 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 */ protected 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 */ protected 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) && (!exclude(keys[i]) )) { saveNames.add(keys[i]); saveValues.add(value); } else { removeAttribute(keys[i]); } } // 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"); } } } /** * Exclude attribute that cannot be serialized. * @param name the attribute's name */ protected boolean exclude(String name){ for (int i = 0; i < excludedAttributes.length; i++) { if (name.equalsIgnoreCase(excludedAttributes[i])) return true; } return false; } protected 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; isValid(); } // ------------------------------------------------------ Protected 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 */ protected 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. */ protected String[] keys() { synchronized (attributes) { return ((String[]) attributes.keySet().toArray(EMPTY_ARRAY)); } } /** * Return the value of an attribute without a check for validity. */ protected Object getAttributeInternal(String name) { Object value ; synchronized(attributes){ value = attributes.get(name); } return 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. * @param notify Should we notify interested listeners that this * attribute is being removed? */ protected void removeAttributeInternal(String name, boolean notify) { // Remove this attribute from our collection Object value ; synchronized(attributes) { value = attributes.remove(name); } // Do we need to do valueUnbound() and attributeRemoved() notification? if (!notify || (value == null)) { return; } // Call the valueUnbound() method if necessary HttpSessionBindingEvent event = null; if (value instanceof HttpSessionBindingListener) { event = new HttpSessionBindingEvent(this, name, value); ((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); if (event == null) { event = new HttpSessionBindingEvent(this, name, value); } listener.attributeRemoved(event); fireContainerEvent(context, "afterSessionAttributeRemoved", listener); } catch (Throwable t) { try { fireContainerEvent(context, "afterSessionAttributeRemoved", listener); } catch (Exception e) { ; } log(sm.getString("standardSession.attributeEvent"), t); } } } /** * 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)) { ((ManagerBase) manager).log(message); } else { System.out.println("StandardSession: " + 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("StandardSession: " + message); throwable.printStackTrace(System.out); } } } // ------------------------------------------------------------ Protected 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 { protected 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); } }