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