0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: */
0017:
0018: package org.apache.catalina.session;
0019:
0020: import java.beans.PropertyChangeEvent;
0021: import java.beans.PropertyChangeListener;
0022: import java.io.IOException;
0023: import java.security.AccessController;
0024: import java.security.PrivilegedActionException;
0025: import java.security.PrivilegedExceptionAction;
0026: import org.apache.catalina.Container;
0027: import org.apache.catalina.Context;
0028: import org.apache.catalina.Lifecycle;
0029: import org.apache.catalina.LifecycleException;
0030: import org.apache.catalina.LifecycleListener;
0031: import org.apache.catalina.Session;
0032: import org.apache.catalina.Store;
0033: import org.apache.catalina.util.LifecycleSupport;
0034:
0035: import org.apache.catalina.security.SecurityUtil;
0036: import org.apache.juli.logging.Log;
0037: import org.apache.juli.logging.LogFactory;
0038:
0039: /**
0040: * Extends the <b>ManagerBase</b> class to implement most of the
0041: * functionality required by a Manager which supports any kind of
0042: * persistence, even if onlyfor restarts.
0043: * <p>
0044: * <b>IMPLEMENTATION NOTE</b>: Correct behavior of session storing and
0045: * reloading depends upon external calls to the <code>start()</code> and
0046: * <code>stop()</code> methods of this class at the correct times.
0047: *
0048: * @author Craig R. McClanahan
0049: * @author Jean-Francois Arcand
0050: * @version $Revision: 467222 $ $Date: 2006-10-24 05:17:11 +0200 (mar., 24 oct. 2006) $
0051: */
0052:
0053: public abstract class PersistentManagerBase extends ManagerBase
0054: implements Lifecycle, PropertyChangeListener {
0055:
0056: private static Log log = LogFactory
0057: .getLog(PersistentManagerBase.class);
0058:
0059: // ---------------------------------------------------- Security Classes
0060:
0061: private class PrivilegedStoreClear implements
0062: PrivilegedExceptionAction {
0063:
0064: PrivilegedStoreClear() {
0065: }
0066:
0067: public Object run() throws Exception {
0068: store.clear();
0069: return null;
0070: }
0071: }
0072:
0073: private class PrivilegedStoreRemove implements
0074: PrivilegedExceptionAction {
0075:
0076: private String id;
0077:
0078: PrivilegedStoreRemove(String id) {
0079: this .id = id;
0080: }
0081:
0082: public Object run() throws Exception {
0083: store.remove(id);
0084: return null;
0085: }
0086: }
0087:
0088: private class PrivilegedStoreLoad implements
0089: PrivilegedExceptionAction {
0090:
0091: private String id;
0092:
0093: PrivilegedStoreLoad(String id) {
0094: this .id = id;
0095: }
0096:
0097: public Object run() throws Exception {
0098: return store.load(id);
0099: }
0100: }
0101:
0102: private class PrivilegedStoreSave implements
0103: PrivilegedExceptionAction {
0104:
0105: private Session session;
0106:
0107: PrivilegedStoreSave(Session session) {
0108: this .session = session;
0109: }
0110:
0111: public Object run() throws Exception {
0112: store.save(session);
0113: return null;
0114: }
0115: }
0116:
0117: private class PrivilegedStoreKeys implements
0118: PrivilegedExceptionAction {
0119:
0120: PrivilegedStoreKeys() {
0121: }
0122:
0123: public Object run() throws Exception {
0124: return store.keys();
0125: }
0126: }
0127:
0128: // ----------------------------------------------------- Instance Variables
0129:
0130: /**
0131: * The descriptive information about this implementation.
0132: */
0133: private static final String info = "PersistentManagerBase/1.1";
0134:
0135: /**
0136: * The lifecycle event support for this component.
0137: */
0138: protected LifecycleSupport lifecycle = new LifecycleSupport(this );
0139:
0140: /**
0141: * The maximum number of active Sessions allowed, or -1 for no limit.
0142: */
0143: protected int maxActiveSessions = -1;
0144:
0145: /**
0146: * The descriptive name of this Manager implementation (for logging).
0147: */
0148: private static String name = "PersistentManagerBase";
0149:
0150: /**
0151: * Has this component been started yet?
0152: */
0153: protected boolean started = false;
0154:
0155: /**
0156: * Store object which will manage the Session store.
0157: */
0158: protected Store store = null;
0159:
0160: /**
0161: * Whether to save and reload sessions when the Manager <code>unload</code>
0162: * and <code>load</code> methods are called.
0163: */
0164: protected boolean saveOnRestart = true;
0165:
0166: /**
0167: * How long a session must be idle before it should be backed up.
0168: * -1 means sessions won't be backed up.
0169: */
0170: protected int maxIdleBackup = -1;
0171:
0172: /**
0173: * Minimum time a session must be idle before it is swapped to disk.
0174: * This overrides maxActiveSessions, to prevent thrashing if there are lots
0175: * of active sessions. Setting to -1 means it's ignored.
0176: */
0177: protected int minIdleSwap = -1;
0178:
0179: /**
0180: * The maximum time a session may be idle before it should be swapped
0181: * to file just on general principle. Setting this to -1 means sessions
0182: * should not be forced out.
0183: */
0184: protected int maxIdleSwap = -1;
0185:
0186: /**
0187: * Number of session creations that failed due to maxActiveSessions.
0188: */
0189: protected int rejectedSessions = 0;
0190:
0191: /**
0192: * Processing time during session expiration and passivation.
0193: */
0194: protected long processingTime = 0;
0195:
0196: // ------------------------------------------------------------- Properties
0197:
0198: /**
0199: * Indicates how many seconds old a session can get, after its last use in a
0200: * request, before it should be backed up to the store. -1 means sessions
0201: * are not backed up.
0202: */
0203: public int getMaxIdleBackup() {
0204:
0205: return maxIdleBackup;
0206:
0207: }
0208:
0209: /**
0210: * Sets the option to back sessions up to the Store after they
0211: * are used in a request. Sessions remain available in memory
0212: * after being backed up, so they are not passivated as they are
0213: * when swapped out. The value set indicates how old a session
0214: * may get (since its last use) before it must be backed up: -1
0215: * means sessions are not backed up.
0216: * <p>
0217: * Note that this is not a hard limit: sessions are checked
0218: * against this age limit periodically according to <b>processExpiresFrequency</b>.
0219: * This value should be considered to indicate when a session is
0220: * ripe for backing up.
0221: * <p>
0222: * So it is possible that a session may be idle for maxIdleBackup +
0223: * processExpiresFrequency * engine.backgroundProcessorDelay seconds, plus the time it takes to handle other
0224: * session expiration, swapping, etc. tasks.
0225: *
0226: * @param backup The number of seconds after their last accessed
0227: * time when they should be written to the Store.
0228: */
0229: public void setMaxIdleBackup(int backup) {
0230:
0231: if (backup == this .maxIdleBackup)
0232: return;
0233: int oldBackup = this .maxIdleBackup;
0234: this .maxIdleBackup = backup;
0235: support.firePropertyChange("maxIdleBackup", new Integer(
0236: oldBackup), new Integer(this .maxIdleBackup));
0237:
0238: }
0239:
0240: /**
0241: * The time in seconds after which a session should be swapped out of
0242: * memory to disk.
0243: */
0244: public int getMaxIdleSwap() {
0245:
0246: return maxIdleSwap;
0247:
0248: }
0249:
0250: /**
0251: * Sets the time in seconds after which a session should be swapped out of
0252: * memory to disk.
0253: */
0254: public void setMaxIdleSwap(int max) {
0255:
0256: if (max == this .maxIdleSwap)
0257: return;
0258: int oldMaxIdleSwap = this .maxIdleSwap;
0259: this .maxIdleSwap = max;
0260: support.firePropertyChange("maxIdleSwap", new Integer(
0261: oldMaxIdleSwap), new Integer(this .maxIdleSwap));
0262:
0263: }
0264:
0265: /**
0266: * The minimum time in seconds that a session must be idle before
0267: * it can be swapped out of memory, or -1 if it can be swapped out
0268: * at any time.
0269: */
0270: public int getMinIdleSwap() {
0271:
0272: return minIdleSwap;
0273:
0274: }
0275:
0276: /**
0277: * Sets the minimum time in seconds that a session must be idle before
0278: * it can be swapped out of memory due to maxActiveSession. Set it to -1
0279: * if it can be swapped out at any time.
0280: */
0281: public void setMinIdleSwap(int min) {
0282:
0283: if (this .minIdleSwap == min)
0284: return;
0285: int oldMinIdleSwap = this .minIdleSwap;
0286: this .minIdleSwap = min;
0287: support.firePropertyChange("minIdleSwap", new Integer(
0288: oldMinIdleSwap), new Integer(this .minIdleSwap));
0289:
0290: }
0291:
0292: /**
0293: * Set the Container with which this Manager has been associated. If it is a
0294: * Context (the usual case), listen for changes to the session timeout
0295: * property.
0296: *
0297: * @param container
0298: * The associated Container
0299: */
0300: public void setContainer(Container container) {
0301:
0302: // De-register from the old Container (if any)
0303: if ((this .container != null)
0304: && (this .container instanceof Context))
0305: ((Context) this .container)
0306: .removePropertyChangeListener(this );
0307:
0308: // Default processing provided by our superclass
0309: super .setContainer(container);
0310:
0311: // Register with the new Container (if any)
0312: if ((this .container != null)
0313: && (this .container instanceof Context)) {
0314: setMaxInactiveInterval(((Context) this .container)
0315: .getSessionTimeout() * 60);
0316: ((Context) this .container).addPropertyChangeListener(this );
0317: }
0318:
0319: }
0320:
0321: /**
0322: * Return descriptive information about this Manager implementation and
0323: * the corresponding version number, in the format
0324: * <code><description>/<version></code>.
0325: */
0326: public String getInfo() {
0327:
0328: return (info);
0329:
0330: }
0331:
0332: /**
0333: * Return true, if the session id is loaded in memory
0334: * otherwise false is returned
0335: *
0336: * @param id The session id for the session to be searched for
0337: */
0338: public boolean isLoaded(String id) {
0339: try {
0340: if (super .findSession(id) != null)
0341: return true;
0342: } catch (IOException e) {
0343: log.error("checking isLoaded for id, " + id + ", "
0344: + e.getMessage(), e);
0345: }
0346: return false;
0347: }
0348:
0349: /**
0350: * Return the maximum number of active Sessions allowed, or -1 for
0351: * no limit.
0352: */
0353: public int getMaxActiveSessions() {
0354:
0355: return (this .maxActiveSessions);
0356:
0357: }
0358:
0359: /**
0360: * Set the maximum number of actives Sessions allowed, or -1 for
0361: * no limit.
0362: *
0363: * @param max The new maximum number of sessions
0364: */
0365: public void setMaxActiveSessions(int max) {
0366:
0367: int oldMaxActiveSessions = this .maxActiveSessions;
0368: this .maxActiveSessions = max;
0369: support.firePropertyChange("maxActiveSessions", new Integer(
0370: oldMaxActiveSessions), new Integer(
0371: this .maxActiveSessions));
0372:
0373: }
0374:
0375: /**
0376: * Number of session creations that failed due to maxActiveSessions.
0377: *
0378: * @return The count
0379: */
0380: public int getRejectedSessions() {
0381: return rejectedSessions;
0382: }
0383:
0384: public void setRejectedSessions(int rejectedSessions) {
0385: this .rejectedSessions = rejectedSessions;
0386: }
0387:
0388: /**
0389: * Return the descriptive short name of this Manager implementation.
0390: */
0391: public String getName() {
0392:
0393: return (name);
0394:
0395: }
0396:
0397: /**
0398: * Get the started status.
0399: */
0400: protected boolean isStarted() {
0401:
0402: return started;
0403:
0404: }
0405:
0406: /**
0407: * Set the started flag
0408: */
0409: protected void setStarted(boolean started) {
0410:
0411: this .started = started;
0412:
0413: }
0414:
0415: /**
0416: * Set the Store object which will manage persistent Session
0417: * storage for this Manager.
0418: *
0419: * @param store the associated Store
0420: */
0421: public void setStore(Store store) {
0422: this .store = store;
0423: store.setManager(this );
0424:
0425: }
0426:
0427: /**
0428: * Return the Store object which manages persistent Session
0429: * storage for this Manager.
0430: */
0431: public Store getStore() {
0432:
0433: return (this .store);
0434:
0435: }
0436:
0437: /**
0438: * Indicates whether sessions are saved when the Manager is shut down
0439: * properly. This requires the unload() method to be called.
0440: */
0441: public boolean getSaveOnRestart() {
0442:
0443: return saveOnRestart;
0444:
0445: }
0446:
0447: /**
0448: * Set the option to save sessions to the Store when the Manager is
0449: * shut down, then loaded when the Manager starts again. If set to
0450: * false, any sessions found in the Store may still be picked up when
0451: * the Manager is started again.
0452: *
0453: * @param saveOnRestart true if sessions should be saved on restart, false if
0454: * they should be ignored.
0455: */
0456: public void setSaveOnRestart(boolean saveOnRestart) {
0457:
0458: if (saveOnRestart == this .saveOnRestart)
0459: return;
0460:
0461: boolean oldSaveOnRestart = this .saveOnRestart;
0462: this .saveOnRestart = saveOnRestart;
0463: support.firePropertyChange("saveOnRestart", new Boolean(
0464: oldSaveOnRestart), new Boolean(this .saveOnRestart));
0465:
0466: }
0467:
0468: // --------------------------------------------------------- Public Methods
0469:
0470: /**
0471: * Clear all sessions from the Store.
0472: */
0473: public void clearStore() {
0474:
0475: if (store == null)
0476: return;
0477:
0478: try {
0479: if (SecurityUtil.isPackageProtectionEnabled()) {
0480: try {
0481: AccessController
0482: .doPrivileged(new PrivilegedStoreClear());
0483: } catch (PrivilegedActionException ex) {
0484: Exception exception = ex.getException();
0485: log.error("Exception clearing the Store: "
0486: + exception);
0487: exception.printStackTrace();
0488: }
0489: } else {
0490: store.clear();
0491: }
0492: } catch (IOException e) {
0493: log.error("Exception clearing the Store: " + e);
0494: e.printStackTrace();
0495: }
0496:
0497: }
0498:
0499: /**
0500: * Implements the Manager interface, direct call to processExpires and processPersistenceChecks
0501: */
0502: public void processExpires() {
0503:
0504: long timeNow = System.currentTimeMillis();
0505: Session sessions[] = findSessions();
0506: int expireHere = 0;
0507: if (log.isDebugEnabled())
0508: log.debug("Start expire sessions " + getName() + " at "
0509: + timeNow + " sessioncount " + sessions.length);
0510: for (int i = 0; i < sessions.length; i++) {
0511: if (!sessions[i].isValid()) {
0512: expiredSessions++;
0513: expireHere++;
0514: }
0515: }
0516: processPersistenceChecks();
0517: if ((getStore() != null) && (getStore() instanceof StoreBase)) {
0518: ((StoreBase) getStore()).processExpires();
0519: }
0520:
0521: long timeEnd = System.currentTimeMillis();
0522: if (log.isDebugEnabled())
0523: log.debug("End expire sessions " + getName()
0524: + " processingTime " + (timeEnd - timeNow)
0525: + " expired sessions: " + expireHere);
0526: processingTime += (timeEnd - timeNow);
0527:
0528: }
0529:
0530: /**
0531: * Called by the background thread after active sessions have been checked
0532: * for expiration, to allow sessions to be swapped out, backed up, etc.
0533: */
0534: public void processPersistenceChecks() {
0535:
0536: processMaxIdleSwaps();
0537: processMaxActiveSwaps();
0538: processMaxIdleBackups();
0539:
0540: }
0541:
0542: /**
0543: * Return the active Session, associated with this Manager, with the
0544: * specified session id (if any); otherwise return <code>null</code>.
0545: * This method checks the persistence store if persistence is enabled,
0546: * otherwise just uses the functionality from ManagerBase.
0547: *
0548: * @param id The session id for the session to be returned
0549: *
0550: * @exception IllegalStateException if a new session cannot be
0551: * instantiated for any reason
0552: * @exception IOException if an input/output error occurs while
0553: * processing this request
0554: */
0555: public Session findSession(String id) throws IOException {
0556:
0557: Session session = super .findSession(id);
0558: if (session != null)
0559: return (session);
0560:
0561: // See if the Session is in the Store
0562: session = swapIn(id);
0563: return (session);
0564:
0565: }
0566:
0567: /**
0568: * Remove this Session from the active Sessions for this Manager,
0569: * but not from the Store. (Used by the PersistentValve)
0570: *
0571: * @param session Session to be removed
0572: */
0573: public void removeSuper(Session session) {
0574: super .remove(session);
0575: }
0576:
0577: /**
0578: * Load all sessions found in the persistence mechanism, assuming
0579: * they are marked as valid and have not passed their expiration
0580: * limit. If persistence is not supported, this method returns
0581: * without doing anything.
0582: * <p>
0583: * Note that by default, this method is not called by the MiddleManager
0584: * class. In order to use it, a subclass must specifically call it,
0585: * for example in the start() and/or processPersistenceChecks() methods.
0586: */
0587: public void load() {
0588:
0589: // Initialize our internal data structures
0590: sessions.clear();
0591:
0592: if (store == null)
0593: return;
0594:
0595: String[] ids = null;
0596: try {
0597: if (SecurityUtil.isPackageProtectionEnabled()) {
0598: try {
0599: ids = (String[]) AccessController
0600: .doPrivileged(new PrivilegedStoreKeys());
0601: } catch (PrivilegedActionException ex) {
0602: Exception exception = ex.getException();
0603: log.error("Exception in the Store during load: "
0604: + exception);
0605: exception.printStackTrace();
0606: }
0607: } else {
0608: ids = store.keys();
0609: }
0610: } catch (IOException e) {
0611: log.error("Can't load sessions from store, "
0612: + e.getMessage(), e);
0613: return;
0614: }
0615:
0616: int n = ids.length;
0617: if (n == 0)
0618: return;
0619:
0620: if (log.isDebugEnabled())
0621: log.debug(sm.getString("persistentManager.loading", String
0622: .valueOf(n)));
0623:
0624: for (int i = 0; i < n; i++)
0625: try {
0626: swapIn(ids[i]);
0627: } catch (IOException e) {
0628: log.error("Failed load session from store, "
0629: + e.getMessage(), e);
0630: }
0631:
0632: }
0633:
0634: /**
0635: * Remove this Session from the active Sessions for this Manager,
0636: * and from the Store.
0637: *
0638: * @param session Session to be removed
0639: */
0640: public void remove(Session session) {
0641:
0642: super .remove(session);
0643:
0644: if (store != null) {
0645: removeSession(session.getIdInternal());
0646: }
0647: }
0648:
0649: /**
0650: * Remove this Session from the active Sessions for this Manager,
0651: * and from the Store.
0652: *
0653: * @param id Session's id to be removed
0654: */
0655: protected void removeSession(String id) {
0656: try {
0657: if (SecurityUtil.isPackageProtectionEnabled()) {
0658: try {
0659: AccessController
0660: .doPrivileged(new PrivilegedStoreRemove(id));
0661: } catch (PrivilegedActionException ex) {
0662: Exception exception = ex.getException();
0663: log
0664: .error("Exception in the Store during removeSession: "
0665: + exception);
0666: exception.printStackTrace();
0667: }
0668: } else {
0669: store.remove(id);
0670: }
0671: } catch (IOException e) {
0672: log.error("Exception removing session " + e.getMessage());
0673: e.printStackTrace();
0674: }
0675: }
0676:
0677: /**
0678: * Save all currently active sessions in the appropriate persistence
0679: * mechanism, if any. If persistence is not supported, this method
0680: * returns without doing anything.
0681: * <p>
0682: * Note that by default, this method is not called by the MiddleManager
0683: * class. In order to use it, a subclass must specifically call it,
0684: * for example in the stop() and/or processPersistenceChecks() methods.
0685: */
0686: public void unload() {
0687:
0688: if (store == null)
0689: return;
0690:
0691: Session sessions[] = findSessions();
0692: int n = sessions.length;
0693: if (n == 0)
0694: return;
0695:
0696: if (log.isDebugEnabled())
0697: log.debug(sm.getString("persistentManager.unloading",
0698: String.valueOf(n)));
0699:
0700: for (int i = 0; i < n; i++)
0701: try {
0702: swapOut(sessions[i]);
0703: } catch (IOException e) {
0704: ; // This is logged in writeSession()
0705: }
0706:
0707: }
0708:
0709: // ------------------------------------------------------ Protected Methods
0710:
0711: /**
0712: * Look for a session in the Store and, if found, restore
0713: * it in the Manager's list of active sessions if appropriate.
0714: * The session will be removed from the Store after swapping
0715: * in, but will not be added to the active session list if it
0716: * is invalid or past its expiration.
0717: */
0718: protected Session swapIn(String id) throws IOException {
0719:
0720: if (store == null)
0721: return null;
0722:
0723: Session session = null;
0724: try {
0725: if (SecurityUtil.isPackageProtectionEnabled()) {
0726: try {
0727: session = (Session) AccessController
0728: .doPrivileged(new PrivilegedStoreLoad(id));
0729: } catch (PrivilegedActionException ex) {
0730: Exception exception = ex.getException();
0731: log.error("Exception in the Store during swapIn: "
0732: + exception);
0733: if (exception instanceof IOException) {
0734: throw (IOException) exception;
0735: } else if (exception instanceof ClassNotFoundException) {
0736: throw (ClassNotFoundException) exception;
0737: }
0738: }
0739: } else {
0740: session = store.load(id);
0741: }
0742: } catch (ClassNotFoundException e) {
0743: log.error(sm.getString(
0744: "persistentManager.deserializeError", id, e));
0745: throw new IllegalStateException(sm.getString(
0746: "persistentManager.deserializeError", id, e));
0747: }
0748:
0749: if (session == null)
0750: return (null);
0751:
0752: if (!session.isValid()) {
0753: log.error("session swapped in is invalid or expired");
0754: session.expire();
0755: removeSession(id);
0756: return (null);
0757: }
0758:
0759: if (log.isDebugEnabled())
0760: log.debug(sm.getString("persistentManager.swapIn", id));
0761:
0762: session.setManager(this );
0763: // make sure the listeners know about it.
0764: ((StandardSession) session).tellNew();
0765: add(session);
0766: ((StandardSession) session).activate();
0767: session.endAccess();
0768:
0769: return (session);
0770:
0771: }
0772:
0773: /**
0774: * Remove the session from the Manager's list of active
0775: * sessions and write it out to the Store. If the session
0776: * is past its expiration or invalid, this method does
0777: * nothing.
0778: *
0779: * @param session The Session to write out.
0780: */
0781: protected void swapOut(Session session) throws IOException {
0782:
0783: if (store == null || !session.isValid()) {
0784: return;
0785: }
0786:
0787: ((StandardSession) session).passivate();
0788: writeSession(session);
0789: super .remove(session);
0790: session.recycle();
0791:
0792: }
0793:
0794: /**
0795: * Write the provided session to the Store without modifying
0796: * the copy in memory or triggering passivation events. Does
0797: * nothing if the session is invalid or past its expiration.
0798: */
0799: protected void writeSession(Session session) throws IOException {
0800:
0801: if (store == null || !session.isValid()) {
0802: return;
0803: }
0804:
0805: try {
0806: if (SecurityUtil.isPackageProtectionEnabled()) {
0807: try {
0808: AccessController
0809: .doPrivileged(new PrivilegedStoreSave(
0810: session));
0811: } catch (PrivilegedActionException ex) {
0812: Exception exception = ex.getException();
0813: log
0814: .error("Exception in the Store during writeSession: "
0815: + exception);
0816: exception.printStackTrace();
0817: }
0818: } else {
0819: store.save(session);
0820: }
0821: } catch (IOException e) {
0822: log.error(sm.getString("persistentManager.serializeError",
0823: session.getIdInternal(), e));
0824: throw e;
0825: }
0826:
0827: }
0828:
0829: // ------------------------------------------------------ Lifecycle Methods
0830:
0831: /**
0832: * Add a lifecycle event listener to this component.
0833: *
0834: * @param listener The listener to add
0835: */
0836: public void addLifecycleListener(LifecycleListener listener) {
0837:
0838: lifecycle.addLifecycleListener(listener);
0839:
0840: }
0841:
0842: /**
0843: * Get the lifecycle listeners associated with this lifecycle. If this
0844: * Lifecycle has no listeners registered, a zero-length array is returned.
0845: */
0846: public LifecycleListener[] findLifecycleListeners() {
0847:
0848: return lifecycle.findLifecycleListeners();
0849:
0850: }
0851:
0852: /**
0853: * Remove a lifecycle event listener from this component.
0854: *
0855: * @param listener The listener to remove
0856: */
0857: public void removeLifecycleListener(LifecycleListener listener) {
0858:
0859: lifecycle.removeLifecycleListener(listener);
0860:
0861: }
0862:
0863: /**
0864: * Prepare for the beginning of active use of the public methods of this
0865: * component. This method should be called after <code>configure()</code>,
0866: * and before any of the public methods of the component are utilized.
0867: *
0868: * @exception LifecycleException if this component detects a fatal error
0869: * that prevents this component from being used
0870: */
0871: public void start() throws LifecycleException {
0872:
0873: // Validate and update our current component state
0874: if (started) {
0875: log.info(sm.getString("standardManager.alreadyStarted"));
0876: return;
0877: }
0878: if (!initialized)
0879: init();
0880:
0881: lifecycle.fireLifecycleEvent(START_EVENT, null);
0882: started = true;
0883:
0884: // Force initialization of the random number generator
0885: if (log.isDebugEnabled())
0886: log.debug("Force random number initialization starting");
0887: String dummy = generateSessionId();
0888: if (log.isDebugEnabled())
0889: log.debug("Force random number initialization completed");
0890:
0891: if (store == null)
0892: log.error("No Store configured, persistence disabled");
0893: else if (store instanceof Lifecycle)
0894: ((Lifecycle) store).start();
0895:
0896: }
0897:
0898: /**
0899: * Gracefully terminate the active use of the public methods of this
0900: * component. This method should be the last one called on a given
0901: * instance of this component.
0902: *
0903: * @exception LifecycleException if this component detects a fatal error
0904: * that needs to be reported
0905: */
0906: public void stop() throws LifecycleException {
0907:
0908: if (log.isDebugEnabled())
0909: log.debug("Stopping");
0910:
0911: // Validate and update our current component state
0912: if (!isStarted()) {
0913: log.info(sm.getString("standardManager.notStarted"));
0914: return;
0915: }
0916:
0917: lifecycle.fireLifecycleEvent(STOP_EVENT, null);
0918: setStarted(false);
0919:
0920: if (getStore() != null && saveOnRestart) {
0921: unload();
0922: } else {
0923: // Expire all active sessions
0924: Session sessions[] = findSessions();
0925: for (int i = 0; i < sessions.length; i++) {
0926: StandardSession session = (StandardSession) sessions[i];
0927: if (!session.isValid())
0928: continue;
0929: session.expire();
0930: }
0931: }
0932:
0933: if (getStore() != null && getStore() instanceof Lifecycle)
0934: ((Lifecycle) getStore()).stop();
0935:
0936: // Require a new random number generator if we are restarted
0937: this .random = null;
0938:
0939: if (initialized)
0940: destroy();
0941:
0942: }
0943:
0944: // ----------------------------------------- PropertyChangeListener Methods
0945:
0946: /**
0947: * Process property change events from our associated Context.
0948: *
0949: * @param event The property change event that has occurred
0950: */
0951: public void propertyChange(PropertyChangeEvent event) {
0952:
0953: // Validate the source of this event
0954: if (!(event.getSource() instanceof Context))
0955: return;
0956: Context context = (Context) event.getSource();
0957:
0958: // Process a relevant property change
0959: if (event.getPropertyName().equals("sessionTimeout")) {
0960: try {
0961: setMaxInactiveInterval(((Integer) event.getNewValue())
0962: .intValue() * 60);
0963: } catch (NumberFormatException e) {
0964: log.error(sm.getString(
0965: "standardManager.sessionTimeout", event
0966: .getNewValue().toString()));
0967: }
0968: }
0969:
0970: }
0971:
0972: // ------------------------------------------------------ Protected Methods
0973:
0974: /**
0975: * Swap idle sessions out to Store if they are idle too long.
0976: */
0977: protected void processMaxIdleSwaps() {
0978:
0979: if (!isStarted() || maxIdleSwap < 0)
0980: return;
0981:
0982: Session sessions[] = findSessions();
0983: long timeNow = System.currentTimeMillis();
0984:
0985: // Swap out all sessions idle longer than maxIdleSwap
0986: // FIXME: What's preventing us from mangling a session during
0987: // a request?
0988: if (maxIdleSwap >= 0) {
0989: for (int i = 0; i < sessions.length; i++) {
0990: StandardSession session = (StandardSession) sessions[i];
0991: if (!session.isValid())
0992: continue;
0993: int timeIdle = // Truncate, do not round up
0994: (int) ((timeNow - session.getLastAccessedTime()) / 1000L);
0995: if (timeIdle > maxIdleSwap && timeIdle > minIdleSwap) {
0996: if (log.isDebugEnabled())
0997: log.debug(sm.getString(
0998: "persistentManager.swapMaxIdle",
0999: session.getIdInternal(), new Integer(
1000: timeIdle)));
1001: try {
1002: swapOut(session);
1003: } catch (IOException e) {
1004: ; // This is logged in writeSession()
1005: }
1006: }
1007: }
1008: }
1009:
1010: }
1011:
1012: /**
1013: * Swap idle sessions out to Store if too many are active
1014: */
1015: protected void processMaxActiveSwaps() {
1016:
1017: if (!isStarted() || getMaxActiveSessions() < 0)
1018: return;
1019:
1020: Session sessions[] = findSessions();
1021:
1022: // FIXME: Smarter algorithm (LRU)
1023: if (getMaxActiveSessions() >= sessions.length)
1024: return;
1025:
1026: if (log.isDebugEnabled())
1027: log.debug(sm.getString("persistentManager.tooManyActive",
1028: new Integer(sessions.length)));
1029:
1030: int toswap = sessions.length - getMaxActiveSessions();
1031: long timeNow = System.currentTimeMillis();
1032:
1033: for (int i = 0; i < sessions.length && toswap > 0; i++) {
1034: int timeIdle = // Truncate, do not round up
1035: (int) ((timeNow - sessions[i].getLastAccessedTime()) / 1000L);
1036: if (timeIdle > minIdleSwap) {
1037: if (log.isDebugEnabled())
1038: log.debug(sm.getString(
1039: "persistentManager.swapTooManyActive",
1040: sessions[i].getIdInternal(), new Integer(
1041: timeIdle)));
1042: try {
1043: swapOut(sessions[i]);
1044: } catch (IOException e) {
1045: ; // This is logged in writeSession()
1046: }
1047: toswap--;
1048: }
1049: }
1050:
1051: }
1052:
1053: /**
1054: * Back up idle sessions.
1055: */
1056: protected void processMaxIdleBackups() {
1057:
1058: if (!isStarted() || maxIdleBackup < 0)
1059: return;
1060:
1061: Session sessions[] = findSessions();
1062: long timeNow = System.currentTimeMillis();
1063:
1064: // Back up all sessions idle longer than maxIdleBackup
1065: if (maxIdleBackup >= 0) {
1066: for (int i = 0; i < sessions.length; i++) {
1067: StandardSession session = (StandardSession) sessions[i];
1068: if (!session.isValid())
1069: continue;
1070: int timeIdle = // Truncate, do not round up
1071: (int) ((timeNow - session.getLastAccessedTime()) / 1000L);
1072: if (timeIdle > maxIdleBackup) {
1073: if (log.isDebugEnabled())
1074: log.debug(sm.getString(
1075: "persistentManager.backupMaxIdle",
1076: session.getIdInternal(), new Integer(
1077: timeIdle)));
1078:
1079: try {
1080: writeSession(session);
1081: } catch (IOException e) {
1082: ; // This is logged in writeSession()
1083: }
1084: }
1085: }
1086: }
1087:
1088: }
1089:
1090: }
|