Modified destroy() method chaining - previously used destroy and chain of before*Destroyed() method calls, which was confusing and hard to trace across the class hierarchy.  Now using simple super.* overriding calls for easy traceability.  The previous before*Destroyed() methods existed for simple subclassing overrides, but are no longer relevant since the next release will favor a composition over inheritance model (subclasses should be very rare).  Also modified some JavaDoc to reflect this.

git-svn-id: https://svn.apache.org/repos/asf/incubator/jsecurity/trunk@736202 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/core/src/org/jsecurity/cache/DefaultCacheManager.java b/core/src/org/jsecurity/cache/DefaultCacheManager.java
index 0db9179..daabc92 100644
--- a/core/src/org/jsecurity/cache/DefaultCacheManager.java
+++ b/core/src/org/jsecurity/cache/DefaultCacheManager.java
@@ -18,6 +18,9 @@
  */
 package org.jsecurity.cache;
 
+import org.jsecurity.util.Destroyable;
+import org.jsecurity.util.LifecycleUtils;
+
 import java.util.HashMap;
 import java.util.Map;
 
@@ -32,7 +35,7 @@
  * @author Les Hazlewood
  * @since 1.0
  */
-public class DefaultCacheManager implements CacheManager {
+public class DefaultCacheManager implements CacheManager, Destroyable {
 
     /**
      * Retains all Cache objects maintained by this cache manager.
@@ -56,4 +59,13 @@
 
         return cache;
     }
+
+    public void destroy() throws Exception {
+        synchronized (caches) {
+            for (Cache cache : caches.values()) {
+                LifecycleUtils.destroy(cache);
+            }
+            caches.clear();
+        }
+    }
 }
diff --git a/core/src/org/jsecurity/cache/MapCache.java b/core/src/org/jsecurity/cache/MapCache.java
index 1bcce67..8b1fa08 100644
--- a/core/src/org/jsecurity/cache/MapCache.java
+++ b/core/src/org/jsecurity/cache/MapCache.java
@@ -18,6 +18,8 @@
  */
 package org.jsecurity.cache;
 
+import org.jsecurity.util.Destroyable;
+
 import java.util.*;
 
 /**
@@ -27,7 +29,7 @@
  * @author Les Hazlewood
  * @since 1.0
  */
-public class MapCache implements Cache {
+public class MapCache implements Cache, Destroyable {
 
     /**
      * Backing instance.
@@ -40,6 +42,12 @@
     private final String name;
 
     public MapCache(String name, Map backingMap) {
+        if (name == null) {
+            throw new IllegalArgumentException("Cache name cannot be null.");
+        }
+        if (backingMap == null) {
+            throw new IllegalArgumentException("Backing map cannot be null.");
+        }
         this.name = name;
         this.map = backingMap;
     }
@@ -89,6 +97,10 @@
     }
 
     public String toString() {
-        return "MapCache [" + name + "]";
+        return getClass().getName() + " : [" + name + "]";
+    }
+
+    public void destroy() throws Exception {
+        clear();
     }
 }
diff --git a/core/src/org/jsecurity/mgt/AuthenticatingSecurityManager.java b/core/src/org/jsecurity/mgt/AuthenticatingSecurityManager.java
index 1584ae4..a064223 100644
--- a/core/src/org/jsecurity/mgt/AuthenticatingSecurityManager.java
+++ b/core/src/org/jsecurity/mgt/AuthenticatingSecurityManager.java
@@ -21,7 +21,6 @@
 import org.jsecurity.authc.*;
 import org.jsecurity.authc.pam.ModularAuthenticationStrategy;
 import org.jsecurity.authc.pam.ModularRealmAuthenticator;
-import org.jsecurity.realm.Realm;
 import org.jsecurity.util.LifecycleUtils;
 
 import java.util.Collection;
@@ -55,6 +54,7 @@
      * {@link org.jsecurity.authc.pam.ModularRealmAuthenticator ModularRealmAuthenticator}.
      */
     public AuthenticatingSecurityManager() {
+        super();
         this.authenticator = new ModularRealmAuthenticator();
     }
 
@@ -165,47 +165,26 @@
     }
 
     /**
-     * Immediately calls {@link RealmSecurityManager#setRealms(java.util.Collection) super.setRealms} and then
-     * additionally passes on those realms to the internal delegate <code>Authenticator</code> instance so
+     * Passes on the {@link #getRealms() realms} to the internal delegate <code>Authenticator</code> instance so
      * that it may use them during authentication attempts.
-     *
-     * @param realms realms the realms managed by this <tt>SecurityManager</tt> instance and subsequently the internal
-     *               delegate <code>Authenticator</code> instance.
      */
-    public void setRealms(Collection<Realm> realms) {
-        super.setRealms(realms);
+    protected void afterRealmsSet() {
+        super.afterRealmsSet();
         if (this.authenticator instanceof ModularRealmAuthenticator) {
-            ((ModularRealmAuthenticator) this.authenticator).setRealms(realms);
+            ((ModularRealmAuthenticator) this.authenticator).setRealms(getRealms());
         }
     }
 
     /**
-     * Lifecycle cleanup method that first calls {@link #beforeAuthenticatorDestroyed() beforeAuthenticatorDestroyed()}
-     * to allow subclass cleanup and then calls {@link #destroyAuthenticator() destroyAuthenticator()} to actually
-     * clean up the internal delegate instance.
-     */
-    protected void beforeRealmsDestroyed() {
-        beforeAuthenticatorDestroyed();
-        destroyAuthenticator();
-    }
-
-    /**
-     * Template hook to allow subclass cleanup when the SecurityManager is being shut down.
-     */
-    protected void beforeAuthenticatorDestroyed() {
-    }
-
-    /**
-     * Cleans up ('destroys') the internal delegate <code>Authenticator</code> instance.  Called during shut down.
-     */
-    protected void destroyAuthenticator() {
-        LifecycleUtils.destroy(getAuthenticator());
-    }
-
-    /**
      * Delegates to the wrapped {@link Authenticator Authenticator} for authentication.
      */
     public AuthenticationInfo authenticate(AuthenticationToken token) throws AuthenticationException {
         return this.authenticator.authenticate(token);
     }
+
+    public void destroy() {
+        LifecycleUtils.destroy(getAuthenticator());
+        this.authenticator = null;
+        super.destroy();
+    }
 }
diff --git a/core/src/org/jsecurity/mgt/AuthorizingSecurityManager.java b/core/src/org/jsecurity/mgt/AuthorizingSecurityManager.java
index c755c49..650523b 100644
--- a/core/src/org/jsecurity/mgt/AuthorizingSecurityManager.java
+++ b/core/src/org/jsecurity/mgt/AuthorizingSecurityManager.java
@@ -24,7 +24,6 @@
 import org.jsecurity.authz.Permission;
 import org.jsecurity.authz.permission.PermissionResolver;
 import org.jsecurity.authz.permission.PermissionResolverAware;
-import org.jsecurity.realm.Realm;
 import org.jsecurity.subject.PrincipalCollection;
 import org.jsecurity.util.LifecycleUtils;
 
@@ -59,6 +58,7 @@
      * {@link ModularRealmAuthorizer ModularRealmAuthorizer}.
      */
     public AuthorizingSecurityManager() {
+        super();
         this.authorizer = new ModularRealmAuthorizer();
     }
 
@@ -114,52 +114,27 @@
     }
 
     /**
-     * First calls <code>super.realms</code> and then sets these same <code>Realm</code> objects on this instance's
-     * {@link Authorizer Authorizer}.
+     * First calls <code>super.afterRealmsSet()</code> and then sets these same <code>Realm</code> objects on this
+     * instance's wrapped {@link Authorizer Authorizer}.
      * <p/>
-     * The setting on the Authorizer will only occur if it is an instance of
+     * The setting of realms the Authorizer will only occur if it is an instance of
      * {@link org.jsecurity.authz.ModularRealmAuthorizer ModularRealmAuthorizer}, that is:
-     * <pre>       Authorizer authz = getAuthorizer();
-     * if ( authz instanceof ModularRealmAuthorizer ) {
-     *     ((ModularRealmAuthorizer)authz).setRealms(realms);
+     * <pre>
+     * if ( this.authorizer instanceof ModularRealmAuthorizer ) {
+     *     ((ModularRealmAuthorizer)this.authorizer).setRealms(realms);
      * }</pre>
-     *
-     * @param realms the realms managed by this <tt>SecurityManager</tt> instance.
      */
-    public void setRealms(Collection<Realm> realms) {
-        super.setRealms(realms);
+    protected void afterRealmsSet() {
+        super.afterRealmsSet();
         if (this.authorizer instanceof ModularRealmAuthorizer) {
-            ((ModularRealmAuthorizer) this.authorizer).setRealms(realms);
+            ((ModularRealmAuthorizer) this.authorizer).setRealms(getRealms());
         }
     }
 
-    /**
-     * Template hook for subclasses to implement destruction/cleanup logic.  This will be called before this
-     * instance's <tt>Authorizer</tt> instance will be cleaned up.
-     */
-    protected void beforeAuthorizerDestroyed() {
-    }
-
-    /**
-     * Cleanup method that destroys/cleans up the wrapped {@link #getAuthorizer Authorizer} instance.
-     * <p/>
-     * The default implementation merely delegates to
-     * <code>{@link LifecycleUtils#destroy LifecycleUtils.destroy}({@link #getAuthorizer getAuthorizer()})</code>.
-     */
-    protected void destroyAuthorizer() {
+    public void destroy() {
         LifecycleUtils.destroy(getAuthorizer());
-    }
-
-    /**
-     * Implementation of parent class's template hook for destruction/cleanup logic.
-     *
-     * <p>This implementation ensures subclasses are cleaned up first by calling
-     * {@link #beforeAuthorizerDestroyed() beforeAuthorizerDestroyed()} and then actually cleans up the
-     * wrapped <tt>Authorizer</tt> via the {@link #destroyAuthorizer() desroyAuthorizer()} method.
-     */
-    protected void beforeAuthenticatorDestroyed() {
-        beforeAuthorizerDestroyed();
-        destroyAuthorizer();
+        this.authorizer = null;
+        super.destroy();
     }
 
     public boolean isPermitted(PrincipalCollection principals, String permissionString) {
diff --git a/core/src/org/jsecurity/mgt/CachingSecurityManager.java b/core/src/org/jsecurity/mgt/CachingSecurityManager.java
index 683baa2..a67caae 100644
--- a/core/src/org/jsecurity/mgt/CachingSecurityManager.java
+++ b/core/src/org/jsecurity/mgt/CachingSecurityManager.java
@@ -75,7 +75,6 @@
         afterCacheManagerSet();
     }
 
-
     /**
      * Template callback to notify subclasses that a
      * {@link CacheManager CacheManager} has been set and is available for use via the
@@ -85,25 +84,10 @@
     }
 
     /**
-     * First calls {@link #beforeCacheManagerDestroyed() beforeCacheManagerDestroyed()} to allow subclasses to clean up
-     * first, then calls {@link #destroyCacheManager() destroyCacheManager()} to clean up the internal
-     * {@link CacheManager CacheManager}.
+     * Destroys the {@link #getCacheManager() cacheManager} via {@link LifecycleUtils#destroy LifecycleUtils.destroy}.
      */
     public void destroy() {
-        beforeCacheManagerDestroyed();
-        destroyCacheManager();
-    }
-
-    /**
-     * Template hook for subclasses to perform cleanup behavior during shutdown.
-     */
-    protected void beforeCacheManagerDestroyed() {
-    }
-
-    /**
-     * Cleans up the internal <code>CacheManager</code> instance during shutdown.
-     */
-    protected void destroyCacheManager() {
         LifecycleUtils.destroy(getCacheManager());
+        this.cacheManager = null;
     }
 }
diff --git a/core/src/org/jsecurity/mgt/DefaultSecurityManager.java b/core/src/org/jsecurity/mgt/DefaultSecurityManager.java
index 2041a97..f3d9fd6 100644
--- a/core/src/org/jsecurity/mgt/DefaultSecurityManager.java
+++ b/core/src/org/jsecurity/mgt/DefaultSecurityManager.java
@@ -85,8 +85,9 @@
      * Default no-arg constructor.

      */

     public DefaultSecurityManager() {

-        setSubjectFactory(new DefaultSubjectFactory());

-        setSubjectBinder(new SessionSubjectBinder());

+        super();

+        this.subjectFactory = new DefaultSubjectFactory(this);

+        this.subjectBinder = new SessionSubjectBinder();

     }

 

     /**

diff --git a/core/src/org/jsecurity/mgt/RealmSecurityManager.java b/core/src/org/jsecurity/mgt/RealmSecurityManager.java
index 4c58f85..4e20277 100644
--- a/core/src/org/jsecurity/mgt/RealmSecurityManager.java
+++ b/core/src/org/jsecurity/mgt/RealmSecurityManager.java
@@ -45,6 +45,7 @@
      * Default no-arg constructor.
      */
     public RealmSecurityManager() {
+        super();
     }
 
     /**
@@ -77,6 +78,10 @@
             throw new IllegalArgumentException("Realms collection argument cannot be empty.");
         }
         this.realms = realms;
+        afterRealmsSet();
+    }
+
+    protected void afterRealmsSet() {
         applyCacheManagerToRealms();
     }
 
@@ -122,29 +127,10 @@
         applyCacheManagerToRealms();
     }
 
-    /**
-     * First calls {@link #beforeRealmsDestroyed() beforeRealmsDestroyed()} to allow subclasses to clean up
-     * first, then calls {@link #destroyRealms() destroyRealms()} to clean up the internal <code>Realm</code>s
-     * collection.
-     */
-    protected void beforeCacheManagerDestroyed() {
-        beforeRealmsDestroyed();
-        destroyRealms();
-    }
-
-    /**
-     * Template hook for subclasses to perform clean up logic during shut-down.
-     */
-    protected void beforeRealmsDestroyed() {
-    }
-
-    /**
-     * Cleans up ('destroys') the internal collection of Realms by calling
-     * {@link LifecycleUtils#destroy(Collection) LifecycleUtils.destroy(getRealms())}.
-     */
-    protected void destroyRealms() {
+    public void destroy() {
         LifecycleUtils.destroy(getRealms());
         this.realms = null;
+        super.destroy();
     }
 
 }
diff --git a/core/src/org/jsecurity/mgt/SessionsSecurityManager.java b/core/src/org/jsecurity/mgt/SessionsSecurityManager.java
index 2453867..e4676c0 100644
--- a/core/src/org/jsecurity/mgt/SessionsSecurityManager.java
+++ b/core/src/org/jsecurity/mgt/SessionsSecurityManager.java
@@ -24,6 +24,8 @@
 import org.jsecurity.session.Session;
 import org.jsecurity.session.SessionListener;
 import org.jsecurity.session.SessionListenerRegistrar;
+import org.jsecurity.session.mgt.AbstractSessionManager;
+import org.jsecurity.session.mgt.AbstractValidatingSessionManager;
 import org.jsecurity.session.mgt.DefaultSessionManager;
 import org.jsecurity.session.mgt.SessionManager;
 import org.jsecurity.util.LifecycleUtils;
@@ -62,11 +64,8 @@
      * instance.
      */
     public SessionsSecurityManager() {
-        applySessionManager(new DefaultSessionManager());
-    }
-
-    protected final void applySessionManager(SessionManager sm) {
-        setSessionManager(sm);
+        super();
+        this.sessionManager = new DefaultSessionManager();
         applyCacheManagerToSessionManager();
     }
 
@@ -85,6 +84,11 @@
      */
     public void setSessionManager(SessionManager sessionManager) {
         this.sessionManager = sessionManager;
+        afterSessionManagerSet();
+    }
+
+    protected void afterSessionManagerSet() {
+        applyCacheManagerToSessionManager();
     }
 
     /**
@@ -141,6 +145,55 @@
         ((SessionListenerRegistrar) this.sessionManager).setSessionListeners(sessionListeners);
     }
 
+    private void assertSessionManager(Class<? extends SessionManager> requiredType) {
+        if (this.sessionManager == null) {
+            throw new IllegalStateException("SessionManager is null - cannot configure property!");
+        }
+        if (!(this.sessionManager instanceof AbstractValidatingSessionManager)) {
+            String msg = "Property configuration failed.  The target property is only configurable when the " +
+                    "underlying SessionManager instance is a part of the " +
+                    "[" + requiredType.getName() + "] class hierarchy.  " +
+                    "The current SessionManager is of type [" + this.sessionManager.getClass().getName() + "].  " +
+                    "This might occur for example if you're trying to set the validation interval " +
+                    "in a servlet container-backed session environment ('http' session mode).  If that is the case " +
+                    "however, that property is only useful when using 'jsecurity' session mode and using " +
+                    "JSecurity enterprise sessions which do not rely on a servlet container.";
+            throw new IllegalStateException(msg);
+        }
+    }
+
+    /**
+     * Passthrough configuration property to the underlying {@link AbstractSessionManager AbstractSessionManager}
+     * instance.  Please read the
+     * {@link org.jsecurity.session.mgt.AbstractSessionManager#getGlobalSessionTimeout() AbstractSessionManager.getGlobalSessionTimeout()}
+     * for more.
+     *
+     * @return the time in milliseconds that any {@link Session Session} may remain idle before expiring.
+     * @throws IllegalStateException if the underlying {@code SessionManager} instance is not a subclass of
+     *                               {@link AbstractSessionManager AbstractSessionManager}.
+     * @see org.jsecurity.session.mgt.AbstractSessionManager#getGlobalSessionTimeout()
+     */
+    public long getGlobalSessionTimeout() {
+        assertSessionManager(AbstractSessionManager.class);
+        return ((AbstractSessionManager) this.sessionManager).getGlobalSessionTimeout();
+    }
+
+    /**
+     * Passthrough configuration property to the underlying {@link AbstractSessionManager AbstractSessionManager}
+     * instance.  Please read the
+     * {@link org.jsecurity.session.mgt.AbstractSessionManager#setGlobalSessionTimeout(long) AbstractSessionManager.setGlobalSessionTimeout(long)}
+     * for more.
+     *
+     * @param globalSessionTimeout the time in milliseconds that any {@link Session Session} may remain idle before expiring.
+     * @throws IllegalStateException if the underlying {@code SessionManager} instance is not a subclass of
+     *                               {@link AbstractSessionManager AbstractSessionManager}.
+     * @see org.jsecurity.session.mgt.AbstractSessionManager#setGlobalSessionTimeout(long)
+     */
+    public void setGlobalSessionTimeout(long globalSessionTimeout) {
+        assertSessionManager(AbstractSessionManager.class);
+        ((AbstractSessionManager) this.sessionManager).setGlobalSessionTimeout(globalSessionTimeout);
+    }
+
     /**
      * Ensures the internal SessionManager instance is an <code>instanceof</code>
      * {@link org.jsecurity.session.SessionListenerRegistrar SessionListenerRegistrar} to ensure that any
@@ -237,28 +290,9 @@
         return this.sessionManager.removeAttribute(sessionId, key);
     }
 
-    /**
-     * Template hook for subclasses that wish to perform clean up behavior during shutdown.
-     */
-    protected void beforeSessionManagerDestroyed() {
-    }
-
-    /**
-     * Cleans up ('destroys') the internal delegate <code>SessionManager</code> by calling
-     * {@link LifecycleUtils#destroy LifecycleUtils.destroy(this.sessionManager)}.
-     */
-    protected void destroySessionManager() {
-        LifecycleUtils.destroy(this.sessionManager);
-    }
-
-    /**
-     * Calls {@link #beforeSessionManagerDestroyed() beforeSessionManagerDestroyed()} to allow subclass clean up and
-     * then immediatley calls {@link #destroySessionManager() destroySessionManager()} to clean up the internal
-     * delegate instance.
-     */
-    protected void beforeAuthorizerDestroyed() {
-        beforeSessionManagerDestroyed();
-        destroySessionManager();
+    public void destroy() {
+        LifecycleUtils.destroy(getSessionManager());
+        this.sessionManager = null;
     }
 
 }
diff --git a/core/src/org/jsecurity/session/mgt/AbstractSessionManager.java b/core/src/org/jsecurity/session/mgt/AbstractSessionManager.java
index e58dac8..4bbe953 100644
--- a/core/src/org/jsecurity/session/mgt/AbstractSessionManager.java
+++ b/core/src/org/jsecurity/session/mgt/AbstractSessionManager.java
@@ -31,18 +31,65 @@
 
 /**
  * TODO - complete JavaDoc
+ *
  * @author Les Hazlewood
  * @since 0.1
  */
 public abstract class AbstractSessionManager implements SessionManager, SessionListenerRegistrar {
 
+    protected static final long MILLIS_PER_SECOND = 1000;
+    protected static final long MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND;
+    protected static final long MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE;
+
+    /**
+     * Default main session timeout value, equal to {@code 30} minutes.
+     */
+    public static final long DEFAULT_GLOBAL_SESSION_TIMEOUT = 30 * MILLIS_PER_MINUTE;
+
     private static final Log log = LogFactory.getLog(AbstractSessionManager.class);
 
-    protected Collection<SessionListener> listeners = new ArrayList<SessionListener>();
+    private long globalSessionTimeout = DEFAULT_GLOBAL_SESSION_TIMEOUT;
+    private Collection<SessionListener> listeners = new ArrayList<SessionListener>();
 
     public AbstractSessionManager() {
     }
 
+    /**
+     * Returns the system-wide default time in milliseconds that any session may remain idle before expiring. This
+     * value is just a main default for all sessions and may be overridden on a <em>per-session</em> basis by calling
+     * {@code Subject.getSession().}{@link Session#setTimeout setTimeout(long)} if so desired.
+     * <ul>
+     * <li>A negative return value means sessions never expire.</li>
+     * <li>A non-negative return value (0 or greater) means session timeout will occur as expected.</li>
+     * </ul>
+     * <p/>
+     * Unless overridden via the {@link #setGlobalSessionTimeout} method, the default value is
+     * {@link #DEFAULT_GLOBAL_SESSION_TIMEOUT}.
+     *
+     * @return the time in milliseconds that any session may remain idle before expiring.
+     */
+    public long getGlobalSessionTimeout() {
+        return this.globalSessionTimeout;
+    }
+
+    /**
+     * Sets the system-wide default time in milliseconds that any session may remain idle before expiring. This
+     * value is just a main default for all sessions and may be overridden on a <em>per-session</em> basis by calling
+     * {@code Subject.getSession().}{@link Session#setTimeout setTimeout(long)} if so desired.
+     *
+     * <ul>
+     * <li>A negative return value means sessions never expire.</li>
+     * <li>A non-negative return value (0 or greater) means session timeout will occur as expected.</li>
+     * </ul>
+     * <p/>
+     * Unless overridden by calling this method, the default value is {@link #DEFAULT_GLOBAL_SESSION_TIMEOUT}.
+     *
+     * @param globalSessionTimeout the time in milliseconds that any session may remain idel before expiring.
+     */
+    public void setGlobalSessionTimeout(long globalSessionTimeout) {
+        this.globalSessionTimeout = globalSessionTimeout;
+    }
+
     public void setSessionListeners(Collection<SessionListener> listeners) {
         if (listeners == null) {
             this.listeners = new ArrayList<SessionListener>();
@@ -188,12 +235,7 @@
     }
 
     public boolean isValid(Serializable sessionId) {
-        try {
-            getSession(sessionId);
-        } catch (InvalidSessionException e) {
-            return false;
-        }
-        return true;
+        return doGetSession(sessionId) != null;
     }
 
     protected void onChange(Session s) {
diff --git a/core/src/org/jsecurity/session/mgt/AbstractValidatingSessionManager.java b/core/src/org/jsecurity/session/mgt/AbstractValidatingSessionManager.java
index 18b2176..0209c7e 100644
--- a/core/src/org/jsecurity/session/mgt/AbstractValidatingSessionManager.java
+++ b/core/src/org/jsecurity/session/mgt/AbstractValidatingSessionManager.java
@@ -45,15 +45,6 @@
 
     private static final Log log = LogFactory.getLog(AbstractValidatingSessionManager.class);
 
-    protected static final long MILLIS_PER_SECOND = 1000;
-    protected static final long MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND;
-    protected static final long MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE;
-
-    /**
-     * Default main session timeout value (30 * 60 * 1000 milliseconds = 30 minutes).
-     */
-    public static final long DEFAULT_GLOBAL_SESSION_TIMEOUT = 30 * MILLIS_PER_MINUTE;
-
     /**
      * The default interval at which sessions will be validated (1 hour);
      * This can be overridden by calling {@link #setSessionValidationInterval(long)}
@@ -67,7 +58,7 @@
     protected SessionValidationScheduler sessionValidationScheduler = null;
 
     protected long sessionValidationInterval = DEFAULT_SESSION_VALIDATION_INTERVAL;
-    protected long globalSessionTimeout = DEFAULT_GLOBAL_SESSION_TIMEOUT;
+
 
     public AbstractValidatingSessionManager() {
     }
@@ -88,7 +79,7 @@
         return sessionValidationScheduler;
     }
 
-    public void enableSessionValidationIfNecessary() {
+    private void enableSessionValidationIfNecessary() {
         SessionValidationScheduler scheduler = getSessionValidationScheduler();
         if (isSessionValidationSchedulerEnabled() && (scheduler == null || !scheduler.isEnabled())) {
             enableSessionValidation();
@@ -96,38 +87,6 @@
     }
 
     /**
-     * Returns the time in milliseconds that any session may remain idle before expiring.  This
-     * value is just a main default for all sessions and may be overridden by subclasses on a
-     * <em>per-session</em> basis by overriding the {@link #getTimeout(Session)} method if
-     * so desired.
-     *
-     * <ul>
-     * <li>A negative return value means sessions never expire.</li>
-     * <li>A non-negative return value (0 or greater) means session timeout will occur as expected.</li>
-     * </ul>
-     *
-     * <p>Unless overridden via the {@link #setGlobalSessionTimeout} method, the default value is
-     * {@link #DEFAULT_GLOBAL_SESSION_TIMEOUT}.
-     *
-     * @return the time in milliseconds that any session may remain idle before expiring.
-     */
-    public long getGlobalSessionTimeout() {
-        return globalSessionTimeout;
-    }
-
-    /**
-     * Sets the time in milliseconds that any session may remain idle before expiring.  This
-     * value is just a main default for all sessions.  Subclasses may override the
-     * {@link #getTimeout} method to determine time-out values on a <em>per-session</em> basis.
-     *
-     * @param globalSessionTimeout the time in milliseconds any session may remain idle before
-     *                             expiring.
-     */
-    public void setGlobalSessionTimeout(int globalSessionTimeout) {
-        this.globalSessionTimeout = globalSessionTimeout;
-    }
-
-    /**
      * If using the underlying default <tt>SessionValidationScheduler</tt> (that is, the
      * {@link #setSessionValidationScheduler(SessionValidationScheduler) setSessionValidationScheduler} method is
      * never called) , this method allows one to specify how
@@ -196,13 +155,13 @@
     }
 
     protected SessionValidationScheduler createSessionValidationScheduler() {
-        SessionValidationScheduler scheduler;
+        ExecutorServiceSessionValidationScheduler scheduler;
 
         if (log.isDebugEnabled()) {
             log.debug("No sessionValidationScheduler set.  Attempting to create default instance.");
         }
         scheduler = new ExecutorServiceSessionValidationScheduler(this);
-        ((ExecutorServiceSessionValidationScheduler) scheduler).setInterval(getSessionValidationInterval());
+        scheduler.setInterval(getSessionValidationInterval());
         if (log.isTraceEnabled()) {
             log.trace("Created default SessionValidationScheduler instance of type [" + scheduler.getClass().getName() + "].");
         }
diff --git a/core/test/org/jsecurity/mgt/DefaultSecurityManagerTest.java b/core/test/org/jsecurity/mgt/DefaultSecurityManagerTest.java
index affee67..1ecfba9 100644
--- a/core/test/org/jsecurity/mgt/DefaultSecurityManagerTest.java
+++ b/core/test/org/jsecurity/mgt/DefaultSecurityManagerTest.java
@@ -39,9 +39,9 @@
 
     @Before
     public void setup() {
+        ThreadContext.clear();
         sm = new DefaultSecurityManager();
         sm.setRealm(new PropertiesRealm());
-        ThreadContext.clear();
     }
 
     @After
diff --git a/web/src/org/jsecurity/web/DefaultWebSecurityManager.java b/web/src/org/jsecurity/web/DefaultWebSecurityManager.java
index a4b98d0..668916f 100644
--- a/web/src/org/jsecurity/web/DefaultWebSecurityManager.java
+++ b/web/src/org/jsecurity/web/DefaultWebSecurityManager.java
@@ -59,8 +59,9 @@
     private String sessionMode = HTTP_SESSION_MODE; //default

 

     public DefaultWebSecurityManager() {

-        WebSessionManager sm = new DefaultWebSessionManager();

-        applySessionManager(sm);

+        super();

+        WebSessionManager sm = new ServletContainerSessionManager();

+        setSessionManager(sm);

         setRememberMeManager(new WebRememberMeManager());

         setSubjectFactory(new WebSubjectFactory(this, sm));

     }

@@ -142,13 +143,13 @@
                     HTTP_SESSION_MODE + "' being the default.";

             throw new IllegalArgumentException(msg);

         }

-        boolean recreate = this.sessionMode == null || !this.sessionMode.equals(sessionMode);

-        this.sessionMode = sessionMode;

+        boolean recreate = this.sessionMode == null || !this.sessionMode.equals(mode);

+        this.sessionMode = mode;

         if (recreate) {

             LifecycleUtils.destroy(getSessionManager());

-            SessionManager sessionManager = newSessionManagerInstance();

-            applySessionManager(sessionManager);

-            setSubjectFactory(new WebSubjectFactory(this, (WebSessionManager) sessionManager));

+            WebSessionManager sessionManager = createSessionManager(mode);

+            setSessionManager(sessionManager);

+            setSubjectFactory(new WebSubjectFactory(this, sessionManager));

         }

     }

 

@@ -156,17 +157,18 @@
         return this.sessionMode == null || this.sessionMode.equals(HTTP_SESSION_MODE);

     }

 

-    protected SessionManager newSessionManagerInstance() {

-        if (isHttpSessionMode()) {

+    protected WebSessionManager createSessionManager(String sessionMode) {

+        if (sessionMode == null || sessionMode.equalsIgnoreCase(HTTP_SESSION_MODE)) {

             if (log.isInfoEnabled()) {

-                log.info(HTTP_SESSION_MODE + " mode - enabling ServletContainerSessionManager (Http Sessions)");

+                log.info(HTTP_SESSION_MODE + " mode - enabling ServletContainerSessionManager (HTTP-only Sessions)");

             }

             return new ServletContainerSessionManager();

         } else {

             if (log.isInfoEnabled()) {

-                log.info(JSECURITY_SESSION_MODE + " mode - enabling WebSessionManager (JSecurity heterogenous sessions)");

+                log.info(JSECURITY_SESSION_MODE + " mode - enabling DefaultWebSessionManager (HTTP + heterogeneous-client sessions)");

             }

             return new DefaultWebSessionManager();

         }

     }

+

 }

diff --git a/web/src/org/jsecurity/web/session/ServletContainerSessionManager.java b/web/src/org/jsecurity/web/session/ServletContainerSessionManager.java
index 7d18ac8..a7f4262 100644
--- a/web/src/org/jsecurity/web/session/ServletContainerSessionManager.java
+++ b/web/src/org/jsecurity/web/session/ServletContainerSessionManager.java
@@ -35,18 +35,19 @@
 /**
  * SessionManager implementation providing Session implementations that are merely wrappers for the
  * Servlet container's HttpSession.
- *
- * <p>Despite its name, this implementation <em>does not</em> itself manage Sessions since the Servlet container
+ * <p/>
+ * Despite its name, this implementation <em>does not</em> itself manage Sessions since the Servlet container
  * provides the actual management support.  This class mainly exists to 'impersonate' a regular JSecurity
  * <tt>SessionManager</tt> so it can be pluggable into a normal JSecurity configuration in a pure web application.
+ * <p/>
+ * Note that because this implementation relies on the {@link HttpSession HttpSession}, it is only functional in a
+ * servlet container.  I.e. it is <em>NOT</em> capable of supporting Sessions any clients other than
+ * {@code HttpRequest/HttpResponse} based clients.
  *
- * <p>Note that because this implementation relies on the <tt>HttpSession</tt>, it is only functional in a servlet
- * container.  I.e. it is <em>NOT</em> capable of supporting Sessions any clients other than HttpRequest/HttpResponse
- * based clients.
- *
- * <p>Therefore, if you need heterogenous Session support across multiple client mediums (e.g. web pages,
- * Flash applets, Java Web Start applications, etc.), use the {@link DefaultWebSessionManager WebSessionManager} instead.  The
- * <tt>WebSessionManager</tt> supports both traditional web-based access as well as non web-based clients.
+ * <p>Therefore, if you need {@code Session} access from heterogenous client mediums (e.g. web pages,
+ * Flash applets, Java Web Start applications, etc.), use the {@link DefaultWebSessionManager DefaultWebSessionManager}
+ * instead.  The {@code DefaultWebSessionManager} supports both traditional web-based access as well as non web-based
+ * clients.
  *
  * @author Les Hazlewood
  * @since 0.9
@@ -55,6 +56,8 @@
 
     //TODO - complete JavaDoc
 
+    //TODO - read session timeout value from web.xml
+
     public ServletContainerSessionManager() {
     }
 
@@ -78,6 +81,11 @@
     protected Session createSession(InetAddress originatingHost) throws HostUnauthorizedException, IllegalArgumentException {
         ServletRequest request = WebUtils.getRequiredServletRequest();
         HttpSession httpSession = ((HttpServletRequest) request).getSession();
+
+        //ensure that the httpSession timeout reflects what is configured:
+        long timeoutMillis = getGlobalSessionTimeout();
+        httpSession.setMaxInactiveInterval((int) (timeoutMillis / MILLIS_PER_SECOND));
+
         return createSession(httpSession, originatingHost);
     }