0001: /*
0002: * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/session/StandardSession.java,v 1.31 2002/07/23 12:49:11 remm Exp $
0003: * $Revision: 1.31 $
0004: * $Date: 2002/07/23 12:49:11 $
0005: *
0006: * ====================================================================
0007: *
0008: * The Apache Software License, Version 1.1
0009: *
0010: * Copyright (c) 1999 The Apache Software Foundation. All rights
0011: * reserved.
0012: *
0013: * Redistribution and use in source and binary forms, with or without
0014: * modification, are permitted provided that the following conditions
0015: * are met:
0016: *
0017: * 1. Redistributions of source code must retain the above copyright
0018: * notice, this list of conditions and the following disclaimer.
0019: *
0020: * 2. Redistributions in binary form must reproduce the above copyright
0021: * notice, this list of conditions and the following disclaimer in
0022: * the documentation and/or other materials provided with the
0023: * distribution.
0024: *
0025: * 3. The end-user documentation included with the redistribution, if
0026: * any, must include the following acknowlegement:
0027: * "This product includes software developed by the
0028: * Apache Software Foundation (http://www.apache.org/)."
0029: * Alternately, this acknowlegement may appear in the software itself,
0030: * if and wherever such third-party acknowlegements normally appear.
0031: *
0032: * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
0033: * Foundation" must not be used to endorse or promote products derived
0034: * from this software without prior written permission. For written
0035: * permission, please contact apache@apache.org.
0036: *
0037: * 5. Products derived from this software may not be called "Apache"
0038: * nor may "Apache" appear in their names without prior written
0039: * permission of the Apache Group.
0040: *
0041: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
0042: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0043: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
0044: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
0045: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
0046: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
0047: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
0048: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
0049: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
0050: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
0051: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
0052: * SUCH DAMAGE.
0053: * ====================================================================
0054: *
0055: * This software consists of voluntary contributions made by many
0056: * individuals on behalf of the Apache Software Foundation. For more
0057: * information on the Apache Software Foundation, please see
0058: * <http://www.apache.org/>.
0059: *
0060: * [Additional notices, if required by prior licensing conditions]
0061: *
0062: */
0063:
0064: package org.apache.catalina.session;
0065:
0066: import java.beans.PropertyChangeListener;
0067: import java.beans.PropertyChangeSupport;
0068: import java.io.IOException;
0069: import java.io.NotSerializableException;
0070: import java.io.ObjectInputStream;
0071: import java.io.ObjectOutputStream;
0072: import java.io.Serializable;
0073: import java.lang.reflect.Method;
0074: import java.security.Principal;
0075: import java.util.ArrayList;
0076: import java.util.Enumeration;
0077: import java.util.HashMap;
0078: import java.util.Iterator;
0079: import javax.servlet.ServletContext;
0080: import javax.servlet.ServletException;
0081: import javax.servlet.http.HttpSession;
0082: import javax.servlet.http.HttpSessionActivationListener;
0083: import javax.servlet.http.HttpSessionAttributeListener;
0084: import javax.servlet.http.HttpSessionBindingEvent;
0085: import javax.servlet.http.HttpSessionBindingListener;
0086: import javax.servlet.http.HttpSessionContext;
0087: import javax.servlet.http.HttpSessionEvent;
0088: import javax.servlet.http.HttpSessionListener;
0089: import org.apache.catalina.Context;
0090: import org.apache.catalina.Manager;
0091: import org.apache.catalina.Session;
0092: import org.apache.catalina.SessionEvent;
0093: import org.apache.catalina.SessionListener;
0094: import org.apache.catalina.util.Enumerator;
0095: import org.apache.catalina.util.StringManager;
0096:
0097: /**
0098: * Standard implementation of the <b>Session</b> interface. This object is
0099: * serializable, so that it can be stored in persistent storage or transferred
0100: * to a different JVM for distributable session support.
0101: * <p>
0102: * <b>IMPLEMENTATION NOTE</b>: An instance of this class represents both the
0103: * internal (Session) and application level (HttpSession) view of the session.
0104: * However, because the class itself is not declared public, Java logic outside
0105: * of the <code>org.apache.catalina.session</code> package cannot cast an
0106: * HttpSession view of this instance back to a Session view.
0107: * <p>
0108: * <b>IMPLEMENTATION NOTE</b>: If you add fields to this class, you must
0109: * make sure that you carry them over in the read/writeObject methods so
0110: * that this class is properly serialized.
0111: *
0112: * @author Craig R. McClanahan
0113: * @author Sean Legassick
0114: * @author <a href="mailto:jon@latchkey.com">Jon S. Stevens</a>
0115: * @version $Revision: 1.31 $ $Date: 2002/07/23 12:49:11 $
0116: */
0117:
0118: class StandardSession implements HttpSession, Session, Serializable {
0119:
0120: // ----------------------------------------------------------- Constructors
0121:
0122: /**
0123: * Construct a new Session associated with the specified Manager.
0124: *
0125: * @param manager The manager with which this Session is associated
0126: */
0127: public StandardSession(Manager manager) {
0128:
0129: super ();
0130: this .manager = manager;
0131: if (manager instanceof ManagerBase)
0132: this .debug = ((ManagerBase) manager).getDebug();
0133:
0134: }
0135:
0136: // ----------------------------------------------------- Instance Variables
0137:
0138: /**
0139: * The dummy attribute value serialized when a NotSerializableException is
0140: * encountered in <code>writeObject()</code>.
0141: */
0142: private static final String NOT_SERIALIZED = "___NOT_SERIALIZABLE_EXCEPTION___";
0143:
0144: /**
0145: * The collection of user data attributes associated with this Session.
0146: */
0147: private HashMap attributes = new HashMap();
0148:
0149: /**
0150: * The authentication type used to authenticate our cached Principal,
0151: * if any. NOTE: This value is not included in the serialized
0152: * version of this object.
0153: */
0154: private transient String authType = null;
0155:
0156: /**
0157: * The <code>java.lang.Method</code> for the
0158: * <code>fireContainerEvent()</code> method of the
0159: * <code>org.apache.catalina.core.StandardContext</code> method,
0160: * if our Context implementation is of this class. This value is
0161: * computed dynamically the first time it is needed, or after
0162: * a session reload (since it is declared transient).
0163: */
0164: private transient Method containerEventMethod = null;
0165:
0166: /**
0167: * The method signature for the <code>fireContainerEvent</code> method.
0168: */
0169: private static final Class containerEventTypes[] = { String.class,
0170: Object.class };
0171:
0172: /**
0173: * The time this session was created, in milliseconds since midnight,
0174: * January 1, 1970 GMT.
0175: */
0176: private long creationTime = 0L;
0177:
0178: /**
0179: * The debugging detail level for this component. NOTE: This value
0180: * is not included in the serialized version of this object.
0181: */
0182: private transient int debug = 0;
0183:
0184: /**
0185: * We are currently processing a session expiration, so bypass
0186: * certain IllegalStateException tests. NOTE: This value is not
0187: * included in the serialized version of this object.
0188: */
0189: private transient boolean expiring = false;
0190:
0191: /**
0192: * The facade associated with this session. NOTE: This value is not
0193: * included in the serialized version of this object.
0194: */
0195: private transient StandardSessionFacade facade = null;
0196:
0197: /**
0198: * The session identifier of this Session.
0199: */
0200: private String id = null;
0201:
0202: /**
0203: * Descriptive information describing this Session implementation.
0204: */
0205: private static final String info = "StandardSession/1.0";
0206:
0207: /**
0208: * The last accessed time for this Session.
0209: */
0210: private long lastAccessedTime = creationTime;
0211:
0212: /**
0213: * The session event listeners for this Session.
0214: */
0215: private transient ArrayList listeners = new ArrayList();
0216:
0217: /**
0218: * The Manager with which this Session is associated.
0219: */
0220: private Manager manager = null;
0221:
0222: /**
0223: * The maximum time interval, in seconds, between client requests before
0224: * the servlet container may invalidate this session. A negative time
0225: * indicates that the session should never time out.
0226: */
0227: private int maxInactiveInterval = -1;
0228:
0229: /**
0230: * Flag indicating whether this session is new or not.
0231: */
0232: private boolean isNew = false;
0233:
0234: /**
0235: * Flag indicating whether this session is valid or not.
0236: */
0237: private boolean isValid = false;
0238:
0239: /**
0240: * Internal notes associated with this session by Catalina components
0241: * and event listeners. <b>IMPLEMENTATION NOTE:</b> This object is
0242: * <em>not</em> saved and restored across session serializations!
0243: */
0244: private transient HashMap notes = new HashMap();
0245:
0246: /**
0247: * The authenticated Principal associated with this session, if any.
0248: * <b>IMPLEMENTATION NOTE:</b> This object is <i>not</i> saved and
0249: * restored across session serializations!
0250: */
0251: private transient Principal principal = null;
0252:
0253: /**
0254: * The string manager for this package.
0255: */
0256: private static StringManager sm = StringManager
0257: .getManager(Constants.Package);
0258:
0259: /**
0260: * The HTTP session context associated with this session.
0261: */
0262: private static HttpSessionContext sessionContext = null;
0263:
0264: /**
0265: * The property change support for this component. NOTE: This value
0266: * is not included in the serialized version of this object.
0267: */
0268: private transient PropertyChangeSupport support = new PropertyChangeSupport(
0269: this );
0270:
0271: /**
0272: * The current accessed time for this session.
0273: */
0274: private long this AccessedTime = creationTime;
0275:
0276: // ----------------------------------------------------- Session Properties
0277:
0278: /**
0279: * Return the authentication type used to authenticate our cached
0280: * Principal, if any.
0281: */
0282: public String getAuthType() {
0283:
0284: return (this .authType);
0285:
0286: }
0287:
0288: /**
0289: * Set the authentication type used to authenticate our cached
0290: * Principal, if any.
0291: *
0292: * @param authType The new cached authentication type
0293: */
0294: public void setAuthType(String authType) {
0295:
0296: String oldAuthType = this .authType;
0297: this .authType = authType;
0298: support.firePropertyChange("authType", oldAuthType,
0299: this .authType);
0300:
0301: }
0302:
0303: /**
0304: * Set the creation time for this session. This method is called by the
0305: * Manager when an existing Session instance is reused.
0306: *
0307: * @param time The new creation time
0308: */
0309: public void setCreationTime(long time) {
0310:
0311: this .creationTime = time;
0312: this .lastAccessedTime = time;
0313: this .this AccessedTime = time;
0314:
0315: }
0316:
0317: /**
0318: * Return the session identifier for this session.
0319: */
0320: public String getId() {
0321:
0322: return (this .id);
0323:
0324: }
0325:
0326: /**
0327: * Set the session identifier for this session.
0328: *
0329: * @param id The new session identifier
0330: */
0331: public void setId(String id) {
0332:
0333: if ((this .id != null) && (manager != null))
0334: manager.remove(this );
0335:
0336: this .id = id;
0337:
0338: if (manager != null)
0339: manager.add(this );
0340:
0341: // Notify interested session event listeners
0342: fireSessionEvent(Session.SESSION_CREATED_EVENT, null);
0343:
0344: // Notify interested application event listeners
0345: Context context = (Context) manager.getContainer();
0346: Object listeners[] = context.getApplicationListeners();
0347: if (listeners != null) {
0348: HttpSessionEvent event = new HttpSessionEvent(getSession());
0349: for (int i = 0; i < listeners.length; i++) {
0350: if (!(listeners[i] instanceof HttpSessionListener))
0351: continue;
0352: HttpSessionListener listener = (HttpSessionListener) listeners[i];
0353: try {
0354: fireContainerEvent(context, "beforeSessionCreated",
0355: listener);
0356: listener.sessionCreated(event);
0357: fireContainerEvent(context, "afterSessionCreated",
0358: listener);
0359: } catch (Throwable t) {
0360: try {
0361: fireContainerEvent(context,
0362: "afterSessionCreated", listener);
0363: } catch (Exception e) {
0364: ;
0365: }
0366: // FIXME - should we do anything besides log these?
0367: log(sm.getString("standardSession.sessionEvent"), t);
0368: }
0369: }
0370: }
0371:
0372: }
0373:
0374: /**
0375: * Return descriptive information about this Session implementation and
0376: * the corresponding version number, in the format
0377: * <code><description>/<version></code>.
0378: */
0379: public String getInfo() {
0380:
0381: return (this .info);
0382:
0383: }
0384:
0385: /**
0386: * Return the last time the client sent a request associated with this
0387: * session, as the number of milliseconds since midnight, January 1, 1970
0388: * GMT. Actions that your application takes, such as getting or setting
0389: * a value associated with the session, do not affect the access time.
0390: */
0391: public long getLastAccessedTime() {
0392:
0393: return (this .lastAccessedTime);
0394:
0395: }
0396:
0397: /**
0398: * Return the Manager within which this Session is valid.
0399: */
0400: public Manager getManager() {
0401:
0402: return (this .manager);
0403:
0404: }
0405:
0406: /**
0407: * Set the Manager within which this Session is valid.
0408: *
0409: * @param manager The new Manager
0410: */
0411: public void setManager(Manager manager) {
0412:
0413: this .manager = manager;
0414:
0415: }
0416:
0417: /**
0418: * Return the maximum time interval, in seconds, between client requests
0419: * before the servlet container will invalidate the session. A negative
0420: * time indicates that the session should never time out.
0421: */
0422: public int getMaxInactiveInterval() {
0423:
0424: return (this .maxInactiveInterval);
0425:
0426: }
0427:
0428: /**
0429: * Set the maximum time interval, in seconds, between client requests
0430: * before the servlet container will invalidate the session. A negative
0431: * time indicates that the session should never time out.
0432: *
0433: * @param interval The new maximum interval
0434: */
0435: public void setMaxInactiveInterval(int interval) {
0436:
0437: this .maxInactiveInterval = interval;
0438:
0439: }
0440:
0441: /**
0442: * Set the <code>isNew</code> flag for this session.
0443: *
0444: * @param isNew The new value for the <code>isNew</code> flag
0445: */
0446: public void setNew(boolean isNew) {
0447:
0448: this .isNew = isNew;
0449:
0450: }
0451:
0452: /**
0453: * Return the authenticated Principal that is associated with this Session.
0454: * This provides an <code>Authenticator</code> with a means to cache a
0455: * previously authenticated Principal, and avoid potentially expensive
0456: * <code>Realm.authenticate()</code> calls on every request. If there
0457: * is no current associated Principal, return <code>null</code>.
0458: */
0459: public Principal getPrincipal() {
0460:
0461: return (this .principal);
0462:
0463: }
0464:
0465: /**
0466: * Set the authenticated Principal that is associated with this Session.
0467: * This provides an <code>Authenticator</code> with a means to cache a
0468: * previously authenticated Principal, and avoid potentially expensive
0469: * <code>Realm.authenticate()</code> calls on every request.
0470: *
0471: * @param principal The new Principal, or <code>null</code> if none
0472: */
0473: public void setPrincipal(Principal principal) {
0474:
0475: Principal oldPrincipal = this .principal;
0476: this .principal = principal;
0477: support.firePropertyChange("principal", oldPrincipal,
0478: this .principal);
0479:
0480: }
0481:
0482: /**
0483: * Return the <code>HttpSession</code> for which this object
0484: * is the facade.
0485: */
0486: public HttpSession getSession() {
0487:
0488: if (facade == null)
0489: facade = new StandardSessionFacade(this );
0490: return (facade);
0491:
0492: }
0493:
0494: /**
0495: * Return the <code>isValid</code> flag for this session.
0496: */
0497: public boolean isValid() {
0498:
0499: return (this .isValid);
0500:
0501: }
0502:
0503: /**
0504: * Set the <code>isValid</code> flag for this session.
0505: *
0506: * @param isValid The new value for the <code>isValid</code> flag
0507: */
0508: public void setValid(boolean isValid) {
0509:
0510: this .isValid = isValid;
0511: }
0512:
0513: // ------------------------------------------------- Session Public Methods
0514:
0515: /**
0516: * Update the accessed time information for this session. This method
0517: * should be called by the context when a request comes in for a particular
0518: * session, even if the application does not reference it.
0519: */
0520: public void access() {
0521:
0522: this .isNew = false;
0523: this .lastAccessedTime = this .this AccessedTime;
0524: this .this AccessedTime = System.currentTimeMillis();
0525:
0526: }
0527:
0528: /**
0529: * Add a session event listener to this component.
0530: */
0531: public void addSessionListener(SessionListener listener) {
0532:
0533: synchronized (listeners) {
0534: listeners.add(listener);
0535: }
0536:
0537: }
0538:
0539: /**
0540: * Perform the internal processing required to invalidate this session,
0541: * without triggering an exception if the session has already expired.
0542: */
0543: public void expire() {
0544:
0545: expire(true);
0546:
0547: }
0548:
0549: /**
0550: * Perform the internal processing required to invalidate this session,
0551: * without triggering an exception if the session has already expired.
0552: *
0553: * @param notify Should we notify listeners about the demise of
0554: * this session?
0555: */
0556: public void expire(boolean notify) {
0557:
0558: // Mark this session as "being expired" if needed
0559: if (expiring)
0560: return;
0561: expiring = true;
0562: setValid(false);
0563:
0564: // Remove this session from our manager's active sessions
0565: if (manager != null)
0566: manager.remove(this );
0567:
0568: // Unbind any objects associated with this session
0569: String keys[] = keys();
0570: for (int i = 0; i < keys.length; i++)
0571: removeAttribute(keys[i], notify);
0572:
0573: // Notify interested session event listeners
0574: if (notify) {
0575: fireSessionEvent(Session.SESSION_DESTROYED_EVENT, null);
0576: }
0577:
0578: // Notify interested application event listeners
0579: // FIXME - Assumes we call listeners in reverse order
0580: Context context = (Context) manager.getContainer();
0581: Object listeners[] = context.getApplicationListeners();
0582: if (notify && (listeners != null)) {
0583: HttpSessionEvent event = new HttpSessionEvent(getSession());
0584: for (int i = 0; i < listeners.length; i++) {
0585: int j = (listeners.length - 1) - i;
0586: if (!(listeners[j] instanceof HttpSessionListener))
0587: continue;
0588: HttpSessionListener listener = (HttpSessionListener) listeners[j];
0589: try {
0590: fireContainerEvent(context,
0591: "beforeSessionDestroyed", listener);
0592: listener.sessionDestroyed(event);
0593: fireContainerEvent(context,
0594: "afterSessionDestroyed", listener);
0595: } catch (Throwable t) {
0596: try {
0597: fireContainerEvent(context,
0598: "afterSessionDestroyed", listener);
0599: } catch (Exception e) {
0600: ;
0601: }
0602: // FIXME - should we do anything besides log these?
0603: log(sm.getString("standardSession.sessionEvent"), t);
0604: }
0605: }
0606: }
0607:
0608: // We have completed expire of this session
0609: expiring = false;
0610: if ((manager != null) && (manager instanceof ManagerBase)) {
0611: recycle();
0612: }
0613:
0614: }
0615:
0616: /**
0617: * Perform the internal processing required to passivate
0618: * this session.
0619: */
0620: public void passivate() {
0621:
0622: // Notify ActivationListeners
0623: HttpSessionEvent event = null;
0624: String keys[] = keys();
0625: for (int i = 0; i < keys.length; i++) {
0626: Object attribute = getAttribute(keys[i]);
0627: if (attribute instanceof HttpSessionActivationListener) {
0628: if (event == null)
0629: event = new HttpSessionEvent(this );
0630: // FIXME: Should we catch throwables?
0631: ((HttpSessionActivationListener) attribute)
0632: .sessionWillPassivate(event);
0633: }
0634: }
0635:
0636: }
0637:
0638: /**
0639: * Perform internal processing required to activate this
0640: * session.
0641: */
0642: public void activate() {
0643:
0644: // Notify ActivationListeners
0645: HttpSessionEvent event = null;
0646: String keys[] = keys();
0647: for (int i = 0; i < keys.length; i++) {
0648: Object attribute = getAttribute(keys[i]);
0649: if (attribute instanceof HttpSessionActivationListener) {
0650: if (event == null)
0651: event = new HttpSessionEvent(this );
0652: // FIXME: Should we catch throwables?
0653: ((HttpSessionActivationListener) attribute)
0654: .sessionDidActivate(event);
0655: }
0656: }
0657:
0658: }
0659:
0660: /**
0661: * Return the object bound with the specified name to the internal notes
0662: * for this session, or <code>null</code> if no such binding exists.
0663: *
0664: * @param name Name of the note to be returned
0665: */
0666: public Object getNote(String name) {
0667:
0668: synchronized (notes) {
0669: return (notes.get(name));
0670: }
0671:
0672: }
0673:
0674: /**
0675: * Return an Iterator containing the String names of all notes bindings
0676: * that exist for this session.
0677: */
0678: public Iterator getNoteNames() {
0679:
0680: synchronized (notes) {
0681: return (notes.keySet().iterator());
0682: }
0683:
0684: }
0685:
0686: /**
0687: * Release all object references, and initialize instance variables, in
0688: * preparation for reuse of this object.
0689: */
0690: public void recycle() {
0691:
0692: // Reset the instance variables associated with this Session
0693: attributes.clear();
0694: setAuthType(null);
0695: creationTime = 0L;
0696: expiring = false;
0697: id = null;
0698: lastAccessedTime = 0L;
0699: maxInactiveInterval = -1;
0700: notes.clear();
0701: setPrincipal(null);
0702: isNew = false;
0703: isValid = false;
0704: Manager savedManager = manager;
0705: manager = null;
0706:
0707: // Tell our Manager that this Session has been recycled
0708: if ((savedManager != null)
0709: && (savedManager instanceof ManagerBase))
0710: ((ManagerBase) savedManager).recycle(this );
0711:
0712: }
0713:
0714: /**
0715: * Remove any object bound to the specified name in the internal notes
0716: * for this session.
0717: *
0718: * @param name Name of the note to be removed
0719: */
0720: public void removeNote(String name) {
0721:
0722: synchronized (notes) {
0723: notes.remove(name);
0724: }
0725:
0726: }
0727:
0728: /**
0729: * Remove a session event listener from this component.
0730: */
0731: public void removeSessionListener(SessionListener listener) {
0732:
0733: synchronized (listeners) {
0734: listeners.remove(listener);
0735: }
0736:
0737: }
0738:
0739: /**
0740: * Bind an object to a specified name in the internal notes associated
0741: * with this session, replacing any existing binding for this name.
0742: *
0743: * @param name Name to which the object should be bound
0744: * @param value Object to be bound to the specified name
0745: */
0746: public void setNote(String name, Object value) {
0747:
0748: synchronized (notes) {
0749: notes.put(name, value);
0750: }
0751:
0752: }
0753:
0754: /**
0755: * Return a string representation of this object.
0756: */
0757: public String toString() {
0758:
0759: StringBuffer sb = new StringBuffer();
0760: sb.append("StandardSession[");
0761: sb.append(id);
0762: sb.append("]");
0763: return (sb.toString());
0764:
0765: }
0766:
0767: // ------------------------------------------------ Session Package Methods
0768:
0769: /**
0770: * Read a serialized version of the contents of this session object from
0771: * the specified object input stream, without requiring that the
0772: * StandardSession itself have been serialized.
0773: *
0774: * @param stream The object input stream to read from
0775: *
0776: * @exception ClassNotFoundException if an unknown class is specified
0777: * @exception IOException if an input/output error occurs
0778: */
0779: void readObjectData(ObjectInputStream stream)
0780: throws ClassNotFoundException, IOException {
0781:
0782: readObject(stream);
0783:
0784: }
0785:
0786: /**
0787: * Write a serialized version of the contents of this session object to
0788: * the specified object output stream, without requiring that the
0789: * StandardSession itself have been serialized.
0790: *
0791: * @param stream The object output stream to write to
0792: *
0793: * @exception IOException if an input/output error occurs
0794: */
0795: void writeObjectData(ObjectOutputStream stream) throws IOException {
0796:
0797: writeObject(stream);
0798:
0799: }
0800:
0801: // ------------------------------------------------- HttpSession Properties
0802:
0803: /**
0804: * Return the time when this session was created, in milliseconds since
0805: * midnight, January 1, 1970 GMT.
0806: *
0807: * @exception IllegalStateException if this method is called on an
0808: * invalidated session
0809: */
0810: public long getCreationTime() {
0811:
0812: if (!isValid)
0813: throw new IllegalStateException(sm
0814: .getString("standardSession.getCreationTime.ise"));
0815:
0816: return (this .creationTime);
0817:
0818: }
0819:
0820: /**
0821: * Return the ServletContext to which this session belongs.
0822: */
0823: public ServletContext getServletContext() {
0824:
0825: if (manager == null)
0826: return (null);
0827: Context context = (Context) manager.getContainer();
0828: if (context == null)
0829: return (null);
0830: else
0831: return (context.getServletContext());
0832:
0833: }
0834:
0835: /**
0836: * Return the session context with which this session is associated.
0837: *
0838: * @deprecated As of Version 2.1, this method is deprecated and has no
0839: * replacement. It will be removed in a future version of the
0840: * Java Servlet API.
0841: */
0842: public HttpSessionContext getSessionContext() {
0843:
0844: if (sessionContext == null)
0845: sessionContext = new StandardSessionContext();
0846: return (sessionContext);
0847:
0848: }
0849:
0850: // ----------------------------------------------HttpSession Public Methods
0851:
0852: /**
0853: * Return the object bound with the specified name in this session, or
0854: * <code>null</code> if no object is bound with that name.
0855: *
0856: * @param name Name of the attribute to be returned
0857: *
0858: * @exception IllegalStateException if this method is called on an
0859: * invalidated session
0860: */
0861: public Object getAttribute(String name) {
0862:
0863: if (!isValid)
0864: throw new IllegalStateException(sm
0865: .getString("standardSession.getAttribute.ise"));
0866:
0867: synchronized (attributes) {
0868: return (attributes.get(name));
0869: }
0870:
0871: }
0872:
0873: /**
0874: * Return an <code>Enumeration</code> of <code>String</code> objects
0875: * containing the names of the objects bound to this session.
0876: *
0877: * @exception IllegalStateException if this method is called on an
0878: * invalidated session
0879: */
0880: public Enumeration getAttributeNames() {
0881:
0882: if (!isValid)
0883: throw new IllegalStateException(sm
0884: .getString("standardSession.getAttributeNames.ise"));
0885:
0886: synchronized (attributes) {
0887: return (new Enumerator(attributes.keySet()));
0888: }
0889:
0890: }
0891:
0892: /**
0893: * Return the object bound with the specified name in this session, or
0894: * <code>null</code> if no object is bound with that name.
0895: *
0896: * @param name Name of the value to be returned
0897: *
0898: * @exception IllegalStateException if this method is called on an
0899: * invalidated session
0900: *
0901: * @deprecated As of Version 2.2, this method is replaced by
0902: * <code>getAttribute()</code>
0903: */
0904: public Object getValue(String name) {
0905:
0906: return (getAttribute(name));
0907:
0908: }
0909:
0910: /**
0911: * Return the set of names of objects bound to this session. If there
0912: * are no such objects, a zero-length array is returned.
0913: *
0914: * @exception IllegalStateException if this method is called on an
0915: * invalidated session
0916: *
0917: * @deprecated As of Version 2.2, this method is replaced by
0918: * <code>getAttributeNames()</code>
0919: */
0920: public String[] getValueNames() {
0921:
0922: if (!isValid)
0923: throw new IllegalStateException(sm
0924: .getString("standardSession.getValueNames.ise"));
0925:
0926: return (keys());
0927:
0928: }
0929:
0930: /**
0931: * Invalidates this session and unbinds any objects bound to it.
0932: *
0933: * @exception IllegalStateException if this method is called on
0934: * an invalidated session
0935: */
0936: public void invalidate() {
0937:
0938: if (!isValid)
0939: throw new IllegalStateException(sm
0940: .getString("standardSession.invalidate.ise"));
0941:
0942: // Cause this session to expire
0943: expire();
0944:
0945: }
0946:
0947: /**
0948: * Return <code>true</code> if the client does not yet know about the
0949: * session, or if the client chooses not to join the session. For
0950: * example, if the server used only cookie-based sessions, and the client
0951: * has disabled the use of cookies, then a session would be new on each
0952: * request.
0953: *
0954: * @exception IllegalStateException if this method is called on an
0955: * invalidated session
0956: */
0957: public boolean isNew() {
0958:
0959: if (!isValid)
0960: throw new IllegalStateException(sm
0961: .getString("standardSession.isNew.ise"));
0962:
0963: return (this .isNew);
0964:
0965: }
0966:
0967: /**
0968: * Bind an object to this session, using the specified name. If an object
0969: * of the same name is already bound to this session, the object is
0970: * replaced.
0971: * <p>
0972: * After this method executes, and if the object implements
0973: * <code>HttpSessionBindingListener</code>, the container calls
0974: * <code>valueBound()</code> on the object.
0975: *
0976: * @param name Name to which the object is bound, cannot be null
0977: * @param value Object to be bound, cannot be null
0978: *
0979: * @exception IllegalStateException if this method is called on an
0980: * invalidated session
0981: *
0982: * @deprecated As of Version 2.2, this method is replaced by
0983: * <code>setAttribute()</code>
0984: */
0985: public void putValue(String name, Object value) {
0986:
0987: setAttribute(name, value);
0988:
0989: }
0990:
0991: /**
0992: * Remove the object bound with the specified name from this session. If
0993: * the session does not have an object bound with this name, this method
0994: * does nothing.
0995: * <p>
0996: * After this method executes, and if the object implements
0997: * <code>HttpSessionBindingListener</code>, the container calls
0998: * <code>valueUnbound()</code> on the object.
0999: *
1000: * @param name Name of the object to remove from this session.
1001: *
1002: * @exception IllegalStateException if this method is called on an
1003: * invalidated session
1004: */
1005: public void removeAttribute(String name) {
1006:
1007: removeAttribute(name, true);
1008:
1009: }
1010:
1011: /**
1012: * Remove the object bound with the specified name from this session. If
1013: * the session does not have an object bound with this name, this method
1014: * does nothing.
1015: * <p>
1016: * After this method executes, and if the object implements
1017: * <code>HttpSessionBindingListener</code>, the container calls
1018: * <code>valueUnbound()</code> on the object.
1019: *
1020: * @param name Name of the object to remove from this session.
1021: * @param notify Should we notify interested listeners that this
1022: * attribute is being removed?
1023: *
1024: * @exception IllegalStateException if this method is called on an
1025: * invalidated session
1026: */
1027: public void removeAttribute(String name, boolean notify) {
1028:
1029: // Validate our current state
1030: if (!expiring && !isValid)
1031: throw new IllegalStateException(sm
1032: .getString("standardSession.removeAttribute.ise"));
1033:
1034: // Remove this attribute from our collection
1035: Object value = null;
1036: boolean found = false;
1037: synchronized (attributes) {
1038: found = attributes.containsKey(name);
1039: if (found) {
1040: value = attributes.get(name);
1041: attributes.remove(name);
1042: } else {
1043: return;
1044: }
1045: }
1046:
1047: // Do we need to do valueUnbound() and attributeRemoved() notification?
1048: if (!notify) {
1049: return;
1050: }
1051:
1052: // Call the valueUnbound() method if necessary
1053: HttpSessionBindingEvent event = new HttpSessionBindingEvent(
1054: (HttpSession) this , name, value);
1055: if ((value != null)
1056: && (value instanceof HttpSessionBindingListener))
1057: ((HttpSessionBindingListener) value).valueUnbound(event);
1058:
1059: // Notify interested application event listeners
1060: Context context = (Context) manager.getContainer();
1061: Object listeners[] = context.getApplicationListeners();
1062: if (listeners == null)
1063: return;
1064: for (int i = 0; i < listeners.length; i++) {
1065: if (!(listeners[i] instanceof HttpSessionAttributeListener))
1066: continue;
1067: HttpSessionAttributeListener listener = (HttpSessionAttributeListener) listeners[i];
1068: try {
1069: fireContainerEvent(context,
1070: "beforeSessionAttributeRemoved", listener);
1071: listener.attributeRemoved(event);
1072: fireContainerEvent(context,
1073: "afterSessionAttributeRemoved", listener);
1074: } catch (Throwable t) {
1075: try {
1076: fireContainerEvent(context,
1077: "afterSessionAttributeRemoved", listener);
1078: } catch (Exception e) {
1079: ;
1080: }
1081: // FIXME - should we do anything besides log these?
1082: log(sm.getString("standardSession.attributeEvent"), t);
1083: }
1084: }
1085:
1086: }
1087:
1088: /**
1089: * Remove the object bound with the specified name from this session. If
1090: * the session does not have an object bound with this name, this method
1091: * does nothing.
1092: * <p>
1093: * After this method executes, and if the object implements
1094: * <code>HttpSessionBindingListener</code>, the container calls
1095: * <code>valueUnbound()</code> on the object.
1096: *
1097: * @param name Name of the object to remove from this session.
1098: *
1099: * @exception IllegalStateException if this method is called on an
1100: * invalidated session
1101: *
1102: * @deprecated As of Version 2.2, this method is replaced by
1103: * <code>removeAttribute()</code>
1104: */
1105: public void removeValue(String name) {
1106:
1107: removeAttribute(name);
1108:
1109: }
1110:
1111: /**
1112: * Bind an object to this session, using the specified name. If an object
1113: * of the same name is already bound to this session, the object is
1114: * replaced.
1115: * <p>
1116: * After this method executes, and if the object implements
1117: * <code>HttpSessionBindingListener</code>, the container calls
1118: * <code>valueBound()</code> on the object.
1119: *
1120: * @param name Name to which the object is bound, cannot be null
1121: * @param value Object to be bound, cannot be null
1122: *
1123: * @exception IllegalArgumentException if an attempt is made to add a
1124: * non-serializable object in an environment marked distributable.
1125: * @exception IllegalStateException if this method is called on an
1126: * invalidated session
1127: */
1128: public void setAttribute(String name, Object value) {
1129:
1130: // Name cannot be null
1131: if (name == null)
1132: throw new IllegalArgumentException(sm
1133: .getString("standardSession.setAttribute.namenull"));
1134:
1135: // Null value is the same as removeAttribute()
1136: if (value == null) {
1137: removeAttribute(name);
1138: return;
1139: }
1140:
1141: // Validate our current state
1142: if (!isValid)
1143: throw new IllegalStateException(sm
1144: .getString("standardSession.setAttribute.ise"));
1145: if ((manager != null) && manager.getDistributable()
1146: && !(value instanceof Serializable))
1147: throw new IllegalArgumentException(sm
1148: .getString("standardSession.setAttribute.iae"));
1149:
1150: // Replace or add this attribute
1151: Object unbound = null;
1152: synchronized (attributes) {
1153: unbound = attributes.get(name);
1154: attributes.put(name, value);
1155: }
1156:
1157: // Call the valueUnbound() method if necessary
1158: if ((unbound != null)
1159: && (unbound instanceof HttpSessionBindingListener)) {
1160: ((HttpSessionBindingListener) unbound)
1161: .valueUnbound(new HttpSessionBindingEvent(
1162: (HttpSession) this , name));
1163: }
1164:
1165: // Call the valueBound() method if necessary
1166: HttpSessionBindingEvent event = null;
1167: if (unbound != null)
1168: event = new HttpSessionBindingEvent((HttpSession) this ,
1169: name, unbound);
1170: else
1171: event = new HttpSessionBindingEvent((HttpSession) this ,
1172: name, value);
1173: if (value instanceof HttpSessionBindingListener)
1174: ((HttpSessionBindingListener) value).valueBound(event);
1175:
1176: // Notify interested application event listeners
1177: Context context = (Context) manager.getContainer();
1178: Object listeners[] = context.getApplicationListeners();
1179: if (listeners == null)
1180: return;
1181: for (int i = 0; i < listeners.length; i++) {
1182: if (!(listeners[i] instanceof HttpSessionAttributeListener))
1183: continue;
1184: HttpSessionAttributeListener listener = (HttpSessionAttributeListener) listeners[i];
1185: try {
1186: if (unbound != null) {
1187: fireContainerEvent(context,
1188: "beforeSessionAttributeReplaced", listener);
1189: listener.attributeReplaced(event);
1190: fireContainerEvent(context,
1191: "afterSessionAttributeReplaced", listener);
1192: } else {
1193: fireContainerEvent(context,
1194: "beforeSessionAttributeAdded", listener);
1195: listener.attributeAdded(event);
1196: fireContainerEvent(context,
1197: "afterSessionAttributeAdded", listener);
1198: }
1199: } catch (Throwable t) {
1200: try {
1201: if (unbound != null) {
1202: fireContainerEvent(context,
1203: "afterSessionAttributeReplaced",
1204: listener);
1205: } else {
1206: fireContainerEvent(context,
1207: "afterSessionAttributeAdded", listener);
1208: }
1209: } catch (Exception e) {
1210: ;
1211: }
1212: // FIXME - should we do anything besides log these?
1213: log(sm.getString("standardSession.attributeEvent"), t);
1214: }
1215: }
1216:
1217: }
1218:
1219: // -------------------------------------------- HttpSession Private Methods
1220:
1221: /**
1222: * Read a serialized version of this session object from the specified
1223: * object input stream.
1224: * <p>
1225: * <b>IMPLEMENTATION NOTE</b>: The reference to the owning Manager
1226: * is not restored by this method, and must be set explicitly.
1227: *
1228: * @param stream The input stream to read from
1229: *
1230: * @exception ClassNotFoundException if an unknown class is specified
1231: * @exception IOException if an input/output error occurs
1232: */
1233: private void readObject(ObjectInputStream stream)
1234: throws ClassNotFoundException, IOException {
1235:
1236: // Deserialize the scalar instance variables (except Manager)
1237: authType = null; // Transient only
1238: creationTime = ((Long) stream.readObject()).longValue();
1239: lastAccessedTime = ((Long) stream.readObject()).longValue();
1240: maxInactiveInterval = ((Integer) stream.readObject())
1241: .intValue();
1242: isNew = ((Boolean) stream.readObject()).booleanValue();
1243: isValid = ((Boolean) stream.readObject()).booleanValue();
1244: this AccessedTime = ((Long) stream.readObject()).longValue();
1245: principal = null; // Transient only
1246: // setId((String) stream.readObject());
1247: id = (String) stream.readObject();
1248: if (debug >= 2)
1249: log("readObject() loading session " + id);
1250:
1251: // Deserialize the attribute count and attribute values
1252: if (attributes == null)
1253: attributes = new HashMap();
1254: int n = ((Integer) stream.readObject()).intValue();
1255: boolean isValidSave = isValid;
1256: isValid = true;
1257: for (int i = 0; i < n; i++) {
1258: String name = (String) stream.readObject();
1259: Object value = (Object) stream.readObject();
1260: if ((value instanceof String)
1261: && (value.equals(NOT_SERIALIZED)))
1262: continue;
1263: if (debug >= 2)
1264: log(" loading attribute '" + name + "' with value '"
1265: + value + "'");
1266: synchronized (attributes) {
1267: attributes.put(name, value);
1268: }
1269: }
1270: isValid = isValidSave;
1271:
1272: }
1273:
1274: /**
1275: * Write a serialized version of this session object to the specified
1276: * object output stream.
1277: * <p>
1278: * <b>IMPLEMENTATION NOTE</b>: The owning Manager will not be stored
1279: * in the serialized representation of this Session. After calling
1280: * <code>readObject()</code>, you must set the associated Manager
1281: * explicitly.
1282: * <p>
1283: * <b>IMPLEMENTATION NOTE</b>: Any attribute that is not Serializable
1284: * will be unbound from the session, with appropriate actions if it
1285: * implements HttpSessionBindingListener. If you do not want any such
1286: * attributes, be sure the <code>distributable</code> property of the
1287: * associated Manager is set to <code>true</code>.
1288: *
1289: * @param stream The output stream to write to
1290: *
1291: * @exception IOException if an input/output error occurs
1292: */
1293: private void writeObject(ObjectOutputStream stream)
1294: throws IOException {
1295:
1296: // Write the scalar instance variables (except Manager)
1297: stream.writeObject(new Long(creationTime));
1298: stream.writeObject(new Long(lastAccessedTime));
1299: stream.writeObject(new Integer(maxInactiveInterval));
1300: stream.writeObject(new Boolean(isNew));
1301: stream.writeObject(new Boolean(isValid));
1302: stream.writeObject(new Long(this AccessedTime));
1303: stream.writeObject(id);
1304: if (debug >= 2)
1305: log("writeObject() storing session " + id);
1306:
1307: // Accumulate the names of serializable and non-serializable attributes
1308: String keys[] = keys();
1309: ArrayList saveNames = new ArrayList();
1310: ArrayList saveValues = new ArrayList();
1311: for (int i = 0; i < keys.length; i++) {
1312: Object value = null;
1313: synchronized (attributes) {
1314: value = attributes.get(keys[i]);
1315: }
1316: if (value == null)
1317: continue;
1318: else if (value instanceof Serializable) {
1319: saveNames.add(keys[i]);
1320: saveValues.add(value);
1321: }
1322: }
1323:
1324: // Serialize the attribute count and the Serializable attributes
1325: int n = saveNames.size();
1326: stream.writeObject(new Integer(n));
1327: for (int i = 0; i < n; i++) {
1328: stream.writeObject((String) saveNames.get(i));
1329: try {
1330: stream.writeObject(saveValues.get(i));
1331: if (debug >= 2)
1332: log(" storing attribute '" + saveNames.get(i)
1333: + "' with value '" + saveValues.get(i)
1334: + "'");
1335: } catch (NotSerializableException e) {
1336: log(sm.getString("standardSession.notSerializable",
1337: saveNames.get(i), id), e);
1338: stream.writeObject(NOT_SERIALIZED);
1339: if (debug >= 2)
1340: log(" storing attribute '" + saveNames.get(i)
1341: + "' with value NOT_SERIALIZED");
1342: }
1343: }
1344:
1345: }
1346:
1347: // -------------------------------------------------------- Private Methods
1348:
1349: /**
1350: * Fire container events if the Context implementation is the
1351: * <code>org.apache.catalina.core.StandardContext</code>.
1352: *
1353: * @param context Context for which to fire events
1354: * @param type Event type
1355: * @param data Event data
1356: *
1357: * @exception Exception occurred during event firing
1358: */
1359: private void fireContainerEvent(Context context, String type,
1360: Object data) throws Exception {
1361:
1362: if (!"org.apache.catalina.core.StandardContext".equals(context
1363: .getClass().getName())) {
1364: return; // Container events are not supported
1365: }
1366: // NOTE: Race condition is harmless, so do not synchronize
1367: if (containerEventMethod == null) {
1368: containerEventMethod = context.getClass().getMethod(
1369: "fireContainerEvent", containerEventTypes);
1370: }
1371: Object containerEventParams[] = new Object[2];
1372: containerEventParams[0] = type;
1373: containerEventParams[1] = data;
1374: containerEventMethod.invoke(context, containerEventParams);
1375:
1376: }
1377:
1378: /**
1379: * Notify all session event listeners that a particular event has
1380: * occurred for this Session. The default implementation performs
1381: * this notification synchronously using the calling thread.
1382: *
1383: * @param type Event type
1384: * @param data Event data
1385: */
1386: public void fireSessionEvent(String type, Object data) {
1387:
1388: if (listeners.size() < 1)
1389: return;
1390: SessionEvent event = new SessionEvent(this , type, data);
1391: SessionListener list[] = new SessionListener[0];
1392: synchronized (listeners) {
1393: list = (SessionListener[]) listeners.toArray(list);
1394: }
1395: for (int i = 0; i < list.length; i++)
1396: ((SessionListener) list[i]).sessionEvent(event);
1397:
1398: }
1399:
1400: /**
1401: * Return the names of all currently defined session attributes
1402: * as an array of Strings. If there are no defined attributes, a
1403: * zero-length array is returned.
1404: */
1405: private String[] keys() {
1406:
1407: String results[] = new String[0];
1408: synchronized (attributes) {
1409: return ((String[]) attributes.keySet().toArray(results));
1410: }
1411:
1412: }
1413:
1414: /**
1415: * Log a message on the Logger associated with our Manager (if any).
1416: *
1417: * @param message Message to be logged
1418: */
1419: private void log(String message) {
1420:
1421: if ((manager != null) && (manager instanceof ManagerBase)) {
1422: ((ManagerBase) manager).log(message);
1423: } else {
1424: System.out.println("StandardSession: " + message);
1425: }
1426:
1427: }
1428:
1429: /**
1430: * Log a message on the Logger associated with our Manager (if any).
1431: *
1432: * @param message Message to be logged
1433: * @param throwable Associated exception
1434: */
1435: private void log(String message, Throwable throwable) {
1436:
1437: if ((manager != null) && (manager instanceof ManagerBase)) {
1438: ((ManagerBase) manager).log(message, throwable);
1439: } else {
1440: System.out.println("StandardSession: " + message);
1441: throwable.printStackTrace(System.out);
1442: }
1443:
1444: }
1445:
1446: }
1447:
1448: // -------------------------------------------------------------- Private Class
1449:
1450: /**
1451: * This class is a dummy implementation of the <code>HttpSessionContext</code>
1452: * interface, to conform to the requirement that such an object be returned
1453: * when <code>HttpSession.getSessionContext()</code> is called.
1454: *
1455: * @author Craig R. McClanahan
1456: *
1457: * @deprecated As of Java Servlet API 2.1 with no replacement. The
1458: * interface will be removed in a future version of this API.
1459: */
1460:
1461: final class StandardSessionContext implements HttpSessionContext {
1462:
1463: private HashMap dummy = new HashMap();
1464:
1465: /**
1466: * Return the session identifiers of all sessions defined
1467: * within this context.
1468: *
1469: * @deprecated As of Java Servlet API 2.1 with no replacement.
1470: * This method must return an empty <code>Enumeration</code>
1471: * and will be removed in a future version of the API.
1472: */
1473: public Enumeration getIds() {
1474:
1475: return (new Enumerator(dummy));
1476:
1477: }
1478:
1479: /**
1480: * Return the <code>HttpSession</code> associated with the
1481: * specified session identifier.
1482: *
1483: * @param id Session identifier for which to look up a session
1484: *
1485: * @deprecated As of Java Servlet API 2.1 with no replacement.
1486: * This method must return null and will be removed in a
1487: * future version of the API.
1488: */
1489: public HttpSession getSession(String id) {
1490:
1491: return (null);
1492:
1493: }
1494:
1495: }
|