0001: /*
0002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
0003: *
0004: * This file is part of Resin(R) Open Source
0005: *
0006: * Each copy or derived work must preserve the copyright notice and this
0007: * notice unmodified.
0008: *
0009: * Resin Open Source is free software; you can redistribute it and/or modify
0010: * it under the terms of the GNU General Public License as published by
0011: * the Free Software Foundation; either version 2 of the License, or
0012: * (at your option) any later version.
0013: *
0014: * Resin Open Source is distributed in the hope that it will be useful,
0015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
0017: * of NON-INFRINGEMENT. See the GNU General Public License for more
0018: * details.
0019: *
0020: * You should have received a copy of the GNU General Public License
0021: * along with Resin Open Source; if not, write to the
0022: *
0023: * Free Software Foundation, Inc.
0024: * 59 Temple Place, Suite 330
0025: * Boston, MA 02111-1307 USA
0026: *
0027: * @author Scott Ferguson
0028: */
0029:
0030: package com.caucho.server.session;
0031:
0032: import com.caucho.hessian.io.*;
0033: import com.caucho.server.webapp.WebApp;
0034: import com.caucho.server.cluster.ClusterObject;
0035: import com.caucho.server.cluster.Store;
0036: import com.caucho.server.security.AbstractAuthenticator;
0037: import com.caucho.server.security.ServletAuthenticator;
0038: import com.caucho.util.Alarm;
0039: import com.caucho.util.CacheListener;
0040: import com.caucho.util.L10N;
0041: import com.caucho.vfs.IOExceptionWrapper;
0042:
0043: import javax.servlet.ServletContext;
0044: import javax.servlet.http.*;
0045: import java.io.*;
0046: import java.security.Principal;
0047: import java.util.*;
0048: import java.util.logging.Level;
0049: import java.util.logging.Logger;
0050:
0051: /**
0052: * Implements a HTTP session.
0053: */
0054: public class SessionImpl implements HttpSession, CacheListener {
0055: static protected final Logger log = Logger
0056: .getLogger(SessionImpl.class.getName());
0057: static final L10N L = new L10N(SessionImpl.class);
0058:
0059: static final String LOGIN = "caucho.login";
0060:
0061: // the session's identifier
0062: private String _id;
0063:
0064: // the owning session manager
0065: protected SessionManager _manager;
0066: // the session store
0067:
0068: // Map containing the actual values.
0069: protected Map<String, Object> _values;
0070:
0071: // time the session was created
0072: private long _creationTime;
0073: // time the session was last accessed
0074: long _accessTime;
0075: // maximum time the session may stay alive.
0076: long _maxInactiveInterval;
0077: // true if the session is new
0078: private boolean _isNew = true;
0079: // true if the session is still valid, i.e. not invalidated
0080: boolean _isValid = true;
0081: // true if the session is closing
0082: boolean _isClosing = false;
0083: // true if the session is being closed from an invalidation
0084: boolean _isInvalidating = false;
0085: //To protect for threading
0086: private int _useCount;
0087:
0088: private ClusterObject _clusterObject;
0089: // The logged-in user
0090: private Principal _user;
0091:
0092: // index of the owning srun
0093: private int _srunIndex = -1;
0094:
0095: /**
0096: * Create a new session object.
0097: *
0098: * @param manager the owning session manager.
0099: * @param id the session identifier.
0100: * @param creationTime the time in milliseconds when the session was created.
0101: */
0102: public SessionImpl(SessionManager manager, String id,
0103: long creationTime) {
0104: _manager = manager;
0105:
0106: _creationTime = creationTime;
0107: _accessTime = creationTime;
0108: _maxInactiveInterval = manager.getSessionTimeout();
0109:
0110: _id = id;
0111:
0112: // Finds the owning JVM from the session encoding
0113: char ch = id.charAt(0);
0114: int length = manager.getSrunLength();
0115:
0116: if (length > 0)
0117: _srunIndex = SessionManager.decode(ch) % length;
0118: else
0119: _srunIndex = 0;
0120:
0121: _values = createValueMap();
0122:
0123: if (log.isLoggable(Level.FINE))
0124: log.fine(this + " new");
0125: }
0126:
0127: /**
0128: * Returns the time the session was created.
0129: */
0130: public long getCreationTime() {
0131: // this test forced by TCK
0132: if (!_isValid)
0133: throw new IllegalStateException(
0134: L
0135: .l(
0136: "{0}: can't call getCreationTime() when session is no longer valid.",
0137: this ));
0138:
0139: return _creationTime;
0140: }
0141:
0142: /**
0143: * Returns the session identifier.
0144: */
0145: public String getId() {
0146: return _id;
0147: }
0148:
0149: /**
0150: * Returns the index of the owning srun for distributed sessions.
0151: */
0152: int getSrunIndex() {
0153: return _srunIndex;
0154: }
0155:
0156: /**
0157: * Sets the cluster object.
0158: */
0159: void setClusterObject(ClusterObject clusterObject) {
0160: _clusterObject = clusterObject;
0161: if (clusterObject != null)
0162: clusterObject.update();
0163: }
0164:
0165: /**
0166: * Returns the last access time.
0167: */
0168: public long getLastAccessedTime() {
0169: // this test forced by TCK
0170: if (!_isValid)
0171: throw new IllegalStateException(
0172: L
0173: .l(
0174: "{0}: can't call getLastAccessedTime() when session is no longer valid.",
0175: this ));
0176:
0177: return _accessTime;
0178: }
0179:
0180: /**
0181: * Returns the time the session is allowed to be alive.
0182: *
0183: * @return time allowed to live in seconds
0184: */
0185: public int getMaxInactiveInterval() {
0186: if (Long.MAX_VALUE / 2 <= _maxInactiveInterval)
0187: return -1;
0188: else
0189: return (int) (_maxInactiveInterval / 1000);
0190: }
0191:
0192: /**
0193: * Sets the maximum time a session is allowed to be alive.
0194: *
0195: * @param value time allowed to live in seconds
0196: */
0197: public void setMaxInactiveInterval(int value) {
0198: if (value < 0)
0199: _maxInactiveInterval = Long.MAX_VALUE / 2;
0200: else
0201: _maxInactiveInterval = ((long) value) * 1000;
0202:
0203: if (_clusterObject != null)
0204: _clusterObject.setExpireInterval(_maxInactiveInterval);
0205: }
0206:
0207: /**
0208: * Returns the session context.
0209: *
0210: * @deprecated
0211: */
0212: public HttpSessionContext getSessionContext() {
0213: return null;
0214: }
0215:
0216: /**
0217: * Returns the servlet context.
0218: */
0219: public ServletContext getServletContext() {
0220: return _manager.getWebApp();
0221: }
0222:
0223: /**
0224: * Returns the session manager.
0225: */
0226: public SessionManager getManager() {
0227: return _manager;
0228: }
0229:
0230: /**
0231: * Returns the authenticator
0232: */
0233: public ServletAuthenticator getAuthenticator() {
0234: return _manager.getWebApp().getAuthenticator();
0235: }
0236:
0237: /**
0238: * Returns the user
0239: */
0240: public Principal getUser() {
0241: if (_user != null)
0242: return _user;
0243:
0244: if (_isValid) {
0245: Object user = getAttribute(LOGIN);
0246:
0247: if (user instanceof Principal)
0248: _user = (Principal) user;
0249: else if (user instanceof LoginPrincipal)
0250: _user = ((LoginPrincipal) user).getUser();
0251: }
0252:
0253: return _user;
0254: }
0255:
0256: /**
0257: * Sets the user
0258: */
0259: public void setUser(Principal user) {
0260: _user = user;
0261:
0262: if (user == null)
0263: removeAttribute(LOGIN);
0264: else if (user instanceof java.io.Serializable)
0265: setAttribute(LOGIN, user);
0266: else
0267: setAttribute(LOGIN, new LoginPrincipal(user));
0268: }
0269:
0270: /**
0271: * Returns the named attribute from the session.
0272: */
0273: public Object getAttribute(String name) {
0274: if (!_isValid)
0275: throw new IllegalStateException(
0276: L
0277: .l(
0278: "{0}: can't call getAttribute() when session is no longer valid.",
0279: this ));
0280:
0281: synchronized (_values) {
0282: Object value = _values.get(name);
0283:
0284: return value;
0285: }
0286: }
0287:
0288: /**
0289: * Sets a session attribute. If the value is a listener, notify it
0290: * of the change. If the value has changed mark the session as changed
0291: * for persistent sessions.
0292: *
0293: * @param name the name of the attribute
0294: * @param value the value of the attribute
0295: */
0296: public void setAttribute(String name, Object value) {
0297: if (!_isValid)
0298: throw new IllegalStateException(
0299: L
0300: .l(
0301: "{0}: can't call setAttribute(String, Object) when session is no longer valid.",
0302: this ));
0303:
0304: Object oldValue;
0305:
0306: if (value != null && !(value instanceof Serializable)
0307: && log.isLoggable(Level.FINE)) {
0308: log
0309: .fine(L
0310: .l(
0311: "{0} attribute '{1}' value is non-serializable type '{2}'",
0312: this , name, value.getClass()
0313: .getName()));
0314: }
0315:
0316: synchronized (_values) {
0317: if (value != null)
0318: oldValue = _values.put(name, value);
0319: else
0320: oldValue = _values.remove(name);
0321: }
0322:
0323: // server/017p
0324: if (_clusterObject != null) // && value != oldValue)
0325: _clusterObject.change();
0326:
0327: if (oldValue instanceof HttpSessionBindingListener) {
0328: HttpSessionBindingListener listener;
0329: listener = (HttpSessionBindingListener) oldValue;
0330:
0331: listener.valueUnbound(new HttpSessionBindingEvent(
0332: SessionImpl.this , name, oldValue));
0333: }
0334:
0335: if (value instanceof HttpSessionBindingListener) {
0336: HttpSessionBindingListener listener;
0337: listener = (HttpSessionBindingListener) value;
0338:
0339: listener.valueBound(new HttpSessionBindingEvent(
0340: SessionImpl.this , name, value));
0341: }
0342:
0343: // Notify the attribute listeners
0344: ArrayList listeners = _manager.getAttributeListeners();
0345:
0346: if (listeners != null && listeners.size() > 0) {
0347: HttpSessionBindingEvent event;
0348:
0349: if (oldValue != null)
0350: event = new HttpSessionBindingEvent(this , name,
0351: oldValue);
0352: else
0353: event = new HttpSessionBindingEvent(this , name, value);
0354:
0355: for (int i = 0; i < listeners.size(); i++) {
0356: HttpSessionAttributeListener listener;
0357: listener = (HttpSessionAttributeListener) listeners
0358: .get(i);
0359:
0360: if (oldValue != null)
0361: listener.attributeReplaced(event);
0362: else
0363: listener.attributeAdded(event);
0364: }
0365: }
0366: }
0367:
0368: /**
0369: * Create the map used to store values.
0370: */
0371: protected Map<String, Object> createValueMap() {
0372: return new Hashtable<String, Object>(8);
0373: }
0374:
0375: /**
0376: * Remove a session attribute. If the value is a listener, notify it
0377: * of the change.
0378: *
0379: * @param name the name of the attribute to remove
0380: */
0381: public void removeAttribute(String name) {
0382: if (!_isValid)
0383: throw new IllegalStateException(
0384: L
0385: .l(
0386: "{0}: can't call removeAttribute(String) when session is no longer valid.",
0387: this ));
0388:
0389: Object oldValue;
0390:
0391: synchronized (_values) {
0392: oldValue = _values.remove(name);
0393: }
0394:
0395: if (_clusterObject != null && oldValue != null)
0396: _clusterObject.change();
0397:
0398: notifyValueUnbound(name, oldValue);
0399: }
0400:
0401: /**
0402: * Return an enumeration of all the sessions' attribute names.
0403: *
0404: * @return enumeration of the attribute names.
0405: */
0406: public Enumeration getAttributeNames() {
0407: synchronized (_values) {
0408: if (!_isValid)
0409: throw new IllegalStateException(
0410: L
0411: .l(
0412: "{0} can't call getAttributeNames() when session is no longer valid.",
0413: this ));
0414:
0415: return Collections.enumeration(_values.keySet());
0416: }
0417: }
0418:
0419: /**
0420: * @deprecated
0421: */
0422: public Object getValue(String name) {
0423: return getAttribute(name);
0424: }
0425:
0426: /**
0427: * @deprecated
0428: */
0429: public void putValue(String name, Object value) {
0430: setAttribute(name, value);
0431: }
0432:
0433: /**
0434: * @deprecated
0435: */
0436: public void removeValue(String name) {
0437: removeAttribute(name);
0438: }
0439:
0440: /**
0441: * @deprecated
0442: */
0443: public String[] getValueNames() {
0444: synchronized (_values) {
0445: if (!_isValid)
0446: throw new IllegalStateException(
0447: L
0448: .l(
0449: "{0} can't call getValueNames() when session is no longer valid.",
0450: this ));
0451:
0452: if (_values == null)
0453: return new String[0];
0454:
0455: String[] s = new String[_values.size()];
0456:
0457: Enumeration e = getAttributeNames();
0458: int count = 0;
0459: while (e.hasMoreElements())
0460: s[count++] = (String) e.nextElement();
0461:
0462: return s;
0463: }
0464: }
0465:
0466: /**
0467: * Returns true if the session is new.
0468: */
0469: public boolean isNew() {
0470: if (!_isValid)
0471: throw new IllegalStateException(
0472: L
0473: .l(
0474: "{0} can't call isNew() when session is no longer valid.",
0475: this ));
0476:
0477: return _isNew;
0478: }
0479:
0480: /**
0481: * Returns true if the session is valid.
0482: */
0483: public boolean isValid() {
0484: return _isValid;
0485: }
0486:
0487: /**
0488: * Set the session valid or invalid.
0489: */
0490: void setValid(boolean isValid) {
0491: _isValid = isValid;
0492: }
0493:
0494: boolean isClosing() {
0495: return _isClosing;
0496: }
0497:
0498: /**
0499: * Callback when the session is removed from the session cache, generally
0500: * because the session cache is full.
0501: */
0502: public void removeEvent() {
0503: synchronized (this ) {
0504: if (_isInvalidating || _useCount <= 0)
0505: _isClosing = true;
0506: }
0507:
0508: if (!_isClosing) {
0509: log
0510: .warning(L
0511: .l(
0512: "{0} LRU while in use (use-count={1}). Consider increasing session-count.",
0513: this , _useCount));
0514: }
0515:
0516: boolean isValid = _isValid;
0517:
0518: if (log.isLoggable(Level.FINE))
0519: log.fine(this + " remove");
0520:
0521: long now = Alarm.getCurrentTime();
0522:
0523: Store store = _manager.getSessionStore();
0524:
0525: // server/015k
0526: if (_isInvalidating || store == null
0527: || _accessTime + getMaxInactiveInterval() < now)
0528: notifyDestroy();
0529:
0530: invalidateLocal();
0531: }
0532:
0533: private void notifyDestroy() {
0534: ArrayList listeners = _manager.getListeners();
0535:
0536: if (listeners != null) {
0537: HttpSessionEvent event = new HttpSessionEvent(this );
0538:
0539: for (int i = listeners.size() - 1; i >= 0; i--) {
0540: HttpSessionListener listener;
0541: listener = (HttpSessionListener) listeners.get(i);
0542:
0543: listener.sessionDestroyed(event);
0544: }
0545: }
0546: }
0547:
0548: /**
0549: * Invalidates the session, called by user code.
0550: *
0551: * This should never be called by Resin code (for logging purposes)
0552: */
0553: public void invalidate() {
0554: if (log.isLoggable(Level.FINE))
0555: log.fine(this + " invalidate");
0556:
0557: _isInvalidating = true;
0558: invalidate(Logout.INVALIDATE);
0559: }
0560:
0561: /**
0562: * Invalidates a session based on a logout.
0563: */
0564: public void invalidateLogout() {
0565: if (log.isLoggable(Level.FINE))
0566: log.fine(this + " logout");
0567:
0568: _isInvalidating = true;
0569: invalidate(Logout.INVALIDATE);
0570: }
0571:
0572: /**
0573: * Invalidates a session based on a timeout
0574: */
0575: void invalidateTimeout() {
0576: if (log.isLoggable(Level.FINE))
0577: log.fine(this + " timeout");
0578:
0579: invalidate(Logout.TIMEOUT);
0580: }
0581:
0582: /**
0583: * Invalidates a session based on a LRU
0584: */
0585: void invalidateLru() {
0586: if (log.isLoggable(Level.FINE))
0587: log.fine(this + " lru");
0588:
0589: invalidateImpl(Logout.LRU);
0590: }
0591:
0592: /**
0593: * Invalidates the session.
0594: */
0595: private void invalidate(Logout logout) {
0596: if (!_isValid)
0597: throw new IllegalStateException(
0598: L
0599: .l(
0600: "{0}: Can't call invalidate() when session is no longer valid.",
0601: this ));
0602:
0603: try {
0604: // server/017s
0605: ServletAuthenticator auth = getAuthenticator();
0606: if (!(auth instanceof AbstractAuthenticator)
0607: || logout == Logout.INVALIDATE
0608: || (logout == Logout.TIMEOUT && ((AbstractAuthenticator) auth)
0609: .getLogoutOnSessionTimeout())) {
0610: // server/12i1, 12ch
0611: logout(logout == Logout.TIMEOUT ? this : null);
0612: }
0613:
0614: _manager.removeSession(this );
0615:
0616: invalidateImpl(logout);
0617: } finally {
0618: _isValid = false;
0619: }
0620: }
0621:
0622: /**
0623: * Logs out the user
0624: */
0625: public void logout() {
0626: // server/12bw
0627: logout(null);
0628: }
0629:
0630: /**
0631: * Logs out the user
0632: *
0633: * @param session the session in case of timeout and single-signon
0634: */
0635: public void logout(SessionImpl timeoutSession) {
0636: if (log.isLoggable(Level.FINE))
0637: log.fine(this + " logout " + timeoutSession);
0638:
0639: if (_user != null) {
0640: if (_isValid)
0641: removeAttribute(LOGIN);
0642: Principal user = _user;
0643: _user = null;
0644:
0645: try {
0646: ServletAuthenticator auth = getAuthenticator();
0647:
0648: if (auth != null)
0649: auth.logout(_manager.getWebApp(), timeoutSession,
0650: _id, user);
0651: } catch (Exception e) {
0652: log.log(Level.WARNING, e.toString(), e);
0653: }
0654: }
0655: }
0656:
0657: /**
0658: * Invalidate the session, removing it from the manager,
0659: * unbinding the values, and removing it from the store.
0660: */
0661: private void invalidateImpl(Logout logout) {
0662: boolean invalidateAfterListener = _manager
0663: .isInvalidateAfterListener();
0664: if (!invalidateAfterListener)
0665: _isValid = false;
0666:
0667: try {
0668: ClusterObject clusterObject = _clusterObject;
0669: // _clusterObject = null;
0670:
0671: if (clusterObject != null && _isInvalidating)
0672: clusterObject.remove();
0673: } catch (Exception e) {
0674: log.log(Level.FINE, e.toString(), e);
0675: }
0676:
0677: invalidateLocal();
0678: }
0679:
0680: /**
0681: * unbinds the session and saves if necessary.
0682: */
0683: private void invalidateLocal() {
0684: ClusterObject clusterObject = _clusterObject;
0685: if (_isValid && !_isInvalidating && clusterObject != null) {
0686: if (_manager.isSaveOnlyOnShutdown()) {
0687: clusterObject.update();
0688:
0689: try {
0690: clusterObject.store(this );
0691: } catch (Exception e) {
0692: log.log(Level.WARNING, this
0693: + ": can't serialize session", e);
0694: }
0695: }
0696: }
0697:
0698: unbind(); // we're invalidating, not passivating
0699: }
0700:
0701: /**
0702: * Creates a new session.
0703: */
0704: void create(long now) {
0705: if (log.isLoggable(Level.FINE)) {
0706: log.fine(this + " create session");
0707: }
0708:
0709: // e.g. server 'C' when 'A' and 'B' have no record of session
0710: if (_isValid)
0711: unbind();
0712:
0713: _isValid = true;
0714: _isNew = true;
0715: _accessTime = now;
0716: _creationTime = now;
0717:
0718: if (_clusterObject != null)
0719: _clusterObject.setValid();
0720: }
0721:
0722: /**
0723: * Returns true if the session is in use.
0724: */
0725: public boolean inUse() {
0726: return _useCount > 0;
0727: }
0728:
0729: /**
0730: * Set true if the session is in use.
0731: */
0732: boolean addUse() {
0733: synchronized (this ) {
0734: if (_isClosing)
0735: return false;
0736:
0737: _useCount++;
0738:
0739: return true;
0740: }
0741: }
0742:
0743: /**
0744: * Set true if the session is in use.
0745: */
0746: void endUse() {
0747: synchronized (this ) {
0748: _useCount--;
0749: }
0750: }
0751:
0752: /**
0753: * Clears the session when reading a bad saved session.
0754: */
0755: void reset(long now) {
0756: if (log.isLoggable(Level.FINE))
0757: log.fine(this + " reset");
0758:
0759: unbind();
0760: _isValid = true;
0761: _isNew = true;
0762: _accessTime = now;
0763: _creationTime = now;
0764: }
0765:
0766: /**
0767: * Loads the session.
0768: */
0769: public boolean load() {
0770: if (!_isValid)
0771: return false;
0772:
0773: boolean isValid;
0774:
0775: // server/01k0
0776: if (_useCount > 1)
0777: return true;
0778:
0779: ClusterObject clusterObject = _clusterObject;
0780: if (clusterObject != null)
0781: isValid = clusterObject.load(this );
0782: else
0783: isValid = true;
0784:
0785: return isValid;
0786: }
0787:
0788: /**
0789: * Passivates the session.
0790: */
0791: public void passivate() {
0792: unbind();
0793: }
0794:
0795: /**
0796: * Cleans up the session.
0797: */
0798: public void unbind() {
0799: if (_values.size() == 0)
0800: return;
0801:
0802: ClusterObject clusterObject = _clusterObject;
0803:
0804: ArrayList<String> names = new ArrayList<String>();
0805: ArrayList<Object> values = new ArrayList<Object>();
0806:
0807: synchronized (_values) {
0808: /*
0809: if (_useCount > 0)
0810: Thread.dumpStack();
0811: */
0812:
0813: Iterator<Map.Entry<String, Object>> iter = _values
0814: .entrySet().iterator();
0815: while (iter.hasNext()) {
0816: Map.Entry<String, Object> entry = iter.next();
0817:
0818: names.add(entry.getKey());
0819: values.add(entry.getValue());
0820: }
0821:
0822: _values.clear();
0823: }
0824:
0825: if (clusterObject != null)
0826: clusterObject.update();
0827:
0828: // server/015a
0829: for (int i = 0; i < names.size(); i++) {
0830: String name = names.get(i);
0831: Object value = values.get(i);
0832:
0833: notifyValueUnbound(name, value);
0834: }
0835: }
0836:
0837: /**
0838: * Notify any value unbound listeners.
0839: */
0840: private void notifyValueUnbound(String name, Object oldValue) {
0841: if (oldValue == null)
0842: return;
0843:
0844: if (oldValue instanceof HttpSessionBindingListener) {
0845: HttpSessionBindingListener listener;
0846: listener = (HttpSessionBindingListener) oldValue;
0847:
0848: listener.valueUnbound(new HttpSessionBindingEvent(this ,
0849: name, oldValue));
0850: }
0851:
0852: // Notify the attributes listeners
0853: ArrayList listeners = _manager.getAttributeListeners();
0854: if (listeners != null) {
0855: HttpSessionBindingEvent event;
0856:
0857: event = new HttpSessionBindingEvent(this , name, oldValue);
0858:
0859: for (int i = 0; i < listeners.size(); i++) {
0860: HttpSessionAttributeListener listener;
0861: listener = (HttpSessionAttributeListener) listeners
0862: .get(i);
0863:
0864: listener.attributeRemoved(event);
0865: }
0866: }
0867: }
0868:
0869: /*
0870: * Set the current access time to now.
0871: */
0872: void setAccess(long now) {
0873: // server/01k0
0874: if (_useCount > 1)
0875: return;
0876:
0877: _isNew = false;
0878:
0879: if (_clusterObject != null)
0880: _clusterObject.access();
0881:
0882: _accessTime = now;
0883: }
0884:
0885: /**
0886: * Cleaning up session stuff at the end of a request.
0887: *
0888: * <p>If the session data has changed and we have persistent sessions,
0889: * save the session. However, if save-on-shutdown is true, only save
0890: * on a server shutdown.
0891: */
0892: public void finish() {
0893: _accessTime = Alarm.getCurrentTime();
0894:
0895: synchronized (this ) {
0896: if (_useCount > 1) {
0897: _useCount--;
0898: return;
0899: }
0900: }
0901:
0902: try {
0903: saveAfterRequest();
0904: } finally {
0905: synchronized (this ) {
0906: _useCount--;
0907: }
0908: }
0909: }
0910:
0911: /**
0912: * Save changes before any flush.
0913: */
0914: public final void saveBeforeFlush() {
0915: if (_manager == null || !_manager.isSaveBeforeFlush())
0916: return;
0917:
0918: save();
0919: }
0920:
0921: /**
0922: * Flush changes before the headers.
0923: */
0924: public final void saveBeforeHeaders() {
0925: if (_manager == null || !_manager.isSaveBeforeHeaders())
0926: return;
0927:
0928: save();
0929: }
0930:
0931: /**
0932: * Flush changes after a request completes.
0933: */
0934: public final void saveAfterRequest() {
0935: if (_manager == null || !_manager.isSaveAfterRequest())
0936: return;
0937:
0938: save();
0939: }
0940:
0941: /**
0942: * Saves changes to the session.
0943: */
0944: public final void save() {
0945: if (!isValid())
0946: return;
0947:
0948: try {
0949: ClusterObject clusterObject = _clusterObject;
0950: if (clusterObject != null) {
0951: clusterObject.store(this );
0952: }
0953: } catch (Throwable e) {
0954: log.log(Level.WARNING, this + ": can't serialize session",
0955: e);
0956: }
0957: }
0958:
0959: /**
0960: * Store on shutdown.
0961: */
0962: void saveOnShutdown() {
0963: try {
0964: ClusterObject clusterObject = _clusterObject;
0965:
0966: if (clusterObject != null) {
0967: clusterObject.change();
0968: clusterObject.store(this );
0969: }
0970: } catch (Throwable e) {
0971: log.log(Level.WARNING, this + ": can't serialize session",
0972: e);
0973: }
0974: }
0975:
0976: /**
0977: * Returns true if the session is empty.
0978: */
0979: public boolean isEmpty() {
0980: return _values == null || _values.size() == 0;
0981: }
0982:
0983: /**
0984: * Loads the object from the input stream.
0985: */
0986: public void load(Hessian2Input in) throws IOException {
0987: HttpSessionEvent event = null;
0988:
0989: synchronized (_values) {
0990: unbind();
0991:
0992: try {
0993: int size = in.readInt();
0994:
0995: //System.out.println("LOAD: " + size + " " + this + " " + _clusterObject + System.identityHashCode(this));
0996:
0997: for (int i = 0; i < size; i++) {
0998: String key = in.readString();
0999: Object value = in.readObject();
1000:
1001: if (value != null) {
1002: synchronized (_values) {
1003: _values.put(key, value);
1004: }
1005:
1006: if (value instanceof HttpSessionActivationListener) {
1007: HttpSessionActivationListener listener = (HttpSessionActivationListener) value;
1008:
1009: if (event == null)
1010: event = new HttpSessionEvent(this );
1011:
1012: listener.sessionDidActivate(event);
1013: }
1014: }
1015: }
1016: } catch (Exception e) {
1017: throw IOExceptionWrapper.create(e);
1018: }
1019:
1020: ArrayList<HttpSessionActivationListener> listeners;
1021: listeners = _manager.getActivationListeners();
1022: for (int i = 0; listeners != null && i < listeners.size(); i++) {
1023: HttpSessionActivationListener listener = listeners
1024: .get(i);
1025:
1026: if (event == null)
1027: event = new HttpSessionEvent(this );
1028:
1029: listener.sessionDidActivate(event);
1030: }
1031: }
1032: }
1033:
1034: /**
1035: * Saves the object to the input stream.
1036: */
1037: public void store(Hessian2Output out) throws IOException {
1038: HttpSessionEvent event = null;
1039:
1040: synchronized (_values) {
1041: Set set = getEntrySet();
1042:
1043: int size = set == null ? 0 : set.size();
1044:
1045: out.writeInt(size);
1046:
1047: if (size == 0)
1048: return;
1049:
1050: ArrayList<HttpSessionActivationListener> listeners;
1051: listeners = _manager.getActivationListeners();
1052: for (int i = 0; listeners != null && i < listeners.size(); i++) {
1053: HttpSessionActivationListener listener = listeners
1054: .get(i);
1055:
1056: if (event == null)
1057: event = new HttpSessionEvent(this );
1058:
1059: listener.sessionWillPassivate(event);
1060: }
1061:
1062: boolean ignoreNonSerializable = getManager()
1063: .getIgnoreSerializationErrors();
1064:
1065: Map.Entry[] entries = new Map.Entry[set.size()];
1066:
1067: Iterator iter = set.iterator();
1068: int i = 0;
1069: while (iter.hasNext()) {
1070: entries[i++] = (Map.Entry) iter.next();
1071: }
1072:
1073: Arrays.sort(entries, KEY_COMPARATOR);
1074:
1075: for (i = 0; i < entries.length; i++) {
1076: Map.Entry entry = entries[i];
1077: Object value = entry.getValue();
1078:
1079: out.writeString((String) entry.getKey());
1080:
1081: if (ignoreNonSerializable
1082: && !(value instanceof Serializable)) {
1083: out.writeObject(null);
1084: continue;
1085: }
1086:
1087: if (value instanceof HttpSessionActivationListener) {
1088: HttpSessionActivationListener listener = (HttpSessionActivationListener) value;
1089:
1090: if (event == null)
1091: event = new HttpSessionEvent(this );
1092:
1093: listener.sessionWillPassivate(event);
1094: }
1095:
1096: try {
1097: out.writeObject(value);
1098: } catch (NotSerializableException e) {
1099: log
1100: .warning(L
1101: .l(
1102: "{0}: failed storing persistent session attribute '{1}'. Persistent session values must extend java.io.Serializable.\n{2}",
1103: this , entry.getKey(),
1104: String.valueOf(e)));
1105: throw e;
1106: }
1107: }
1108: }
1109: }
1110:
1111: /**
1112: * Loads the object from the input stream.
1113: */
1114: public void load(ObjectInput in) throws IOException {
1115: HttpSessionEvent event = null;
1116:
1117: synchronized (_values) {
1118: // server/017u, #1820 - load does not trigger callbacks
1119: _values.clear();
1120:
1121: try {
1122: int size = in.readInt();
1123:
1124: //System.out.println("LOAD: " + size + " " + this + " " + _clusterObject + System.identityHashCode(this));
1125:
1126: for (int i = 0; i < size; i++) {
1127: String key = in.readUTF();
1128: Object value = in.readObject();
1129:
1130: if (value != null) {
1131: synchronized (_values) {
1132: _values.put(key, value);
1133: }
1134:
1135: if (value instanceof HttpSessionActivationListener) {
1136: HttpSessionActivationListener listener = (HttpSessionActivationListener) value;
1137:
1138: if (event == null)
1139: event = new HttpSessionEvent(this );
1140:
1141: listener.sessionDidActivate(event);
1142: }
1143: }
1144: }
1145: } catch (Exception e) {
1146: throw IOExceptionWrapper.create(e);
1147: }
1148:
1149: ArrayList<HttpSessionActivationListener> listeners;
1150: listeners = _manager.getActivationListeners();
1151: for (int i = 0; listeners != null && i < listeners.size(); i++) {
1152: HttpSessionActivationListener listener = listeners
1153: .get(i);
1154:
1155: if (event == null)
1156: event = new HttpSessionEvent(this );
1157:
1158: listener.sessionDidActivate(event);
1159: }
1160: }
1161: }
1162:
1163: /**
1164: * Saves the object to the input stream.
1165: */
1166: public void store(ObjectOutput out) throws IOException {
1167: HttpSessionEvent event = null;
1168:
1169: synchronized (_values) {
1170: Set set = getEntrySet();
1171:
1172: int size = set == null ? 0 : set.size();
1173:
1174: out.writeInt(size);
1175:
1176: if (size == 0)
1177: return;
1178:
1179: ArrayList<HttpSessionActivationListener> listeners;
1180: listeners = _manager.getActivationListeners();
1181: for (int i = 0; listeners != null && i < listeners.size(); i++) {
1182: HttpSessionActivationListener listener = listeners
1183: .get(i);
1184:
1185: if (event == null)
1186: event = new HttpSessionEvent(this );
1187:
1188: listener.sessionWillPassivate(event);
1189: }
1190:
1191: boolean ignoreNonSerializable = getManager()
1192: .getIgnoreSerializationErrors();
1193:
1194: Map.Entry[] entries = new Map.Entry[set.size()];
1195:
1196: Iterator iter = set.iterator();
1197: int i = 0;
1198: while (iter.hasNext()) {
1199: entries[i++] = (Map.Entry) iter.next();
1200: }
1201:
1202: Arrays.sort(entries, KEY_COMPARATOR);
1203:
1204: for (i = 0; i < entries.length; i++) {
1205: Map.Entry entry = entries[i];
1206: Object value = entry.getValue();
1207:
1208: out.writeUTF((String) entry.getKey());
1209:
1210: if (ignoreNonSerializable
1211: && !(value instanceof Serializable)) {
1212: out.writeObject(null);
1213: continue;
1214: }
1215:
1216: if (value instanceof HttpSessionActivationListener) {
1217: HttpSessionActivationListener listener = (HttpSessionActivationListener) value;
1218:
1219: if (event == null)
1220: event = new HttpSessionEvent(this );
1221:
1222: listener.sessionWillPassivate(event);
1223: }
1224:
1225: try {
1226: out.writeObject(value);
1227: } catch (NotSerializableException e) {
1228: log
1229: .warning(L
1230: .l(
1231: "{0}: failed storing persistent session attribute '{1}'. Persistent session values must extend java.io.Serializable.\n{2}",
1232: this , entry.getKey(),
1233: String.valueOf(e)));
1234: throw e;
1235: }
1236: }
1237: }
1238: }
1239:
1240: /**
1241: * Returns the set of values in the session
1242: */
1243: Set getEntrySet() {
1244: synchronized (_values) {
1245: if (!_isValid)
1246: throw new IllegalStateException(
1247: L
1248: .l(
1249: "{0}: can't call getEntrySet() when session is no longer valid.",
1250: this ));
1251:
1252: return _values.entrySet();
1253: }
1254: }
1255:
1256: public boolean canLog() {
1257: return log.isLoggable(Level.FINE);
1258: }
1259:
1260: public void log(String value) {
1261: log.fine(value);
1262: }
1263:
1264: @Override
1265: public String toString() {
1266: String contextPath = "";
1267:
1268: SessionManager manager = _manager;
1269: if (manager != null) {
1270: WebApp webApp = manager.getWebApp();
1271:
1272: if (webApp != null)
1273: contextPath = "," + webApp.getContextPath();
1274: }
1275:
1276: return "SessionImpl[" + getId() + contextPath + "]";
1277: }
1278:
1279: enum Logout {
1280: INVALIDATE, LRU, TIMEOUT
1281: };
1282:
1283: static class LoginPrincipal implements java.io.Serializable {
1284: private transient Principal _user;
1285:
1286: LoginPrincipal(Principal user) {
1287: _user = user;
1288: }
1289:
1290: public Principal getUser() {
1291: return _user;
1292: }
1293: }
1294:
1295: private static Comparator KEY_COMPARATOR = new Comparator() {
1296: public int compare(Object aObj, Object bObj) {
1297: Map.Entry a = (Map.Entry) aObj;
1298: Map.Entry b = (Map.Entry) bObj;
1299:
1300: String aStr = (String) a.getKey();
1301: String bStr = (String) b.getKey();
1302:
1303: return aStr.compareTo(bStr);
1304: }
1305: };
1306: }
|