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.config.Config;
0033: import com.caucho.config.ConfigException;
0034: import com.caucho.config.types.JndiBuilder;
0035: import com.caucho.config.types.Period;
0036: import com.caucho.hessian.io.*;
0037: import com.caucho.management.server.SessionManagerMXBean;
0038: import com.caucho.server.cluster.Cluster;
0039: import com.caucho.server.cluster.ClusterObject;
0040: import com.caucho.server.cluster.ClusterServer;
0041: import com.caucho.server.cluster.ObjectManager;
0042: import com.caucho.server.cluster.Store;
0043: import com.caucho.server.cluster.StoreManager;
0044: import com.caucho.server.dispatch.DispatchServer;
0045: import com.caucho.server.dispatch.InvocationDecoder;
0046: import com.caucho.server.security.ServletAuthenticator;
0047: import com.caucho.server.webapp.WebApp;
0048: import com.caucho.util.Alarm;
0049: import com.caucho.util.AlarmListener;
0050: import com.caucho.util.L10N;
0051: import com.caucho.util.LruCache;
0052: import com.caucho.util.RandomUtil;
0053: import com.caucho.vfs.Path;
0054: import com.caucho.vfs.Vfs;
0055:
0056: import javax.naming.Context;
0057: import javax.naming.InitialContext;
0058: import javax.servlet.http.HttpServletRequest;
0059: import javax.servlet.http.HttpSessionActivationListener;
0060: import javax.servlet.http.HttpSessionAttributeListener;
0061: import javax.servlet.http.HttpSessionEvent;
0062: import javax.servlet.http.HttpSessionListener;
0063: import java.io.*;
0064: import java.util.ArrayList;
0065: import java.util.Iterator;
0066: import java.util.logging.Level;
0067: import java.util.logging.Logger;
0068:
0069: // import com.caucho.server.http.ServletServer;
0070: // import com.caucho.server.http.VirtualHost;
0071:
0072: /**
0073: * Manages sessions in a web-app.
0074: */
0075: public final class SessionManager implements ObjectManager,
0076: AlarmListener {
0077: static protected final L10N L = new L10N(SessionManager.class);
0078: static protected final Logger log = Logger
0079: .getLogger(SessionManager.class.getName());
0080:
0081: private static final int FALSE = 0;
0082: private static final int COOKIE = 1;
0083: private static final int TRUE = 2;
0084:
0085: private static final int UNSET = 0;
0086: private static final int SET_TRUE = 1;
0087: private static final int SET_FALSE = 2;
0088:
0089: private static final int SAVE_BEFORE_HEADERS = 0x1;
0090: private static final int SAVE_BEFORE_FLUSH = 0x2;
0091: private static final int SAVE_AFTER_REQUEST = 0x4;
0092: private static final int SAVE_ON_SHUTDOWN = 0x8;
0093:
0094: private static final int DECODE[];
0095:
0096: private WebApp _webApp;
0097: private final SessionManagerAdmin _admin;
0098:
0099: // factory for creating sessions
0100: // private SessionFactory _sessionFactory;
0101:
0102: // active sessions
0103: private LruCache<String, SessionImpl> _sessions;
0104: // total sessions
0105: private int _totalSessions;
0106:
0107: // iterator to purge sessions (to reduce gc)
0108: private Iterator<SessionImpl> _sessionIter;
0109: // array list for session timeout
0110: private ArrayList<SessionImpl> _sessionList = new ArrayList<SessionImpl>();
0111: // generate cookies
0112: private boolean _enableSessionCookies = true;
0113: // allow session rewriting
0114: private boolean _enableSessionUrls = true;
0115:
0116: private boolean _isModuloSessionId = false;
0117: private boolean _isAppendServerIndex = false;
0118: private boolean _isTwoDigitSessionIndex = false;
0119:
0120: // invalidate the session after the listeners have been called
0121: private boolean _isInvalidateAfterListener;
0122:
0123: // maximum number of sessions
0124: private int _sessionMax = 8192;
0125: // how long a session will be inactive before it times out
0126: private long _sessionTimeout = 30 * 60 * 1000;
0127:
0128: private String _cookieName = "JSESSIONID";
0129: private String _sslCookieName;
0130:
0131: // Rewriting strings.
0132: private String _sessionSuffix = ";jsessionid=";
0133: private String _sessionPrefix;
0134:
0135: // default cookie version
0136: private int _cookieVersion;
0137: private String _cookieDomain;
0138: private long _cookieMaxAge;
0139: private boolean _cookieSecure;
0140: private int _isCookieHttpOnly;
0141: private String _cookiePort;
0142: private int _reuseSessionId = COOKIE;
0143: private int _cookieLength = 21;
0144:
0145: private int _sessionSaveMode = SAVE_AFTER_REQUEST;
0146:
0147: //private SessionStore sessionStore;
0148: private StoreManager _storeManager;
0149:
0150: // If true, serialization errors should not be logged
0151: // XXX: changed for JSF
0152: private boolean _ignoreSerializationErrors = true;
0153: private boolean _isHessianSerialization = false;
0154:
0155: // List of the HttpSessionListeners from the configuration file
0156: private ArrayList<HttpSessionListener> _listeners;
0157:
0158: // List of the HttpSessionListeners from the configuration file
0159: private ArrayList<HttpSessionActivationListener> _activationListeners;
0160:
0161: // List of the HttpSessionAttributeListeners from the configuration file
0162: private ArrayList<HttpSessionAttributeListener> _attributeListeners;
0163:
0164: //
0165: // Compatibility fields
0166: //
0167:
0168: private boolean _isWebAppStore; // i.e. for old-style compatibility
0169: private Store _sessionStore;
0170: private int _alwaysLoadSession;
0171: private int _alwaysSaveSession;
0172:
0173: private boolean _distributedRing;
0174: private Path _persistentPath;
0175:
0176: private boolean _isClosed;
0177:
0178: private String _distributionId;
0179: private Cluster _cluster;
0180: private ClusterServer _selfServer;
0181: private ClusterServer[] _srunGroup = new ClusterServer[0];
0182:
0183: private int _srunIndex;
0184: private int _srunLength;
0185: private int _machineLength;
0186:
0187: private Alarm _alarm = new Alarm(this );
0188:
0189: // statistics
0190: private Object _statisticsLock = new Object();
0191: private long _sessionCreateCount;
0192: private long _sessionTimeoutCount;
0193: private long _sessionInvalidateCount;
0194:
0195: /**
0196: * Creates and initializes a new session manager
0197: *
0198: * @param app the web-app webApp
0199: * @param registry the web-app configuration node
0200: */
0201: public SessionManager(WebApp app) throws Exception {
0202: _webApp = app;
0203:
0204: DispatchServer server = app.getDispatchServer();
0205: if (server != null) {
0206: InvocationDecoder decoder = server.getInvocationDecoder();
0207:
0208: _sessionSuffix = decoder.getSessionURLPrefix();
0209: _sessionPrefix = decoder.getAlternateSessionURLPrefix();
0210:
0211: _cookieName = decoder.getSessionCookie();
0212: _sslCookieName = decoder.getSSLSessionCookie();
0213: }
0214:
0215: // this.server = app.getVirtualHost().getServer();
0216: // this.srunIndex = server.getSrunIndex();
0217:
0218: String hostName = app.getHostName();
0219: String contextPath = app.getContextPath();
0220:
0221: if (hostName == null || hostName.equals(""))
0222: hostName = "default";
0223:
0224: String name = hostName + contextPath;
0225:
0226: if (_distributionId == null)
0227: _distributionId = name;
0228:
0229: _persistentPath = Vfs.lookup("WEB-INF/sessions");
0230:
0231: _admin = new SessionManagerAdmin(this );
0232: }
0233:
0234: /**
0235: * Returns the admin.
0236: */
0237: public SessionManagerMXBean getAdmin() {
0238: return _admin;
0239: }
0240:
0241: /**
0242: * Gets the cluster.
0243: */
0244: protected Cluster getCluster() {
0245: synchronized (this ) {
0246: if (_cluster == null) {
0247: _cluster = Cluster.getLocal();
0248: ClusterServer selfServer = null;
0249:
0250: if (_cluster != null) {
0251: _machineLength = _cluster.getMachineList().size();
0252: _srunLength = _cluster.getServerList().length;
0253:
0254: selfServer = _cluster.getSelfServer();
0255: _selfServer = selfServer;
0256:
0257: if (selfServer != null) {
0258: _srunGroup = _cluster.getServerList();
0259: _srunIndex = selfServer.getIndex();
0260: }
0261: }
0262: }
0263: }
0264:
0265: return _cluster;
0266: }
0267:
0268: /**
0269: * Returns the session prefix, ie.. ";jsessionid=".
0270: */
0271: public String getSessionPrefix() {
0272: return _sessionSuffix;
0273: }
0274:
0275: /**
0276: * Returns the alternate session prefix, before the URL for wap.
0277: */
0278: public String getAlternateSessionPrefix() {
0279: return _sessionPrefix;
0280: }
0281:
0282: /**
0283: * Returns the cookie version.
0284: */
0285: public int getCookieVersion() {
0286: return _cookieVersion;
0287: }
0288:
0289: /**
0290: * Sets the cookie version.
0291: */
0292: public void setCookieVersion(int cookieVersion) {
0293: _cookieVersion = cookieVersion;
0294: }
0295:
0296: /**
0297: * Sets the cookie ports.
0298: */
0299: public void setCookiePort(String port) {
0300: _cookiePort = port;
0301: }
0302:
0303: /**
0304: * Gets the cookie ports.
0305: */
0306: public String getCookiePort() {
0307: return _cookiePort;
0308: }
0309:
0310: /**
0311: * Returns the debug log
0312: */
0313: public Logger getDebug() {
0314: return log;
0315: }
0316:
0317: /**
0318: * Returns the SessionManager's webApp
0319: */
0320: WebApp getWebApp() {
0321: return _webApp;
0322: }
0323:
0324: /**
0325: * Returns the SessionManager's authenticator
0326: */
0327: ServletAuthenticator getAuthenticator() {
0328: return _webApp.getAuthenticator();
0329: }
0330:
0331: /**
0332: * Sets the persistent store.
0333: */
0334: public void setPersistentStore(JndiBuilder store)
0335: throws javax.naming.NamingException, ConfigException {
0336: _storeManager = (StoreManager) store.getObject();
0337:
0338: if (_storeManager == null)
0339: throw new ConfigException(L.l(
0340: "{0} is an unknown persistent store.", store
0341: .getJndiName()));
0342: }
0343:
0344: /**
0345: * True if sessions should always be loadd.
0346: */
0347: boolean getAlwaysLoadSession() {
0348: return _alwaysLoadSession == SET_TRUE;
0349: }
0350:
0351: /**
0352: * True if sessions should always be loadd.
0353: */
0354: public void setAlwaysLoadSession(boolean load) {
0355: _alwaysLoadSession = load ? SET_TRUE : SET_FALSE;
0356: }
0357:
0358: /**
0359: * True if sessions should always be saved.
0360: */
0361: boolean getAlwaysSaveSession() {
0362: return _alwaysSaveSession == SET_TRUE;
0363: }
0364:
0365: /**
0366: * True if sessions should always be saved.
0367: */
0368: public void setAlwaysSaveSession(boolean save) {
0369: _alwaysSaveSession = save ? SET_TRUE : SET_FALSE;
0370: }
0371:
0372: /**
0373: * True if sessions should be saved on shutdown.
0374: */
0375: public boolean isSaveOnShutdown() {
0376: return (_sessionSaveMode & SAVE_ON_SHUTDOWN) != 0;
0377: }
0378:
0379: /**
0380: * True if sessions should only be saved on shutdown.
0381: */
0382: public boolean isSaveOnlyOnShutdown() {
0383: return (_sessionSaveMode & SAVE_ON_SHUTDOWN) == SAVE_ON_SHUTDOWN;
0384: }
0385:
0386: /**
0387: * True if sessions should be saved before the HTTP headers.
0388: */
0389: public boolean isSaveBeforeHeaders() {
0390: return (_sessionSaveMode & SAVE_BEFORE_HEADERS) != 0;
0391: }
0392:
0393: /**
0394: * True if sessions should be saved before each flush.
0395: */
0396: public boolean isSaveBeforeFlush() {
0397: return (_sessionSaveMode & SAVE_BEFORE_FLUSH) != 0;
0398: }
0399:
0400: /**
0401: * True if sessions should be saved after the request.
0402: */
0403: public boolean isSaveAfterRequest() {
0404: return (_sessionSaveMode & SAVE_AFTER_REQUEST) != 0;
0405: }
0406:
0407: /**
0408: * Sets the save-mode: before-flush, before-headers, after-request,
0409: * on-shutdown
0410: */
0411: public void setSaveMode(String mode) throws ConfigException {
0412: /* XXX: probably don't want to implement this.
0413: if ("before-flush".equals(mode)) {
0414: _sessionSaveMode = (SAVE_BEFORE_FLUSH|
0415: SAVE_BEFORE_HEADERS|
0416: SAVE_AFTER_REQUEST|
0417: SAVE_ON_SHUTDOWN);
0418: }
0419: else
0420: */
0421:
0422: if ("before-headers".equals(mode)) {
0423: _sessionSaveMode = (SAVE_BEFORE_HEADERS
0424: | SAVE_AFTER_REQUEST | SAVE_ON_SHUTDOWN);
0425: } else if ("after-request".equals(mode)) {
0426: _sessionSaveMode = (SAVE_AFTER_REQUEST | SAVE_ON_SHUTDOWN);
0427: } else if ("on-shutdown".equals(mode)) {
0428: _sessionSaveMode = (SAVE_ON_SHUTDOWN);
0429: } else
0430: throw new ConfigException(
0431: L
0432: .l(
0433: "'{0}' is an unknown session save-mode. Values are: before-headers, after-request, and on-shutdown.",
0434: mode));
0435:
0436: }
0437:
0438: /**
0439: * Returns the string value of the save-mode.
0440: */
0441: public String getSaveMode() {
0442: if (isSaveBeforeFlush())
0443: return "before-flush";
0444: else if (isSaveBeforeHeaders())
0445: return "before-headers";
0446: else if (isSaveAfterRequest())
0447: return "after-request";
0448: else if (isSaveOnShutdown())
0449: return "on-shutdown";
0450: else
0451: return "unknown";
0452: }
0453:
0454: /**
0455: * True if sessions should only be saved on shutdown.
0456: */
0457: public void setSaveOnlyOnShutdown(boolean save) {
0458: log
0459: .warning("<save-only-on-shutdown> is deprecated. Use <save-mode>on-shutdown</save-mode> instead");
0460:
0461: if (save)
0462: _sessionSaveMode = SAVE_ON_SHUTDOWN;
0463: }
0464:
0465: /**
0466: * True if sessions should only be saved on shutdown.
0467: */
0468: public void setSaveOnShutdown(boolean save) {
0469: log
0470: .warning("<save-on-shutdown> is deprecated. Use <save-only-on-shutdown> instead");
0471:
0472: setSaveOnlyOnShutdown(save);
0473: }
0474:
0475: /**
0476: * Sets the serialization type.
0477: */
0478: public void setSerializationType(String type) {
0479: if ("hessian".equals(type))
0480: _isHessianSerialization = true;
0481: else if ("java".equals(type))
0482: _isHessianSerialization = false;
0483: else
0484: throw new ConfigException(
0485: L
0486: .l(
0487: "'{0}' is an unknown valud for serialization-type. The valid types are 'hessian' and 'java'.",
0488: type));
0489: }
0490:
0491: /**
0492: * Returns true for Hessian serialization.
0493: */
0494: public boolean isHessianSerialization() {
0495: return _isHessianSerialization;
0496: }
0497:
0498: /**
0499: * True if the session should be invalidated after the listener.
0500: */
0501: public void setInvalidateAfterListener(boolean inv) {
0502: _isInvalidateAfterListener = inv;
0503: }
0504:
0505: /**
0506: * True if the session should be invalidated after the listener.
0507: */
0508: public boolean isInvalidateAfterListener() {
0509: return _isInvalidateAfterListener;
0510: }
0511:
0512: /**
0513: * Returns the current number of active sessions.
0514: */
0515: public int getActiveSessionCount() {
0516: if (_sessions == null)
0517: return -1;
0518: else
0519: return _sessions.size();
0520: }
0521:
0522: /**
0523: * Returns the active sessions.
0524: */
0525: public int getSessionActiveCount() {
0526: return getActiveSessionCount();
0527: }
0528:
0529: /**
0530: * Returns the created sessions.
0531: */
0532: public long getSessionCreateCount() {
0533: return _sessionCreateCount;
0534: }
0535:
0536: /**
0537: * Returns the timeout sessions.
0538: */
0539: public long getSessionTimeoutCount() {
0540: return _sessionTimeoutCount;
0541: }
0542:
0543: /**
0544: * Returns the invalidate sessions.
0545: */
0546: public long getSessionInvalidateCount() {
0547: return _sessionInvalidateCount;
0548: }
0549:
0550: /**
0551: * Adds a new HttpSessionListener.
0552: */
0553: public void addListener(HttpSessionListener listener) {
0554: if (_listeners == null)
0555: _listeners = new ArrayList<HttpSessionListener>();
0556:
0557: _listeners.add(listener);
0558: }
0559:
0560: /**
0561: * Adds a new HttpSessionListener.
0562: */
0563: ArrayList<HttpSessionListener> getListeners() {
0564: return _listeners;
0565: }
0566:
0567: /**
0568: * Adds a new HttpSessionActivationListener.
0569: */
0570: public void addActivationListener(
0571: HttpSessionActivationListener listener) {
0572: if (_activationListeners == null)
0573: _activationListeners = new ArrayList<HttpSessionActivationListener>();
0574:
0575: _activationListeners.add(listener);
0576: }
0577:
0578: /**
0579: * Returns the activation listeners.
0580: */
0581: ArrayList<HttpSessionActivationListener> getActivationListeners() {
0582: return _activationListeners;
0583: }
0584:
0585: /**
0586: * Adds a new HttpSessionAttributeListener.
0587: */
0588: public void addAttributeListener(
0589: HttpSessionAttributeListener listener) {
0590: if (_attributeListeners == null)
0591: _attributeListeners = new ArrayList<HttpSessionAttributeListener>();
0592:
0593: _attributeListeners.add(listener);
0594: }
0595:
0596: /**
0597: * Gets the HttpSessionAttributeListener.
0598: */
0599: ArrayList<HttpSessionAttributeListener> getAttributeListeners() {
0600: return _attributeListeners;
0601: }
0602:
0603: /**
0604: * True if serialization errors should just fail silently.
0605: */
0606: boolean getIgnoreSerializationErrors() {
0607: return _ignoreSerializationErrors;
0608: }
0609:
0610: /**
0611: * True if serialization errors should just fail silently.
0612: */
0613: public void setIgnoreSerializationErrors(boolean ignore) {
0614: _ignoreSerializationErrors = ignore;
0615: }
0616:
0617: /**
0618: * True if the server should reuse the current session id if the
0619: * session doesn't exist.
0620: */
0621: public int getReuseSessionId() {
0622: return _reuseSessionId;
0623: }
0624:
0625: /**
0626: * True if the server should reuse the current session id if the
0627: * session doesn't exist.
0628: */
0629: public boolean reuseSessionId(boolean fromCookie) {
0630: int reuseSessionId = _reuseSessionId;
0631:
0632: return reuseSessionId == TRUE || fromCookie
0633: && reuseSessionId == COOKIE;
0634: }
0635:
0636: /**
0637: * True if the server should reuse the current session id if the
0638: * session doesn't exist.
0639: */
0640: public void setReuseSessionId(String reuse) throws ConfigException {
0641: if (reuse == null)
0642: _reuseSessionId = COOKIE;
0643: else if (reuse.equalsIgnoreCase("true")
0644: || reuse.equalsIgnoreCase("yes")
0645: || reuse.equalsIgnoreCase("cookie"))
0646: _reuseSessionId = COOKIE;
0647: else if (reuse.equalsIgnoreCase("false")
0648: || reuse.equalsIgnoreCase("no"))
0649: _reuseSessionId = FALSE;
0650: else if (reuse.equalsIgnoreCase("all"))
0651: _reuseSessionId = TRUE;
0652: else
0653: throw new ConfigException(
0654: L
0655: .l(
0656: "'{0}' is an invalid value for reuse-session-id. 'true' or 'false' are the allowed values.",
0657: reuse));
0658: }
0659:
0660: /**
0661: * Returns the owning server.
0662: */
0663: ClusterServer getServer(int index) {
0664: Cluster cluster = getCluster();
0665:
0666: if (cluster != null)
0667: return cluster.getServer(index);
0668: else
0669: return null;
0670: }
0671:
0672: /**
0673: * Returns the index of this JVM in the ring.
0674: */
0675: public int getSrunIndex() {
0676: return _srunIndex;
0677: }
0678:
0679: /**
0680: * Returns the number of sruns in the cluster
0681: */
0682: public int getSrunLength() {
0683: return _srunLength;
0684: }
0685:
0686: /**
0687: * Returns true if the sessions are closed.
0688: */
0689: public boolean isClosed() {
0690: return _isClosed;
0691: }
0692:
0693: /**
0694: * Sets the file store.
0695: */
0696: public StoreManager createFileStore() throws ConfigException {
0697: Cluster cluster = getCluster();
0698:
0699: if (cluster == null)
0700: throw new ConfigException(L
0701: .l("<file-store> needs a defined <cluster>."));
0702:
0703: if (cluster.getStore() != null)
0704: throw new ConfigException(
0705: L
0706: .l("<file-store> may not be used with a defined <persistent-store>. Use <use-persistent-store> instead."));
0707:
0708: StoreManager fileStore = cluster.createPrivateFileStore();
0709:
0710: _storeManager = fileStore;
0711:
0712: _isWebAppStore = true;
0713:
0714: return fileStore;
0715: }
0716:
0717: /**
0718: * Sets the jdbc store.
0719: */
0720: public StoreManager createJdbcStore() throws ConfigException {
0721: Cluster cluster = getCluster();
0722:
0723: if (cluster == null)
0724: throw new ConfigException(L
0725: .l("<jdbc-store> needs a defined <cluster>."));
0726:
0727: if (cluster.getStore() != null)
0728: throw new ConfigException(
0729: L
0730: .l("<jdbc-store> may not be used with a defined <persistent-store>. Use <use-persistent-store> instead."));
0731:
0732: _storeManager = cluster.createJdbcStore();
0733:
0734: _isWebAppStore = true;
0735:
0736: return _storeManager;
0737: }
0738:
0739: /**
0740: * Sets the tcp store.
0741: */
0742: public void setTcpStore(boolean isEnable) throws Exception {
0743: setClusterStore(isEnable);
0744: }
0745:
0746: /**
0747: * Sets the cluster store.
0748: */
0749: public void setClusterStore(boolean isEnable) throws Exception {
0750: if (!isEnable)
0751: return;
0752:
0753: Cluster cluster = getCluster();
0754:
0755: if (cluster == null)
0756: throw new ConfigException(L
0757: .l("<cluster-store> needs a defined <cluster>."));
0758:
0759: StoreManager store = cluster.getStore();
0760:
0761: if (store == null)
0762: throw new ConfigException(
0763: L
0764: .l("cluster-store in <session-config> requires a configured cluster-store in the <cluster>"));
0765:
0766: _storeManager = store;
0767: }
0768:
0769: /**
0770: * Sets the cluster store.
0771: */
0772: public void setUsePersistentStore(boolean enable) throws Exception {
0773: if (!enable)
0774: return;
0775:
0776: Cluster cluster = getCluster();
0777:
0778: if (cluster == null)
0779: throw new ConfigException(
0780: L
0781: .l("<use-persistent-store> needs a defined <cluster>."));
0782:
0783: StoreManager store = cluster.getStore();
0784:
0785: if (store == null) {
0786: try {
0787: Context ic = new InitialContext();
0788: store = (StoreManager) ic
0789: .lookup("java:comp/env/caucho/persistent-store");
0790: } catch (Throwable e) {
0791: log.log(Level.FINER, e.toString(), e);
0792: }
0793: }
0794:
0795: if (store != null) {
0796: } else if (!Config.evalBoolean("${resin.isProfessional()}")) {
0797: throw new ConfigException(
0798: L
0799: .l("use-persistent-store in <session-config> requires Resin professional."));
0800: } else
0801: throw new ConfigException(
0802: L
0803: .l("use-persistent-store in <session-config> requires a configured <persistent-store> in the <server>"));
0804:
0805: if (_isWebAppStore)
0806: throw new ConfigException(
0807: L
0808: .l("use-persistent-store may not be used with <jdbc-store> or <file-store>."));
0809:
0810: _storeManager = store;
0811: }
0812:
0813: /**
0814: * Returns the session factory.
0815: */
0816: public void setPersistentPath(Path path) {
0817: _persistentPath = path;
0818: }
0819:
0820: public String getDistributionId() {
0821: return _distributionId;
0822: }
0823:
0824: public void setDistributionId(String distributionId) {
0825: _distributionId = distributionId;
0826: }
0827:
0828: /**
0829: * Returns the default session timeout in milliseconds.
0830: */
0831: public long getSessionTimeout() {
0832: return _sessionTimeout;
0833: }
0834:
0835: /**
0836: * Set the default session timeout in minutes
0837: */
0838: public void setSessionTimeout(long timeout) {
0839: if (timeout <= 0 || Integer.MAX_VALUE / 2 < timeout)
0840: _sessionTimeout = Long.MAX_VALUE / 2;
0841: else
0842: _sessionTimeout = 60000L * timeout;
0843: }
0844:
0845: /**
0846: * Returns the idle time.
0847: */
0848: public long getMaxIdleTime() {
0849: return _sessionTimeout;
0850: }
0851:
0852: /**
0853: * Returns the maximum number of sessions.
0854: */
0855: public int getSessionMax() {
0856: return _sessionMax;
0857: }
0858:
0859: /**
0860: * Returns the maximum number of sessions.
0861: */
0862: public void setSessionMax(int max) {
0863: if (max < 1)
0864: throw new ConfigException(
0865: L
0866: .l(
0867: "session-max '{0}' is too small. session-max must be a positive number",
0868: max));
0869:
0870: _sessionMax = max;
0871: }
0872:
0873: /**
0874: * Returns true if sessions use the cookie header.
0875: */
0876: public boolean enableSessionCookies() {
0877: return _enableSessionCookies;
0878: }
0879:
0880: /**
0881: * Returns true if sessions use the cookie header.
0882: */
0883: public void setEnableCookies(boolean enableCookies) {
0884: _enableSessionCookies = enableCookies;
0885: }
0886:
0887: /**
0888: * Returns true if sessions can use the session rewriting.
0889: */
0890: public boolean enableSessionUrls() {
0891: return _enableSessionUrls;
0892: }
0893:
0894: /**
0895: * Returns true if sessions can use the session rewriting.
0896: */
0897: public void setEnableUrlRewriting(boolean enableUrls) {
0898: _enableSessionUrls = enableUrls;
0899: }
0900:
0901: /**
0902: * Returns the default cookie name.
0903: */
0904: public String getCookieName() {
0905: return _cookieName;
0906: }
0907:
0908: /**
0909: * Returns the SSL cookie name.
0910: */
0911: public String getSSLCookieName() {
0912: if (_sslCookieName != null)
0913: return _sslCookieName;
0914: else
0915: return _cookieName;
0916: }
0917:
0918: /**
0919: * Returns the default session cookie domain.
0920: */
0921: public String getCookieDomain() {
0922: return _cookieDomain;
0923: }
0924:
0925: /**
0926: * Sets the default session cookie domain.
0927: */
0928: public void setCookieDomain(String domain) {
0929: _cookieDomain = domain;
0930: }
0931:
0932: /**
0933: * Returns the max-age of the session cookie.
0934: */
0935: public long getCookieMaxAge() {
0936: return _cookieMaxAge;
0937: }
0938:
0939: /**
0940: * Sets the max-age of the session cookie.
0941: */
0942: public void setCookieMaxAge(Period maxAge) {
0943: _cookieMaxAge = maxAge.getPeriod();
0944: }
0945:
0946: /**
0947: * Returns the secure of the session cookie.
0948: */
0949: public boolean getCookieSecure() {
0950: if (_cookieSecure)
0951: return true;
0952: else
0953: return !_cookieName.equals(_sslCookieName);
0954: }
0955:
0956: /**
0957: * Sets the secure of the session cookie.
0958: */
0959: public void setCookieSecure(boolean secure) {
0960: _cookieSecure = secure;
0961: }
0962:
0963: /**
0964: * Returns the http-only of the session cookie.
0965: */
0966: public boolean isCookieHttpOnly() {
0967: if (_isCookieHttpOnly == SET_TRUE)
0968: return true;
0969: else if (_isCookieHttpOnly == SET_FALSE)
0970: return true;
0971: else
0972: return getWebApp().getCookieHttpOnly();
0973: }
0974:
0975: /**
0976: * Sets the http-only of the session cookie.
0977: */
0978: public void setCookieHttpOnly(boolean httpOnly) {
0979: _isCookieHttpOnly = httpOnly ? SET_TRUE : SET_FALSE;
0980: }
0981:
0982: /**
0983: * Sets the cookie length
0984: */
0985: public void setCookieLength(int cookieLength) {
0986: if (cookieLength < 7)
0987: cookieLength = 7;
0988:
0989: _cookieLength = cookieLength;
0990: }
0991:
0992: /**
0993: * Returns the cookie length.
0994: */
0995: public long getCookieLength() {
0996: return _cookieLength;
0997: }
0998:
0999: /**
1000: * Sets module session id generation.
1001: */
1002: public void setCookieModuloCluster(boolean isModulo) {
1003: _isModuloSessionId = isModulo;
1004: }
1005:
1006: /**
1007: * Sets module session id generation.
1008: */
1009: public void setCookieAppendServerIndex(boolean isAppend) {
1010: _isAppendServerIndex = isAppend;
1011: }
1012:
1013: /**
1014: * Sets module session id generation.
1015: */
1016: public boolean isCookieAppendServerIndex() {
1017: return _isAppendServerIndex;
1018: }
1019:
1020: public void init() {
1021: if (_sessionSaveMode == SAVE_ON_SHUTDOWN
1022: && (_alwaysSaveSession == SET_TRUE || _alwaysLoadSession == SET_TRUE))
1023: throw new ConfigException(
1024: L
1025: .l("save-mode='on-shutdown' cannot be used with <always-save-session/> or <always-load-session/>"));
1026: }
1027:
1028: public void start() throws Exception {
1029: _sessions = new LruCache<String, SessionImpl>(_sessionMax);
1030: _sessionIter = _sessions.values();
1031:
1032: if (_cluster == null)
1033: getCluster();
1034:
1035: if (_isWebAppStore) {
1036: // for backward compatibility
1037:
1038: if (_alwaysLoadSession == SET_TRUE)
1039: _storeManager.setAlwaysLoad(true);
1040: else if (_alwaysLoadSession == SET_FALSE)
1041: _storeManager.setAlwaysLoad(false);
1042:
1043: if (_alwaysSaveSession == SET_TRUE)
1044: _storeManager.setAlwaysSave(true);
1045: else if (_alwaysSaveSession == SET_FALSE)
1046: _storeManager.setAlwaysSave(false);
1047:
1048: _storeManager.init();
1049:
1050: _storeManager.updateIdleCheckInterval(_sessionTimeout);
1051: }
1052:
1053: if (_storeManager != null) {
1054: _sessionStore = _storeManager.createStore(_distributionId,
1055: this );
1056: _sessionStore.setMaxIdleTime(_sessionTimeout);
1057:
1058: if (_alwaysLoadSession == SET_TRUE)
1059: _sessionStore.setAlwaysLoad(true);
1060: else if (_alwaysLoadSession == SET_FALSE)
1061: _sessionStore.setAlwaysLoad(false);
1062:
1063: if (_alwaysSaveSession == SET_TRUE)
1064: _sessionStore.setAlwaysSave(true);
1065: else if (_alwaysSaveSession == SET_FALSE)
1066: _sessionStore.setAlwaysSave(false);
1067: }
1068:
1069: _alarm.queue(60000);
1070: }
1071:
1072: /**
1073: * Returns the session store.
1074: */
1075: public Store getSessionStore() {
1076: return _sessionStore;
1077: }
1078:
1079: /**
1080: * Returns true if the session exists in this manager.
1081: */
1082: public boolean containsSession(String id) {
1083: return _sessions.get(id) != null;
1084: }
1085:
1086: /**
1087: * Create a new session.
1088: *
1089: * @param oldId the id passed to the request. Reuse if possible.
1090: * @param now the current date
1091: * @param sessionGroup the srun index for this machine
1092: */
1093: public SessionImpl createSession(String oldId, long now,
1094: HttpServletRequest request, boolean fromCookie) {
1095: String id = oldId;
1096:
1097: if (id == null || id.length() < 4 || !isInSessionGroup(id)
1098: || !reuseSessionId(fromCookie)) {
1099: id = createSessionId(request, true);
1100: }
1101:
1102: SessionImpl session = create(id, now, true);
1103:
1104: if (session == null)
1105: return null;
1106:
1107: session.addUse();
1108:
1109: synchronized (_statisticsLock) {
1110: _sessionCreateCount++;
1111: }
1112:
1113: synchronized (session) {
1114: if (_sessionStore != null && id.equals(oldId))
1115: load(session, now);
1116: else
1117: session.create(now);
1118: }
1119:
1120: // after load so a reset doesn't clear any setting
1121: handleCreateListeners(session);
1122:
1123: return session;
1124: }
1125:
1126: /**
1127: * Creates a pseudo-random session id. If there's an old id and the
1128: * group matches, then use it because different webApps on the
1129: * same matchine should use the same cookie.
1130: *
1131: * @param sessionGroup possibly assigned by the web server
1132: */
1133: public String createSessionId(HttpServletRequest request) {
1134: return createSessionId(request, false);
1135: }
1136:
1137: /**
1138: * Creates a pseudo-random session id. If there's an old id and the
1139: * group matches, then use it because different webApps on the
1140: * same machine should use the same cookie.
1141: *
1142: * @param sessionGroup possibly assigned by the web server
1143: */
1144: public String createSessionId(HttpServletRequest request,
1145: boolean create) {
1146: String id;
1147:
1148: do {
1149: id = createSessionIdImpl(request);
1150: } while (create && getSession(id, 0, create, true) != null);
1151:
1152: if (id == null || id.equals(""))
1153: throw new RuntimeException();
1154:
1155: return id;
1156: }
1157:
1158: public String createSessionIdImpl(HttpServletRequest request) {
1159: StringBuffer cb = new StringBuffer();
1160: // this section is the host specific session index
1161: // the most random bit is the high bit
1162: int index = _srunIndex;
1163:
1164: // look at caucho.session-server-id for a hint of the owner
1165: Object owner = request.getAttribute("caucho.session-server-id");
1166: if (owner == null) {
1167: } else if (owner instanceof Number) {
1168: index = ((Number) owner).intValue();
1169: if (_srunLength <= index)
1170: index = _srunIndex;
1171: } else if (owner instanceof String) {
1172: ClusterServer server = _cluster.getServer((String) owner);
1173:
1174: if (server != null)
1175: index = server.getIndex();
1176: }
1177:
1178: if (index < 0)
1179: index = 0;
1180:
1181: int length = _cookieLength;
1182:
1183: addBackup(cb, index);
1184:
1185: length -= cb.length();
1186:
1187: long random = RandomUtil.getRandomLong();
1188:
1189: for (int i = 0; i < 11 && length-- > 0; i++) {
1190: cb.append(convert(random));
1191: random = random >> 6;
1192: }
1193:
1194: if (length > 0) {
1195: long time = Alarm.getCurrentTime();
1196: for (int i = 0; i < 7 && length-- > 0; i++) {
1197: cb.append(convert(time));
1198: time = time >> 6;
1199: }
1200: }
1201:
1202: while (length > 0) {
1203: random = RandomUtil.getRandomLong();
1204: for (int i = 0; i < 11 && length-- > 0; i++) {
1205: cb.append(convert(random));
1206: random = random >> 6;
1207: }
1208: }
1209:
1210: if (_isAppendServerIndex) {
1211: cb.append('.');
1212: cb.append((index + 1));
1213: }
1214:
1215: return cb.toString();
1216: }
1217:
1218: /**
1219: * Adds the primary/backup/third digits to the session id.
1220: */
1221: private void addBackup(StringBuffer cb, int index) {
1222: long backupCode;
1223:
1224: if (_selfServer != null)
1225: backupCode = _selfServer.getCluster().generateBackupCode(
1226: index);
1227: else
1228: backupCode = 0x000200010000L;
1229:
1230: addDigit(cb, (int) (backupCode & 0xffff));
1231: addDigit(cb, (int) ((backupCode >> 16) & 0xffff));
1232: addDigit(cb, (int) ((backupCode >> 32) & 0xffff));
1233: }
1234:
1235: private void addDigit(StringBuffer cb, int digit) {
1236: if (_srunLength <= 64 && !_isTwoDigitSessionIndex)
1237: cb.append(convert(digit));
1238: else {
1239: cb.append(convert(digit / 64));
1240: cb.append(convert(digit));
1241: }
1242: }
1243:
1244: /**
1245: * Returns a session from the session store, returning null if there's
1246: * no cached session.
1247: *
1248: * @param key the session id
1249: * @param now the time in milliseconds
1250: *
1251: * @return the cached session.
1252: */
1253: public SessionImpl getSession(String key, long now, boolean create,
1254: boolean fromCookie) {
1255: SessionImpl session;
1256: boolean isNew = false;
1257: boolean killSession = false;
1258:
1259: if (_sessions == null)
1260: return null;
1261:
1262: session = _sessions.get(key);
1263:
1264: if (session != null && !session.getId().equals(key))
1265: throw new IllegalStateException(key + " != "
1266: + session.getId());
1267:
1268: if (now <= 0) // just generating id
1269: return session;
1270:
1271: if (session != null && !session.addUse()) {
1272: session = null;
1273: }
1274:
1275: if (session == null && _sessionStore != null) {
1276: if (!isInSessionGroup(key))
1277: return null;
1278:
1279: session = create(key, now, create);
1280:
1281: if (!session.addUse())
1282: session = null;
1283: isNew = true;
1284: }
1285:
1286: if (session == null)
1287: return null;
1288:
1289: if (isNew) {
1290: killSession = !load(session, now);
1291: isNew = killSession;
1292: } else if (!session.load()) {
1293: // if the load failed, then the session died out from underneath
1294: session.reset(now);
1295: isNew = true;
1296: }
1297:
1298: if (killSession && (!create || !reuseSessionId(fromCookie))) {
1299: // XXX: session.setClosed();
1300: session.endUse();
1301: _sessions.remove(key);
1302: // XXX:
1303: session._isValid = false;
1304:
1305: return null;
1306: } else if (isNew)
1307: handleCreateListeners(session);
1308: else
1309: session.setAccess(now);
1310:
1311: return session;
1312: }
1313:
1314: public boolean isInSessionGroup(String id) {
1315: if (_srunLength == 0 || _srunGroup.length == 0)
1316: return true;
1317:
1318: int group = decode(id.charAt(0)) % _srunLength;
1319:
1320: for (int i = _srunGroup.length - 1; i >= 0; i--) {
1321: ClusterServer server = _srunGroup[i];
1322:
1323: if (server != null && group == server.getIndex())
1324: return true;
1325: }
1326:
1327: return false;
1328: }
1329:
1330: /**
1331: * Creates a session. It's already been established that the
1332: * key does not currently have a session.
1333: */
1334: private SessionImpl create(String key, long now, boolean isCreate) {
1335: SessionImpl session = new SessionImpl(this , key, now);
1336:
1337: // If another thread has created and stored a new session,
1338: // putIfNew will return the old session
1339: session = _sessions.putIfNew(key, session);
1340:
1341: if (!key.equals(session.getId()))
1342: throw new IllegalStateException(key + " != "
1343: + session.getId());
1344:
1345: Store sessionStore = _sessionStore;
1346: if (sessionStore != null) {
1347: ClusterObject clusterObject = sessionStore
1348: .createClusterObject(key);
1349: session.setClusterObject(clusterObject);
1350: }
1351:
1352: return session;
1353: }
1354:
1355: /**
1356: * Notification from the cluster.
1357: */
1358: public void notifyRemove(String id) {
1359: SessionImpl session = _sessions.remove(id);
1360:
1361: if (session != null)
1362: session.invalidateLru();
1363: }
1364:
1365: /**
1366: * Notification from the cluster.
1367: */
1368: public void notifyUpdate(String id) {
1369: }
1370:
1371: /**
1372: * Converts an integer to a printable character
1373: */
1374: private static char convert(long code) {
1375: code = code & 0x3f;
1376:
1377: if (code < 26)
1378: return (char) ('a' + code);
1379: else if (code < 52)
1380: return (char) ('A' + code - 26);
1381: else if (code < 62)
1382: return (char) ('0' + code - 52);
1383: else if (code == 62)
1384: return '_';
1385: else
1386: return '-';
1387: }
1388:
1389: public static int decode(int code) {
1390: return DECODE[code & 0x7f];
1391: }
1392:
1393: private void handleCreateListeners(SessionImpl session) {
1394: if (_listeners != null) {
1395: HttpSessionEvent event = new HttpSessionEvent(session);
1396:
1397: for (int i = 0; i < _listeners.size(); i++) {
1398: HttpSessionListener listener = _listeners.get(i);
1399:
1400: listener.sessionCreated(event);
1401: }
1402: }
1403: }
1404:
1405: /**
1406: * Loads the session from the backing store. The caller must synchronize
1407: * the session.
1408: *
1409: * @param session the session to load.
1410: * @param now current time in milliseconds.
1411: */
1412: private boolean load(SessionImpl session, long now) {
1413: try {
1414: // XXX: session.setNeedsLoad(false);
1415:
1416: /*
1417: if (session.getUseCount() > 1) {
1418: // if used by more than just us,
1419: return true;
1420: }
1421: else*/if (now <= 0) {
1422: return false;
1423: } else if (session.load()) {
1424: session.setAccess(now);
1425: return true;
1426: } else {
1427: session.create(now);
1428: }
1429: } catch (Exception e) {
1430: log.log(Level.FINE, e.toString(), e);
1431: session.reset(now);
1432: }
1433:
1434: return false;
1435: }
1436:
1437: /**
1438: * Adds a session from the cache.
1439: */
1440: void addSession(SessionImpl session) {
1441: _sessions.put(session.getId(), session);
1442: }
1443:
1444: /**
1445: * Removes a session from the cache.
1446: */
1447: void removeSession(SessionImpl session) {
1448: _sessions.remove(session.getId());
1449: }
1450:
1451: /**
1452: * Loads the session.
1453: *
1454: * @param in the input stream containing the serialized session
1455: * @param obj the session object to be deserialized
1456: */
1457: public void load(InputStream is, Object obj) throws IOException {
1458: SessionImpl session = (SessionImpl) obj;
1459:
1460: if (_isHessianSerialization) {
1461: Hessian2Input in = new Hessian2Input(is);
1462:
1463: session.load(in);
1464:
1465: in.close();
1466: } else {
1467: ObjectInputStream in = new DistributedObjectInputStream(is);
1468:
1469: session.load(in);
1470:
1471: in.close();
1472: }
1473: }
1474:
1475: /**
1476: * Checks if the session is empty.
1477: */
1478: public boolean isEmpty(Object obj) {
1479: SessionImpl session = (SessionImpl) obj;
1480:
1481: return session.isEmpty();
1482: }
1483:
1484: /**
1485: * Saves the session.
1486: */
1487: public void store(OutputStream os, Object obj) throws IOException {
1488: SessionImpl session = (SessionImpl) obj;
1489:
1490: if (_isHessianSerialization) {
1491: Hessian2Output out = new Hessian2Output(os);
1492:
1493: session.store(out);
1494:
1495: out.close();
1496: } else {
1497: ObjectOutputStream out = new ObjectOutputStream(os);
1498:
1499: session.store(out);
1500:
1501: out.close();
1502: }
1503: }
1504:
1505: /**
1506: * Timeout for reaping old sessions
1507: *
1508: * @return number of live sessions for stats
1509: */
1510: public void handleAlarm(Alarm alarm) {
1511: try {
1512: _sessionList.clear();
1513:
1514: int liveSessions = 0;
1515:
1516: if (_isClosed)
1517: return;
1518:
1519: long now = Alarm.getCurrentTime();
1520: long accessWindow = 0;
1521:
1522: if (_sessionStore != null)
1523: accessWindow = _sessionStore.getAccessWindowTime();
1524:
1525: synchronized (_sessions) {
1526: _sessionIter = _sessions.values(_sessionIter);
1527: while (_sessionIter.hasNext()) {
1528: SessionImpl session = _sessionIter.next();
1529:
1530: long maxIdleTime = session._maxInactiveInterval
1531: + accessWindow;
1532:
1533: if (session.inUse())
1534: liveSessions++;
1535: else if (session._accessTime + maxIdleTime < now)
1536: _sessionList.add(session);
1537: else
1538: liveSessions++;
1539: }
1540: }
1541:
1542: synchronized (_statisticsLock) {
1543: _sessionTimeoutCount += _sessionList.size();
1544: }
1545:
1546: for (int i = 0; i < _sessionList.size(); i++) {
1547: SessionImpl session = _sessionList.get(i);
1548:
1549: try {
1550: if (!session.isValid())
1551: continue;
1552:
1553: if (_storeManager == null) {
1554: // if no persistent store then invalidate
1555: // XXX: server/12cg - single signon shouldn't logout
1556: session.invalidateTimeout();
1557: } else if (session.getSrunIndex() != _srunIndex
1558: && _srunIndex >= 0) {
1559: if (log.isLoggable(Level.FINE))
1560: log.fine(session + " timeout (backup)");
1561:
1562: // if not the owner, then just remove
1563: _sessions.remove(session.getId());
1564: } else {
1565: session.invalidateTimeout();
1566: }
1567: } catch (Throwable e) {
1568: log.log(Level.FINE, e.toString(), e);
1569: }
1570: }
1571: } finally {
1572: if (!_isClosed)
1573: _alarm.queue(60000);
1574: }
1575: }
1576:
1577: /**
1578: * Cleans up the sessions when the WebApp shuts down gracefully.
1579: */
1580: public void close() {
1581: synchronized (this ) {
1582: if (_isClosed)
1583: return;
1584: _isClosed = true;
1585: }
1586:
1587: if (_sessions == null)
1588: return;
1589:
1590: _alarm.dequeue();
1591:
1592: ArrayList<SessionImpl> list = new ArrayList<SessionImpl>();
1593:
1594: boolean isError = false;
1595: // XXX: messy way of dealing with saveOnlyOnShutdown
1596: synchronized (_sessions) {
1597: _sessionIter = _sessions.values(_sessionIter);
1598: while (_sessionIter.hasNext()) {
1599: SessionImpl session = _sessionIter.next();
1600:
1601: if (session.isValid())
1602: list.add(session);
1603: }
1604:
1605: // XXX: if cleared here, will remove the session
1606: // _sessions.clear();
1607: }
1608:
1609: for (int i = list.size() - 1; i >= 0; i--) {
1610: SessionImpl session = list.get(i);
1611:
1612: if (log.isLoggable(Level.FINE))
1613: log.fine("close session " + session.getId());
1614:
1615: try {
1616: if (session.isValid()) {
1617: synchronized (session) {
1618: // server/016i, server/018x
1619: if (!session.isEmpty())
1620: session.saveOnShutdown();
1621: }
1622: }
1623:
1624: _sessions.remove(session.getId());
1625: } catch (Exception e) {
1626: if (!isError)
1627: log.log(Level.WARNING, "Can't store session: " + e,
1628: e);
1629: isError = true;
1630: }
1631: }
1632:
1633: if (_admin != null)
1634: _admin.unregister();
1635:
1636: _sessionList = null;
1637:
1638: /*
1639: if (_clusterManager != null)
1640: _clusterManager.removeContext(_distributionId);
1641: */
1642: }
1643:
1644: @Override
1645: public String toString() {
1646: if (_webApp != null)
1647: return "SessionManager[" + _webApp.getContextPath() + "]";
1648: else
1649: return "SessionManager[]";
1650: }
1651:
1652: static class DistributedObjectInputStream extends ObjectInputStream {
1653: private ClassLoader _loader;
1654:
1655: DistributedObjectInputStream(InputStream is) throws IOException {
1656: super (is);
1657:
1658: Thread thread = Thread.currentThread();
1659: _loader = thread.getContextClassLoader();
1660: }
1661:
1662: @Override
1663: protected Class resolveClass(ObjectStreamClass v)
1664: throws IOException, ClassNotFoundException {
1665: String name = v.getName();
1666:
1667: return Class.forName(name, false, _loader);
1668: }
1669: }
1670:
1671: static {
1672: DECODE = new int[128];
1673: for (int i = 0; i < 64; i++)
1674: DECODE[(int) convert(i)] = i;
1675: }
1676: }
|