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