| /* |
| * Copyright 1999,2004 The Apache Software Foundation. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| |
| package org.apache.catalina.session; |
| |
| import java.beans.PropertyChangeEvent; |
| import java.beans.PropertyChangeListener; |
| import java.io.IOException; |
| import java.security.AccessController; |
| import java.security.PrivilegedActionException; |
| import java.security.PrivilegedExceptionAction; |
| import org.apache.catalina.Container; |
| import org.apache.catalina.Context; |
| import org.apache.catalina.Lifecycle; |
| import org.apache.catalina.LifecycleException; |
| import org.apache.catalina.LifecycleListener; |
| import org.apache.catalina.Session; |
| import org.apache.catalina.Store; |
| import org.apache.catalina.util.LifecycleSupport; |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| |
| import org.apache.catalina.security.SecurityUtil; |
| /** |
| * Extends the <b>ManagerBase</b> class to implement most of the |
| * functionality required by a Manager which supports any kind of |
| * persistence, even if onlyfor restarts. |
| * <p> |
| * <b>IMPLEMENTATION NOTE</b>: Correct behavior of session storing and |
| * reloading depends upon external calls to the <code>start()</code> and |
| * <code>stop()</code> methods of this class at the correct times. |
| * |
| * @author Craig R. McClanahan |
| * @author Jean-Francois Arcand |
| * @version $Revision$ $Date$ |
| */ |
| |
| public abstract class PersistentManagerBase |
| extends ManagerBase |
| implements Lifecycle, PropertyChangeListener { |
| |
| private static Log log = LogFactory.getLog(PersistentManagerBase.class); |
| |
| // ---------------------------------------------------- Security Classes |
| |
| private class PrivilegedStoreClear |
| implements PrivilegedExceptionAction { |
| |
| PrivilegedStoreClear() { |
| } |
| |
| public Object run() throws Exception{ |
| store.clear(); |
| return null; |
| } |
| } |
| |
| private class PrivilegedStoreRemove |
| implements PrivilegedExceptionAction { |
| |
| private String id; |
| |
| PrivilegedStoreRemove(String id) { |
| this.id = id; |
| } |
| |
| public Object run() throws Exception{ |
| store.remove(id); |
| return null; |
| } |
| } |
| |
| private class PrivilegedStoreLoad |
| implements PrivilegedExceptionAction { |
| |
| private String id; |
| |
| PrivilegedStoreLoad(String id) { |
| this.id = id; |
| } |
| |
| public Object run() throws Exception{ |
| return store.load(id); |
| } |
| } |
| |
| private class PrivilegedStoreSave |
| implements PrivilegedExceptionAction { |
| |
| private Session session; |
| |
| PrivilegedStoreSave(Session session) { |
| this.session = session; |
| } |
| |
| public Object run() throws Exception{ |
| store.save(session); |
| return null; |
| } |
| } |
| |
| private class PrivilegedStoreKeys |
| implements PrivilegedExceptionAction { |
| |
| PrivilegedStoreKeys() { |
| } |
| |
| public Object run() throws Exception{ |
| return store.keys(); |
| } |
| } |
| |
| // ----------------------------------------------------- Instance Variables |
| |
| |
| /** |
| * The descriptive information about this implementation. |
| */ |
| private static final String info = "PersistentManagerBase/1.1"; |
| |
| |
| /** |
| * The lifecycle event support for this component. |
| */ |
| protected LifecycleSupport lifecycle = new LifecycleSupport(this); |
| |
| |
| /** |
| * The maximum number of active Sessions allowed, or -1 for no limit. |
| */ |
| protected int maxActiveSessions = -1; |
| |
| |
| /** |
| * The descriptive name of this Manager implementation (for logging). |
| */ |
| private static String name = "PersistentManagerBase"; |
| |
| |
| /** |
| * Has this component been started yet? |
| */ |
| protected boolean started = false; |
| |
| |
| /** |
| * Store object which will manage the Session store. |
| */ |
| protected Store store = null; |
| |
| |
| /** |
| * Whether to save and reload sessions when the Manager <code>unload</code> |
| * and <code>load</code> methods are called. |
| */ |
| protected boolean saveOnRestart = true; |
| |
| |
| /** |
| * How long a session must be idle before it should be backed up. |
| * -1 means sessions won't be backed up. |
| */ |
| protected int maxIdleBackup = -1; |
| |
| |
| /** |
| * Minimum time a session must be idle before it is swapped to disk. |
| * This overrides maxActiveSessions, to prevent thrashing if there are lots |
| * of active sessions. Setting to -1 means it's ignored. |
| */ |
| protected int minIdleSwap = -1; |
| |
| /** |
| * The maximum time a session may be idle before it should be swapped |
| * to file just on general principle. Setting this to -1 means sessions |
| * should not be forced out. |
| */ |
| protected int maxIdleSwap = -1; |
| |
| |
| /** |
| * Number of session creations that failed due to maxActiveSessions. |
| */ |
| protected int rejectedSessions = 0; |
| |
| |
| /** |
| * Processing time during session expiration and passivation. |
| */ |
| protected long processingTime = 0; |
| |
| |
| // ------------------------------------------------------------- Properties |
| |
| |
| |
| |
| |
| /** |
| * Indicates how many seconds old a session can get, after its last use in a |
| * request, before it should be backed up to the store. -1 means sessions |
| * are not backed up. |
| */ |
| public int getMaxIdleBackup() { |
| |
| return maxIdleBackup; |
| |
| } |
| |
| |
| /** |
| * Sets the option to back sessions up to the Store after they |
| * are used in a request. Sessions remain available in memory |
| * after being backed up, so they are not passivated as they are |
| * when swapped out. The value set indicates how old a session |
| * may get (since its last use) before it must be backed up: -1 |
| * means sessions are not backed up. |
| * <p> |
| * Note that this is not a hard limit: sessions are checked |
| * against this age limit periodically according to <b>processExpiresFrequency</b>. |
| * This value should be considered to indicate when a session is |
| * ripe for backing up. |
| * <p> |
| * So it is possible that a session may be idle for maxIdleBackup + |
| * processExpiresFrequency * engine.backgroundProcessorDelay seconds, plus the time it takes to handle other |
| * session expiration, swapping, etc. tasks. |
| * |
| * @param backup The number of seconds after their last accessed |
| * time when they should be written to the Store. |
| */ |
| public void setMaxIdleBackup (int backup) { |
| |
| if (backup == this.maxIdleBackup) |
| return; |
| int oldBackup = this.maxIdleBackup; |
| this.maxIdleBackup = backup; |
| support.firePropertyChange("maxIdleBackup", |
| new Integer(oldBackup), |
| new Integer(this.maxIdleBackup)); |
| |
| } |
| |
| |
| /** |
| * The time in seconds after which a session should be swapped out of |
| * memory to disk. |
| */ |
| public int getMaxIdleSwap() { |
| |
| return maxIdleSwap; |
| |
| } |
| |
| |
| /** |
| * Sets the time in seconds after which a session should be swapped out of |
| * memory to disk. |
| */ |
| public void setMaxIdleSwap(int max) { |
| |
| if (max == this.maxIdleSwap) |
| return; |
| int oldMaxIdleSwap = this.maxIdleSwap; |
| this.maxIdleSwap = max; |
| support.firePropertyChange("maxIdleSwap", |
| new Integer(oldMaxIdleSwap), |
| new Integer(this.maxIdleSwap)); |
| |
| } |
| |
| |
| /** |
| * The minimum time in seconds that a session must be idle before |
| * it can be swapped out of memory, or -1 if it can be swapped out |
| * at any time. |
| */ |
| public int getMinIdleSwap() { |
| |
| return minIdleSwap; |
| |
| } |
| |
| |
| /** |
| * Sets the minimum time in seconds that a session must be idle before |
| * it can be swapped out of memory due to maxActiveSession. Set it to -1 |
| * if it can be swapped out at any time. |
| */ |
| public void setMinIdleSwap(int min) { |
| |
| if (this.minIdleSwap == min) |
| return; |
| int oldMinIdleSwap = this.minIdleSwap; |
| this.minIdleSwap = min; |
| support.firePropertyChange("minIdleSwap", |
| new Integer(oldMinIdleSwap), |
| new Integer(this.minIdleSwap)); |
| |
| } |
| |
| |
| /** |
| * Set the Container with which this Manager has been associated. If it is a |
| * Context (the usual case), listen for changes to the session timeout |
| * property. |
| * |
| * @param container |
| * The associated Container |
| */ |
| public void setContainer(Container container) { |
| |
| // De-register from the old Container (if any) |
| if ((this.container != null) && (this.container instanceof Context)) |
| ((Context) this.container).removePropertyChangeListener(this); |
| |
| // Default processing provided by our superclass |
| super.setContainer(container); |
| |
| // Register with the new Container (if any) |
| if ((this.container != null) && (this.container instanceof Context)) { |
| setMaxInactiveInterval |
| ( ((Context) this.container).getSessionTimeout()*60 ); |
| ((Context) this.container).addPropertyChangeListener(this); |
| } |
| |
| } |
| |
| |
| /** |
| * Return descriptive information about this Manager implementation and |
| * the corresponding version number, in the format |
| * <code><description>/<version></code>. |
| */ |
| public String getInfo() { |
| |
| return (info); |
| |
| } |
| |
| |
| /** |
| * Return true, if the session id is loaded in memory |
| * otherwise false is returned |
| * |
| * @param id The session id for the session to be searched for |
| */ |
| public boolean isLoaded( String id ){ |
| try { |
| if ( super.findSession(id) != null ) |
| return true; |
| } catch (IOException e) { |
| log.error("checking isLoaded for id, " + id + ", "+e.getMessage(), e); |
| } |
| return false; |
| } |
| |
| |
| /** |
| * Return the maximum number of active Sessions allowed, or -1 for |
| * no limit. |
| */ |
| public int getMaxActiveSessions() { |
| |
| return (this.maxActiveSessions); |
| |
| } |
| |
| |
| /** |
| * Set the maximum number of actives Sessions allowed, or -1 for |
| * no limit. |
| * |
| * @param max The new maximum number of sessions |
| */ |
| public void setMaxActiveSessions(int max) { |
| |
| int oldMaxActiveSessions = this.maxActiveSessions; |
| this.maxActiveSessions = max; |
| support.firePropertyChange("maxActiveSessions", |
| new Integer(oldMaxActiveSessions), |
| new Integer(this.maxActiveSessions)); |
| |
| } |
| |
| |
| /** |
| * Number of session creations that failed due to maxActiveSessions. |
| * |
| * @return The count |
| */ |
| public int getRejectedSessions() { |
| return rejectedSessions; |
| } |
| |
| |
| public void setRejectedSessions(int rejectedSessions) { |
| this.rejectedSessions = rejectedSessions; |
| } |
| |
| /** |
| * Return the descriptive short name of this Manager implementation. |
| */ |
| public String getName() { |
| |
| return (name); |
| |
| } |
| |
| |
| /** |
| * Get the started status. |
| */ |
| protected boolean isStarted() { |
| |
| return started; |
| |
| } |
| |
| |
| /** |
| * Set the started flag |
| */ |
| protected void setStarted(boolean started) { |
| |
| this.started = started; |
| |
| } |
| |
| |
| /** |
| * Set the Store object which will manage persistent Session |
| * storage for this Manager. |
| * |
| * @param store the associated Store |
| */ |
| public void setStore(Store store) { |
| this.store = store; |
| store.setManager(this); |
| |
| } |
| |
| |
| /** |
| * Return the Store object which manages persistent Session |
| * storage for this Manager. |
| */ |
| public Store getStore() { |
| |
| return (this.store); |
| |
| } |
| |
| |
| |
| /** |
| * Indicates whether sessions are saved when the Manager is shut down |
| * properly. This requires the unload() method to be called. |
| */ |
| public boolean getSaveOnRestart() { |
| |
| return saveOnRestart; |
| |
| } |
| |
| |
| /** |
| * Set the option to save sessions to the Store when the Manager is |
| * shut down, then loaded when the Manager starts again. If set to |
| * false, any sessions found in the Store may still be picked up when |
| * the Manager is started again. |
| * |
| * @param saveOnRestart true if sessions should be saved on restart, false if |
| * they should be ignored. |
| */ |
| public void setSaveOnRestart(boolean saveOnRestart) { |
| |
| if (saveOnRestart == this.saveOnRestart) |
| return; |
| |
| boolean oldSaveOnRestart = this.saveOnRestart; |
| this.saveOnRestart = saveOnRestart; |
| support.firePropertyChange("saveOnRestart", |
| new Boolean(oldSaveOnRestart), |
| new Boolean(this.saveOnRestart)); |
| |
| } |
| |
| |
| // --------------------------------------------------------- Public Methods |
| |
| |
| /** |
| * Clear all sessions from the Store. |
| */ |
| public void clearStore() { |
| |
| if (store == null) |
| return; |
| |
| try { |
| if (SecurityUtil.isPackageProtectionEnabled()){ |
| try{ |
| AccessController.doPrivileged(new PrivilegedStoreClear()); |
| }catch(PrivilegedActionException ex){ |
| Exception exception = ex.getException(); |
| log.error("Exception clearing the Store: " + exception); |
| exception.printStackTrace(); |
| } |
| } else { |
| store.clear(); |
| } |
| } catch (IOException e) { |
| log.error("Exception clearing the Store: " + e); |
| e.printStackTrace(); |
| } |
| |
| } |
| |
| |
| /** |
| * Implements the Manager interface, direct call to processExpires and processPersistenceChecks |
| */ |
| public void processExpires() { |
| |
| long timeNow = System.currentTimeMillis(); |
| Session sessions[] = findSessions(); |
| int expireHere = 0 ; |
| if(log.isDebugEnabled()) |
| log.debug("Start expire sessions " + getName() + " at " + timeNow + " sessioncount " + sessions.length); |
| for (int i = 0; i < sessions.length; i++) { |
| if (!sessions[i].isValid()) { |
| expiredSessions++; |
| expireHere++; |
| } |
| } |
| processPersistenceChecks(); |
| if ((getStore() != null) && (getStore() instanceof StoreBase)) { |
| ((StoreBase) getStore()).processExpires(); |
| } |
| |
| long timeEnd = System.currentTimeMillis(); |
| if(log.isDebugEnabled()) |
| log.debug("End expire sessions " + getName() + " processingTime " + (timeEnd - timeNow) + " expired sessions: " + expireHere); |
| processingTime += (timeEnd - timeNow); |
| |
| } |
| |
| |
| /** |
| * Called by the background thread after active sessions have been checked |
| * for expiration, to allow sessions to be swapped out, backed up, etc. |
| */ |
| public void processPersistenceChecks() { |
| |
| processMaxIdleSwaps(); |
| processMaxActiveSwaps(); |
| processMaxIdleBackups(); |
| |
| } |
| |
| |
| /** |
| * Return the active Session, associated with this Manager, with the |
| * specified session id (if any); otherwise return <code>null</code>. |
| * This method checks the persistence store if persistence is enabled, |
| * otherwise just uses the functionality from ManagerBase. |
| * |
| * @param id The session id for the session to be returned |
| * |
| * @exception IllegalStateException if a new session cannot be |
| * instantiated for any reason |
| * @exception IOException if an input/output error occurs while |
| * processing this request |
| */ |
| public Session findSession(String id) throws IOException { |
| |
| Session session = super.findSession(id); |
| if (session != null) |
| return (session); |
| |
| // See if the Session is in the Store |
| session = swapIn(id); |
| return (session); |
| |
| } |
| |
| /** |
| * Remove this Session from the active Sessions for this Manager, |
| * but not from the Store. (Used by the PersistentValve) |
| * |
| * @param session Session to be removed |
| */ |
| public void removeSuper(Session session) { |
| super.remove (session); |
| } |
| |
| /** |
| * Load all sessions found in the persistence mechanism, assuming |
| * they are marked as valid and have not passed their expiration |
| * limit. If persistence is not supported, this method returns |
| * without doing anything. |
| * <p> |
| * Note that by default, this method is not called by the MiddleManager |
| * class. In order to use it, a subclass must specifically call it, |
| * for example in the start() and/or processPersistenceChecks() methods. |
| */ |
| public void load() { |
| |
| // Initialize our internal data structures |
| sessions.clear(); |
| |
| if (store == null) |
| return; |
| |
| String[] ids = null; |
| try { |
| if (SecurityUtil.isPackageProtectionEnabled()){ |
| try{ |
| ids = (String[]) |
| AccessController.doPrivileged(new PrivilegedStoreKeys()); |
| }catch(PrivilegedActionException ex){ |
| Exception exception = ex.getException(); |
| log.error("Exception in the Store during load: " |
| + exception); |
| exception.printStackTrace(); |
| } |
| } else { |
| ids = store.keys(); |
| } |
| } catch (IOException e) { |
| log.error("Can't load sessions from store, " + e.getMessage(), e); |
| return; |
| } |
| |
| int n = ids.length; |
| if (n == 0) |
| return; |
| |
| if (log.isDebugEnabled()) |
| log.debug(sm.getString("persistentManager.loading", String.valueOf(n))); |
| |
| for (int i = 0; i < n; i++) |
| try { |
| swapIn(ids[i]); |
| } catch (IOException e) { |
| log.error("Failed load session from store, " + e.getMessage(), e); |
| } |
| |
| } |
| |
| |
| /** |
| * Remove this Session from the active Sessions for this Manager, |
| * and from the Store. |
| * |
| * @param session Session to be removed |
| */ |
| public void remove(Session session) { |
| |
| super.remove (session); |
| |
| if (store != null){ |
| removeSession(session.getIdInternal()); |
| } |
| } |
| |
| |
| /** |
| * Remove this Session from the active Sessions for this Manager, |
| * and from the Store. |
| * |
| * @param id Session's id to be removed |
| */ |
| protected void removeSession(String id){ |
| try { |
| if (SecurityUtil.isPackageProtectionEnabled()){ |
| try{ |
| AccessController.doPrivileged(new PrivilegedStoreRemove(id)); |
| }catch(PrivilegedActionException ex){ |
| Exception exception = ex.getException(); |
| log.error("Exception in the Store during removeSession: " |
| + exception); |
| exception.printStackTrace(); |
| } |
| } else { |
| store.remove(id); |
| } |
| } catch (IOException e) { |
| log.error("Exception removing session " + e.getMessage()); |
| e.printStackTrace(); |
| } |
| } |
| |
| /** |
| * Save all currently active sessions in the appropriate persistence |
| * mechanism, if any. If persistence is not supported, this method |
| * returns without doing anything. |
| * <p> |
| * Note that by default, this method is not called by the MiddleManager |
| * class. In order to use it, a subclass must specifically call it, |
| * for example in the stop() and/or processPersistenceChecks() methods. |
| */ |
| public void unload() { |
| |
| if (store == null) |
| return; |
| |
| Session sessions[] = findSessions(); |
| int n = sessions.length; |
| if (n == 0) |
| return; |
| |
| if (log.isDebugEnabled()) |
| log.debug(sm.getString("persistentManager.unloading", |
| String.valueOf(n))); |
| |
| for (int i = 0; i < n; i++) |
| try { |
| swapOut(sessions[i]); |
| } catch (IOException e) { |
| ; // This is logged in writeSession() |
| } |
| |
| } |
| |
| |
| // ------------------------------------------------------ Protected Methods |
| |
| |
| /** |
| * Look for a session in the Store and, if found, restore |
| * it in the Manager's list of active sessions if appropriate. |
| * The session will be removed from the Store after swapping |
| * in, but will not be added to the active session list if it |
| * is invalid or past its expiration. |
| */ |
| protected Session swapIn(String id) throws IOException { |
| |
| if (store == null) |
| return null; |
| |
| Session session = null; |
| try { |
| if (SecurityUtil.isPackageProtectionEnabled()){ |
| try{ |
| session = (Session) |
| AccessController.doPrivileged(new PrivilegedStoreLoad(id)); |
| }catch(PrivilegedActionException ex){ |
| Exception exception = ex.getException(); |
| log.error("Exception in the Store during swapIn: " |
| + exception); |
| if (exception instanceof IOException){ |
| throw (IOException)exception; |
| } else if (exception instanceof ClassNotFoundException) { |
| throw (ClassNotFoundException)exception; |
| } |
| } |
| } else { |
| session = store.load(id); |
| } |
| } catch (ClassNotFoundException e) { |
| log.error(sm.getString("persistentManager.deserializeError", id, e)); |
| throw new IllegalStateException |
| (sm.getString("persistentManager.deserializeError", id, e)); |
| } |
| |
| if (session == null) |
| return (null); |
| |
| if (!session.isValid()) { |
| log.error("session swapped in is invalid or expired"); |
| session.expire(); |
| removeSession(id); |
| return (null); |
| } |
| |
| if(log.isDebugEnabled()) |
| log.debug(sm.getString("persistentManager.swapIn", id)); |
| |
| session.setManager(this); |
| // make sure the listeners know about it. |
| ((StandardSession)session).tellNew(); |
| add(session); |
| ((StandardSession)session).activate(); |
| session.endAccess(); |
| |
| return (session); |
| |
| } |
| |
| |
| /** |
| * Remove the session from the Manager's list of active |
| * sessions and write it out to the Store. If the session |
| * is past its expiration or invalid, this method does |
| * nothing. |
| * |
| * @param session The Session to write out. |
| */ |
| protected void swapOut(Session session) throws IOException { |
| |
| if (store == null || !session.isValid()) { |
| return; |
| } |
| |
| ((StandardSession)session).passivate(); |
| writeSession(session); |
| super.remove(session); |
| session.recycle(); |
| |
| } |
| |
| |
| /** |
| * Write the provided session to the Store without modifying |
| * the copy in memory or triggering passivation events. Does |
| * nothing if the session is invalid or past its expiration. |
| */ |
| protected void writeSession(Session session) throws IOException { |
| |
| if (store == null || !session.isValid()) { |
| return; |
| } |
| |
| try { |
| if (SecurityUtil.isPackageProtectionEnabled()){ |
| try{ |
| AccessController.doPrivileged(new PrivilegedStoreSave(session)); |
| }catch(PrivilegedActionException ex){ |
| Exception exception = ex.getException(); |
| log.error("Exception in the Store during writeSession: " |
| + exception); |
| exception.printStackTrace(); |
| } |
| } else { |
| store.save(session); |
| } |
| } catch (IOException e) { |
| log.error(sm.getString |
| ("persistentManager.serializeError", session.getIdInternal(), e)); |
| throw e; |
| } |
| |
| } |
| |
| |
| // ------------------------------------------------------ Lifecycle Methods |
| |
| |
| /** |
| * Add a lifecycle event listener to this component. |
| * |
| * @param listener The listener to add |
| */ |
| public void addLifecycleListener(LifecycleListener listener) { |
| |
| lifecycle.addLifecycleListener(listener); |
| |
| } |
| |
| |
| /** |
| * Get the lifecycle listeners associated with this lifecycle. If this |
| * Lifecycle has no listeners registered, a zero-length array is returned. |
| */ |
| public LifecycleListener[] findLifecycleListeners() { |
| |
| return lifecycle.findLifecycleListeners(); |
| |
| } |
| |
| |
| /** |
| * Remove a lifecycle event listener from this component. |
| * |
| * @param listener The listener to remove |
| */ |
| public void removeLifecycleListener(LifecycleListener listener) { |
| |
| lifecycle.removeLifecycleListener(listener); |
| |
| } |
| |
| |
| /** |
| * Prepare for the beginning of active use of the public methods of this |
| * component. This method should be called after <code>configure()</code>, |
| * and before any of the public methods of the component are utilized. |
| * |
| * @exception LifecycleException if this component detects a fatal error |
| * that prevents this component from being used |
| */ |
| public void start() throws LifecycleException { |
| |
| // Validate and update our current component state |
| if (started) { |
| log.info(sm.getString("standardManager.alreadyStarted")); |
| return; |
| } |
| if( ! initialized ) |
| init(); |
| |
| lifecycle.fireLifecycleEvent(START_EVENT, null); |
| started = true; |
| |
| // Force initialization of the random number generator |
| if (log.isDebugEnabled()) |
| log.debug("Force random number initialization starting"); |
| String dummy = generateSessionId(); |
| if (log.isDebugEnabled()) |
| log.debug("Force random number initialization completed"); |
| |
| if (store == null) |
| log.error("No Store configured, persistence disabled"); |
| else if (store instanceof Lifecycle) |
| ((Lifecycle)store).start(); |
| |
| } |
| |
| |
| /** |
| * Gracefully terminate the active use of the public methods of this |
| * component. This method should be the last one called on a given |
| * instance of this component. |
| * |
| * @exception LifecycleException if this component detects a fatal error |
| * that needs to be reported |
| */ |
| public void stop() throws LifecycleException { |
| |
| if (log.isDebugEnabled()) |
| log.debug("Stopping"); |
| |
| // Validate and update our current component state |
| if (!isStarted()) { |
| log.info(sm.getString("standardManager.notStarted")); |
| return; |
| } |
| |
| lifecycle.fireLifecycleEvent(STOP_EVENT, null); |
| setStarted(false); |
| |
| if (getStore() != null && saveOnRestart) { |
| unload(); |
| } else { |
| // Expire all active sessions |
| Session sessions[] = findSessions(); |
| for (int i = 0; i < sessions.length; i++) { |
| StandardSession session = (StandardSession) sessions[i]; |
| if (!session.isValid()) |
| continue; |
| session.expire(); |
| } |
| } |
| |
| if (getStore() != null && getStore() instanceof Lifecycle) |
| ((Lifecycle)getStore()).stop(); |
| |
| // Require a new random number generator if we are restarted |
| this.random = null; |
| |
| if( initialized ) |
| destroy(); |
| |
| } |
| |
| |
| // ----------------------------------------- PropertyChangeListener Methods |
| |
| |
| /** |
| * Process property change events from our associated Context. |
| * |
| * @param event The property change event that has occurred |
| */ |
| public void propertyChange(PropertyChangeEvent event) { |
| |
| // Validate the source of this event |
| if (!(event.getSource() instanceof Context)) |
| return; |
| Context context = (Context) event.getSource(); |
| |
| // Process a relevant property change |
| if (event.getPropertyName().equals("sessionTimeout")) { |
| try { |
| setMaxInactiveInterval |
| ( ((Integer) event.getNewValue()).intValue()*60 ); |
| } catch (NumberFormatException e) { |
| log.error(sm.getString("standardManager.sessionTimeout", |
| event.getNewValue().toString())); |
| } |
| } |
| |
| } |
| |
| |
| // ------------------------------------------------------ Protected Methods |
| |
| |
| /** |
| * Swap idle sessions out to Store if they are idle too long. |
| */ |
| protected void processMaxIdleSwaps() { |
| |
| if (!isStarted() || maxIdleSwap < 0) |
| return; |
| |
| Session sessions[] = findSessions(); |
| long timeNow = System.currentTimeMillis(); |
| |
| // Swap out all sessions idle longer than maxIdleSwap |
| // FIXME: What's preventing us from mangling a session during |
| // a request? |
| if (maxIdleSwap >= 0) { |
| for (int i = 0; i < sessions.length; i++) { |
| StandardSession session = (StandardSession) sessions[i]; |
| if (!session.isValid()) |
| continue; |
| int timeIdle = // Truncate, do not round up |
| (int) ((timeNow - session.getLastAccessedTime()) / 1000L); |
| if (timeIdle > maxIdleSwap && timeIdle > minIdleSwap) { |
| if (log.isDebugEnabled()) |
| log.debug(sm.getString |
| ("persistentManager.swapMaxIdle", |
| session.getIdInternal(), new Integer(timeIdle))); |
| try { |
| swapOut(session); |
| } catch (IOException e) { |
| ; // This is logged in writeSession() |
| } |
| } |
| } |
| } |
| |
| } |
| |
| |
| /** |
| * Swap idle sessions out to Store if too many are active |
| */ |
| protected void processMaxActiveSwaps() { |
| |
| if (!isStarted() || getMaxActiveSessions() < 0) |
| return; |
| |
| Session sessions[] = findSessions(); |
| |
| // FIXME: Smarter algorithm (LRU) |
| if (getMaxActiveSessions() >= sessions.length) |
| return; |
| |
| if(log.isDebugEnabled()) |
| log.debug(sm.getString |
| ("persistentManager.tooManyActive", |
| new Integer(sessions.length))); |
| |
| int toswap = sessions.length - getMaxActiveSessions(); |
| long timeNow = System.currentTimeMillis(); |
| |
| for (int i = 0; i < sessions.length && toswap > 0; i++) { |
| int timeIdle = // Truncate, do not round up |
| (int) ((timeNow - sessions[i].getLastAccessedTime()) / 1000L); |
| if (timeIdle > minIdleSwap) { |
| if(log.isDebugEnabled()) |
| log.debug(sm.getString |
| ("persistentManager.swapTooManyActive", |
| sessions[i].getIdInternal(), new Integer(timeIdle))); |
| try { |
| swapOut(sessions[i]); |
| } catch (IOException e) { |
| ; // This is logged in writeSession() |
| } |
| toswap--; |
| } |
| } |
| |
| } |
| |
| |
| /** |
| * Back up idle sessions. |
| */ |
| protected void processMaxIdleBackups() { |
| |
| if (!isStarted() || maxIdleBackup < 0) |
| return; |
| |
| Session sessions[] = findSessions(); |
| long timeNow = System.currentTimeMillis(); |
| |
| // Back up all sessions idle longer than maxIdleBackup |
| if (maxIdleBackup >= 0) { |
| for (int i = 0; i < sessions.length; i++) { |
| StandardSession session = (StandardSession) sessions[i]; |
| if (!session.isValid()) |
| continue; |
| int timeIdle = // Truncate, do not round up |
| (int) ((timeNow - session.getLastAccessedTime()) / 1000L); |
| if (timeIdle > maxIdleBackup) { |
| if (log.isDebugEnabled()) |
| log.debug(sm.getString |
| ("persistentManager.backupMaxIdle", |
| session.getIdInternal(), new Integer(timeIdle))); |
| |
| try { |
| writeSession(session); |
| } catch (IOException e) { |
| ; // This is logged in writeSession() |
| } |
| } |
| } |
| } |
| |
| } |
| |
| } |