[jboss-cvs] JBossAS SVN: r57326 - branches/Branch_4_0/tomcat/src/main/org/jboss/web/tomcat/tc5/sso

jboss-cvs-commits at lists.jboss.org jboss-cvs-commits at lists.jboss.org
Sun Oct 1 11:30:53 EDT 2006


Author: bstansberry at jboss.com
Date: 2006-10-01 11:30:51 -0400 (Sun, 01 Oct 2006)
New Revision: 57326

Modified:
   branches/Branch_4_0/tomcat/src/main/org/jboss/web/tomcat/tc5/sso/ClusteredSingleSignOn.java
   branches/Branch_4_0/tomcat/src/main/org/jboss/web/tomcat/tc5/sso/TreeCacheSSOClusterManager.java
   branches/Branch_4_0/tomcat/src/main/org/jboss/web/tomcat/tc5/sso/mbeans-descriptors.xml
Log:
[JBAS-3096] Add maxEmptyLife concept

Modified: branches/Branch_4_0/tomcat/src/main/org/jboss/web/tomcat/tc5/sso/ClusteredSingleSignOn.java
===================================================================
--- branches/Branch_4_0/tomcat/src/main/org/jboss/web/tomcat/tc5/sso/ClusteredSingleSignOn.java	2006-10-01 14:22:21 UTC (rev 57325)
+++ branches/Branch_4_0/tomcat/src/main/org/jboss/web/tomcat/tc5/sso/ClusteredSingleSignOn.java	2006-10-01 15:30:51 UTC (rev 57326)
@@ -16,13 +16,12 @@
 package org.jboss.web.tomcat.tc5.sso;
 
 
-import org.jboss.web.tomcat.tc5.Tomcat5;
-import org.jboss.web.tomcat.tc5.session.JBossManager;
-
 import java.io.IOException;
 import java.security.Principal;
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
 import java.util.Set;
 
 import javax.management.ObjectName;
@@ -34,15 +33,19 @@
 import org.apache.catalina.LifecycleException;
 import org.apache.catalina.LifecycleListener;
 import org.apache.catalina.Manager;
+import org.apache.catalina.Realm;
 import org.apache.catalina.Session;
-import org.apache.catalina.Realm;
 import org.apache.catalina.SessionEvent;
 import org.apache.catalina.authenticator.Constants;
 import org.apache.catalina.connector.Request;
 import org.apache.catalina.connector.Response;
 import org.apache.catalina.session.ManagerBase;
+import org.jboss.web.tomcat.tc5.Tomcat5;
+import org.jboss.web.tomcat.tc5.session.JBossManager;
 
+import EDU.oswego.cs.dl.util.concurrent.ConcurrentHashMap;
 
+
 /**
  * A <strong>Valve</strong> that supports a "single sign on" user experience,
  * where the security identity of a user who successfully authenticates to one
@@ -67,7 +70,11 @@
    extends org.apache.catalina.authenticator.SingleSignOn
    implements LifecycleListener
 {
-
+   /** By default we process expired SSOs no more often than once per minute */
+   public static final int DEFAULT_PROCESS_EXPIRES_INTERVAL = 60;
+   /** By default we let SSOs without active sessions live for 30 mins */
+   public static final int DEFAULT_MAX_EMPTY_LIFE = 1800;
+   
    // Override the superclass value
    static
    {
@@ -97,7 +104,37 @@
     */
    private String treeCacheName = Tomcat5.DEFAULT_CACHE_NAME;
 
+// DISABLED UNTIL 4.0.6.CR1   
+//   /**
+//    * Object name of the thread pool used by SSOClusterManager.
+//    * Only relevant if the SSOClusterManager implementation is
+//    * TreeCacheSSOClusterManager.
+//    */
+//   private String threadPoolName = "jboss.system:service=ThreadPool";
+
+   /** Currently started Managers that have associated as session with an SSO */
    private Set activeManagers = Collections.synchronizedSet(new HashSet());
+   
+   /** Max number of ms an SSO with no active sessions will be usable by a request */
+   private int maxEmptyLife = DEFAULT_MAX_EMPTY_LIFE * 1000;
+   
+   /** 
+    * Minimum number of ms since the last processExpires() run 
+    * before a new run is allowed.
+    */
+   private int processExpiresInterval = DEFAULT_PROCESS_EXPIRES_INTERVAL * 1000;
+   
+   /** Timestamp of the last processExpires() run */
+   private long lastProcessExpires = System.currentTimeMillis();
+   
+   /** 
+    * Map<String, Long> containing the ids of SSOs with no active sessions
+    * and the time at which they entered that state
+    */
+   private Map emptySSOs = new ConcurrentHashMap();
+   
+   /** Used for sync locking of processExpires runs */
+   private final Object mutex = new Object();
 
    // ------------------------------------------------------------- Properties
 
@@ -216,8 +253,133 @@
          ((TreeCacheSSOClusterManager) ssoClusterManager).setCacheName(cacheName);
       }
    }
+   
+// DISABLED UNTIL 4.0.6.CR1
+//   /**
+//    * Object name of the thread pool used by SSOClusterManager.
+//    * Only relevant if the SSOClusterManager implementation is
+//    * TreeCacheSSOClusterManager.
+//    */
+//   public String getThreadPoolName()
+//   {
+//      return threadPoolName;
+//   }
+//
+//   /**
+//    * Sets the object name of the thread pool used by SSOClusterManager.
+//    * Only relevant if the SSOClusterManager implementation is
+//    * TreeCacheSSOClusterManager.
+//    */
+//   public void setThreadPoolName(String poolName)
+//      throws Exception
+//   {
+//      this.threadPoolName = poolName;
+//      if (ssoClusterManager != null
+//         && ssoClusterManager instanceof TreeCacheSSOClusterManager)
+//      {
+//         ((TreeCacheSSOClusterManager) ssoClusterManager).setThreadPoolName(poolName);
+//      }
+//   }
 
+   /**
+    * Gets the max number of seconds an SSO with no active sessions will be 
+    * usable by a request.
+    * 
+    * @return a non-negative number
+    * 
+    * @see #DEFAULT_MAX_EMPTY_LIFE    * 
+    * @see #setMaxEmptyLife() 
+    */
+   public int getMaxEmptyLife()
+   {
+      return (maxEmptyLife / 1000);
+   }
 
+
+   /**
+    * Sets the maximum number of seconds an SSO with no active sessions will be 
+    * usable by a request.
+    * <p>
+    * A positive value for this property allows a user to continue to use an SSO
+    * even after all the sessions associated with it have been expired. It does not
+    * keep an SSO alive if a session associated with it has been invalidated due to 
+    * an <code>HttpSession.invalidate()</code> call.
+    * </p>
+    * <p>
+    * The primary purpose of this property is to avoid the situation where a server
+    * on which all of an SSO's sessions lives is shutdown, thus expiring all the
+    * sessions and causing the invalidation of the SSO. A positive value for this
+    * property would give the user an opportunity to fail over to another server
+    * and maintain the SSO.
+    * </p>
+    * 
+    * @param maxEmptyLife a non-negative number
+    * 
+    * @throws IllegalArgumentException if <code>maxEmptyLife < 0</code>
+    */
+   public void setMaxEmptyLife(int maxEmptyLife)
+   {
+      if (maxEmptyLife < 0)
+         throw new IllegalArgumentException("maxEmptyLife must be >= 0");
+      
+      this.maxEmptyLife = maxEmptyLife * 1000;
+   }
+
+
+   /**
+    * Gets the minimum number of seconds since the start of the last check for overaged
+    * SSO's with no active sessions before a new run is allowed.
+    * 
+    * @return a positive number
+    * 
+    * @see #DEFAULT_PROCESS_EXPIRES_INTERVAL
+    * @see #setMaxEmptyLife() 
+    * @see #setProcessExpiresInterval(int)
+    */
+   public int getProcessExpiresInterval()
+   {
+      return processExpiresInterval / 1000;
+   }
+
+   /**
+    * Sets the minimum number of seconds since the start of the last check for overaged
+    * SSO's with no active sessions before a new run is allowed.  During this check,
+    * any such overaged SSOs will be invalidated. 
+    * <p>
+    * Note that setting this value does not imply that a check will be performed
+    * every <code>processExpiresInterval</code> seconds, only that it will not
+    * be performed more often than that.
+    * </p>
+    * 
+    * @param processExpiresInterval a non-negative number. <code>0</code> means
+    *                               the overage check can be performed whenever
+    *                               the container wishes to.
+    *                               
+    * @throws IllegalArgumentException if <code>processExpiresInterval < 1</code>
+    * 
+    * @see #setMaxEmptyLife()
+    */
+   public void setProcessExpiresInterval(int processExpiresInterval)
+   {
+      if (processExpiresInterval < 0)
+         throw new IllegalArgumentException("processExpiresInterval must be >= 0");
+      
+      this.processExpiresInterval = processExpiresInterval * 1000;
+   }
+
+
+   /**
+    * Gets the timestamp of the start of the last check for overaged
+    * SSO's with no active sessions.
+    * 
+    * @see #setProcessExpiresInterval(int)
+    */
+   public long getLastProcessExpires()
+   {
+      return lastProcessExpires;
+   }
+
+
    // ------------------------------------------------------ Lifecycle Methods
 
 
@@ -314,21 +476,35 @@
       if (ssoId == null)
          return;
 
-      // Was the session destroyed as the result of a timeout or
-      // the undeployment of the containing webapp?
-      // If so, we'll just remove the expired session from the
-      // SSO.  If the session was logged out, we'll log out
-      // of all sessions associated with the SSO.
-      if (isSessionTimedOut(session) || isManagerStopped(session))
+      try
       {
-         removeSession(ssoId, session);
+         // Was the session destroyed as the result of a timeout or
+         // the undeployment of the containing webapp?
+         // If so, we'll just remove the expired session from the
+         // SSO.  If the session was logged out, we'll log out
+         // of all sessions associated with the SSO.
+         if (isSessionTimedOut(session) || isManagerStopped(session))
+         {
+            removeSession(ssoId, session);
+
+            // Quite poor.  We hijack the caller thread (the Tomcat background thread)
+            // to do our cleanup of expired sessions
+            processExpires();
+         }
+         else
+         {
+            // The session was logged out.
+            logout(ssoId);
+         }
       }
-      else
+      catch (Exception e)
       {
-         // The session was logged out.
-         logout(ssoId);
+         // Don't propagate back to the webapp; we don't want to disrupt
+         // the session expiration process
+         getContainer().getLogger().error("Caught exception updating SSO " + ssoId +
+                                          " following destruction of session " +
+                                          session.getIdInternal(), e);
       }
-
    }
 
    private boolean isSessionTimedOut(Session session)
@@ -463,10 +639,11 @@
       }
 
       // Look up the cached Principal associated with this cookie value
+      String ssoId = cookie.getValue();
       if (getContainer().getLogger().isDebugEnabled())
-          getContainer().getLogger().debug(" Checking for cached principal for " + cookie.getValue());
+          getContainer().getLogger().debug(" Checking for cached principal for " + ssoId);
       SingleSignOnEntry entry = getSingleSignOnEntry(cookie.getValue());
-      if (entry != null)
+      if (entry != null && isValid(ssoId, entry))
       {
          Principal ssoPrinc = entry.getPrincipal();
          // have to deal with the fact that the entry may not have an
@@ -693,7 +870,7 @@
             }
          }
       }
-
+      
       return sso;
    }
 
@@ -811,12 +988,12 @@
          reverse.remove(session);
       }
 
-      // If there are no sessions left in the SingleSignOnEntry,
-      // deregister the entry.
-      if (entry.getSessionCount() == 0)
-      {
-         deregister(ssoId);
-      }
+//      // If there are no sessions left in the SingleSignOnEntry,
+//      // deregister the entry.
+//      if (entry.getSessionCount() == 0)
+//      {
+//         deregister(ssoId);
+//      }
    }
 
 
@@ -980,6 +1157,24 @@
       }
 
    }
+   
+   /**
+    * Callback from the SSOManager when it detects an SSO without
+    * any active sessions across the cluster
+    */
+   void notifySSOEmpty(String ssoId)
+   {
+      emptySSOs.put(ssoId, new Long(System.currentTimeMillis()));
+   }
+   
+   /**
+    * Callback from the SSOManager when it detects an SSO that
+    * has active sessions across the cluster
+    */
+   void notifySSONotEmpty(String ssoId)
+   {
+      emptySSOs.remove(ssoId);
+   }
 
    
    // -------------------------------------------------------  Private Methods
@@ -1018,6 +1213,8 @@
             if (mgr instanceof TreeCacheSSOClusterManager)
             {
                ((TreeCacheSSOClusterManager) mgr).setCacheName(getTreeCacheName());
+// DISABLED UNTIL 4.0.6.CR1
+//               ((TreeCacheSSOClusterManager) mgr).setThreadPoolName(getThreadPoolName());
             }
             ssoClusterManager = mgr;
             clusterManagerClass = className;
@@ -1035,5 +1232,57 @@
          }
       }
    }
+   
+   
+   private void processExpires()
+   {
+      long now = 0L;
+      synchronized (mutex)
+      {
+         now = System.currentTimeMillis();
+      
+         if (now - lastProcessExpires > processExpiresInterval)
+         {
+            lastProcessExpires = now;
+         }
+         else
+         {
+             return;
+         }
+      }
+      
+      clearExpiredSSOs(now);
+   }
+   
+   private synchronized void clearExpiredSSOs(long now)
+   {      
+      for (Iterator iter = emptySSOs.entrySet().iterator(); iter.hasNext();)
+      {
+         Map.Entry entry = (Map.Entry) iter.next();
+         if ( (now - ((Long) entry.getValue()).longValue()) > maxEmptyLife)
+         {
+            logout((String) entry.getKey());
+            iter.remove();
+         }
+      }      
+   }
+   
+   private boolean isValid(String ssoId, SingleSignOnEntry entry)
+   {
+      boolean valid = true;
+      if (entry.getSessionCount() == 0)
+      {
+         Long expired = (Long) emptySSOs.get(ssoId);
+         if (expired != null 
+               && (System.currentTimeMillis() - expired.longValue()) > maxEmptyLife)
+         {
+            valid = false;
+            logout(ssoId);
+            emptySSOs.remove(ssoId);
+         }
+      }
+      
+      return valid;
+   }
 
 }
\ No newline at end of file

Modified: branches/Branch_4_0/tomcat/src/main/org/jboss/web/tomcat/tc5/sso/TreeCacheSSOClusterManager.java
===================================================================
--- branches/Branch_4_0/tomcat/src/main/org/jboss/web/tomcat/tc5/sso/TreeCacheSSOClusterManager.java	2006-10-01 14:22:21 UTC (rev 57325)
+++ branches/Branch_4_0/tomcat/src/main/org/jboss/web/tomcat/tc5/sso/TreeCacheSSOClusterManager.java	2006-10-01 15:30:51 UTC (rev 57326)
@@ -24,7 +24,9 @@
 import java.io.Serializable;
 import java.security.Principal;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.Set;
+import java.util.Vector;
 
 import javax.management.AttributeNotFoundException;
 import javax.management.MBeanException;
@@ -37,14 +39,18 @@
 import org.apache.catalina.LifecycleListener;
 import org.apache.catalina.Session;
 import org.apache.catalina.util.LifecycleSupport;
+import org.jboss.cache.AbstractTreeCacheListener;
 import org.jboss.cache.Fqn;
-import org.jboss.cache.TreeCache;
 import org.jboss.cache.TreeCacheListener;
 import org.jboss.cache.config.Option;
 import org.jboss.logging.Logger;
+import org.jboss.mx.util.MBeanProxyExt;
 import org.jboss.mx.util.MBeanServerLocator;
 import org.jboss.util.NestedRuntimeException;
+import org.jboss.util.threadpool.ThreadPool;
+import org.jboss.util.threadpool.ThreadPoolMBean;
 import org.jboss.web.tomcat.tc5.Tomcat5;
+import org.jgroups.Address;
 import org.jgroups.View;
 
 /**
@@ -55,7 +61,8 @@
  * @version $Revision$ $Date$
  */
 public final class TreeCacheSSOClusterManager
-   implements SSOClusterManager, TreeCacheListener
+   extends AbstractTreeCacheListener
+   implements SSOClusterManager
 {
    // -------------------------------------------------------------  Constants
 
@@ -108,12 +115,15 @@
          {Fqn.class.getName(), Option.class.getName()};
    
    private static final Option GRAVITATE_OPTION = new Option();
-
+   private static final Option LOCAL_OPTION = new Option();
+   
    static
    {
       GRAVITATE_OPTION.setForceDataGravitation(true);
+      LOCAL_OPTION.setCacheModeLocal(true);
    }
    
+   
    // -------------------------------------------------------  Instance Fields
    
    /**
@@ -135,7 +145,12 @@
     * ObjectName of the TreeCache
     */
    private ObjectName cacheObjectName = null;
-
+   
+// DISABLED UNTIL 4.0.6.CR1
+//   private String threadPoolName = "jboss.system:service=ThreadPool";
+//
+//   private ThreadPool threadPool;
+   
    /**
     * String version of the object name to use to access the TreeCache
     */
@@ -191,6 +206,13 @@
     */
    private Serializable localAddress = null;
    
+// DISABLED UNTIL 4.0.6.CR1
+//   /** The last view passed to viewAccepted() */
+//   private View currentView;
+//   
+//   /** Mutex lock to ensure only one view change at a time is being processed */
+//   private Object cleanupMutex = new Object();
+   
    // ----------------------------------------------------------  Constructors
 
    
@@ -276,8 +298,28 @@
       }
    }
    
+// DISABLED UNTIL 4.0.6.CR1
+//   public String getThreadPoolName()
+//   {
+//      return threadPoolName;
+//   }
+//
+//
+//   public void setThreadPoolName(String threadPoolName)
+//   {
+//      if (started)
+//      {
+//         log.info("Call to setThreadPoolName() ignored; already started");
+//      }
+//      else
+//      {
+//         this.threadPoolName = threadPoolName;
+//      }
+//   }
+   
    // -----------------------------------------------------  SSOClusterManager
 
+
    /**
     * Notify the cluster of the addition of a Session to an SSO session.
     *
@@ -319,9 +361,9 @@
          if(doTx)
             tm.begin();
          
-         Set sessions = getSessionSet(fqn, true);
+         Set sessions = getSessionSet(fqn, true, false);
          sessions.add(new SessionAddress(session.getId(), localAddress));
-         putInTreeCache(fqn, sessions);
+         putInTreeCache(fqn, sessions, false);
       }
       catch (Exception e)
       {
@@ -439,7 +481,7 @@
       Fqn fqn = getCredentialsFqn(ssoId);
       try
       {
-         SSOCredentials data = (SSOCredentials) getFromTreeCache(fqn);
+         SSOCredentials data = (SSOCredentials) getFromTreeCache(fqn, false);
          if (data != null)
          {
             entry = new SingleSignOnEntry(null,
@@ -516,7 +558,7 @@
 
       Fqn fqn = getSessionsFqn(ssoId);
       boolean doTx = false;
-      boolean removing = false;
+//      boolean removing = false;
       try
       {
          // Confirm we have a transaction manager; if not get it from TreeCache
@@ -532,24 +574,24 @@
          if(doTx)
             tm.begin();
          
-         Set sessions = getSessionSet(fqn, false);
+         Set sessions = getSessionSet(fqn, false, false);
          if (sessions != null)
          {
             sessions.remove(new SessionAddress(session.getId(), localAddress));
-            if (sessions.size() == 0)
-            {               
-               // No sessions left; remove node
-               
-               // Add this SSO to our list of in-process local removals so
-               // this.nodeRemoved() will ignore the removal
-               removing = true;
-               beingLocallyRemoved.set(ssoId);
-               removeFromTreeCache(getSingleSignOnFqn(ssoId));
-            }
-            else
-            {
-               putInTreeCache(fqn, sessions);
-            }
+//            if (sessions.size() == 0)
+//            {               
+//               // No sessions left; remove node
+//               
+//               // Add this SSO to our list of in-process local removals so
+//               // this.nodeRemoved() will ignore the removal
+//               removing = true;
+//               beingLocallyRemoved.set(ssoId);
+//               removeFromTreeCache(getSingleSignOnFqn(ssoId));
+//            }
+//            else
+//            {
+               putInTreeCache(fqn, sessions, false);
+//            }
          }
       }
       catch (Exception e)
@@ -569,18 +611,18 @@
       }
       finally
       {
-         try
-         {
-            if (removing)
-            {
-               beingLocallyRemoved.set(null);
-            }
-         }
-         finally
-         {
+//         try
+//         {
+//            if (removing)
+//            {
+//               beingLocallyRemoved.set(null);
+//            }
+//         }
+//         finally
+//         {
             if (doTx)
                endTransaction();
-         }
+//         }
       }
    }
 
@@ -616,50 +658,6 @@
    // ------------------------------------------------------  TreeCacheListener
 
    /**
-    * Does nothing
-    */
-   public void nodeCreated(Fqn fqn)
-   {
-      ; // do nothing
-   }
-
-   /**
-    * Does nothing
-    */
-   public void nodeLoaded(Fqn fqn)
-   {
-      ; // do nothing
-   }
-
-
-   /**
-    * Does nothing
-    */
-   public void nodeVisited(Fqn fqn)
-   {
-      ; // do nothing
-   }
-
-
-   /**
-    * Does nothing
-    */
-   public void cacheStarted(TreeCache cache)
-   {
-      ; // do nothing
-   }
-
-
-   /**
-    * Does nothing
-    */
-   public void cacheStopped(TreeCache cache)
-   {
-      ; // do nothing
-   }
-
-
-   /**
     * Extracts an SSO session id from the Fqn and uses it in an invocation of
     * {@link ClusteredSingleSignOn#deregister(String) ClusteredSingleSignOn.deregister(String)}.
     * <p/>
@@ -717,14 +715,26 @@
     */
    public void nodeModified(Fqn fqn)
    {
-      // We are only interested in changes to the CREDENTIALS node
-      if (CREDENTIALS.equals(getTypeFromFqn(fqn)) == false)
+      String type = getTypeFromFqn(fqn);
+      if (CREDENTIALS.equals(type))
       {
-         return;
+         handleCredentialUpdate(fqn);
       }
+      else if (SESSIONS.equals(type))
+      {
+         handleSessionSetChange(fqn);
+      }
+   }
 
-      String ssoId = getIdFromFqn(fqn); // won't be null or above check fails
 
+   /**
+    *  
+    * @param fqn an Fqn that points to the CREDENTIALS node of an SSO
+    */
+   private void handleCredentialUpdate(Fqn fqn)
+   {
+      String ssoId = getIdFromFqn(fqn); // won't be null per the API contract
+
       // Ignore invocations that come as a result of our additions
       if (ssoId.equals(beingLocallyAdded.get()))
       {
@@ -747,7 +757,7 @@
 //      credentialUpdater.enqueue(sso, ssoId);
       try
       {
-         SSOCredentials data = (SSOCredentials) getFromTreeCache(fqn);
+         SSOCredentials data = (SSOCredentials) getFromTreeCache(fqn, false);
          if (data != null)
          {
             // We want to release our read lock quickly, so get the needed
@@ -774,28 +784,92 @@
          log.error("failed to update credentials for SSO " + ssoId, e);
       }
    }
-
-
+   
    /**
-    * Does nothing
+    *  
+    * @param fqn an Fqn that points to the SESSIONS node of an SSO
     */
-   public void viewChange(View new_view)
+   private void handleSessionSetChange(Fqn fqn)
    {
-      ; // do nothing
+      // Here we *want* to handle local activity as well as remote,
+      // as the distributed cache set of sessions is the only
+      // complete representation
+      
+      String ssoId = getIdFromFqn(fqn);
+      try      
+      {
+         Set sessions = (Set) getFromTreeCache(fqn, false);
+         if (sessions == null)
+         {
+            // don't think we'd get this event; 
+            // in any case we don't deal with this
+         }
+         else if (sessions.size() == 0)
+         {
+            ssoValve.notifySSOEmpty(ssoId);            
+         }
+         else
+         {
+            ssoValve.notifySSONotEmpty(ssoId);
+         }
+      }
+      catch (Exception e)
+      {
+         log.error("failed in check for empty SSO " + ssoId, e);
+      }
    }
 
-
-   /**
-    * Does nothing. Called when a node is evicted (not the same as remove()).
-    *
-    * @param fqn
-    */
-   public void nodeEvicted(Fqn fqn)
-   {
-      // TODO do we need to handle this?
-      ; // do nothing
-   }
-
+// DISABLE UNTIL 4.0.6.CR1   
+//   /**
+//    * If any nodes have been removed from the view, asynchronously scans
+//    * all SSOs looking for and removing sessions owned by the removed node.
+//    * Notifies the SSO valve if as a result any SSOs no longer have active
+//    * sessions.  If the removed node is the one associated with this object,
+//    * does nothing.
+//    */
+//   public void viewChange(View new_view)
+//   {
+//      if (currentView == null)
+//      {
+//         currentView = new_view;
+//         return;
+//      }
+//      
+//      Vector oldMembers = currentView.getMembers();
+//      
+//      currentView = new_view;
+//      
+//      // If we're not in the view, just exit
+//      if (localAddress == null || !currentView.containsMember((Address) localAddress))
+//         return;
+//      
+//      boolean hasDeadMembers = false;
+//      for (Iterator iter = oldMembers.iterator(); iter.hasNext(); )
+//      {
+//         Address member = (Address) iter.next();
+//         if (!new_view.containsMember(member))
+//         {
+//            hasDeadMembers = true;
+//            break;
+//         }
+//      }
+//      
+//      if (hasDeadMembers)
+//      {
+//         DeadMemberCleaner cleaner = new DeadMemberCleaner();
+//         if (threadPool != null)
+//         {
+//            threadPool.run(cleaner);
+//         }
+//         else
+//         {
+//            Thread t = new Thread(cleaner, "ClusteredSSOCleaner");
+//            t.setDaemon(true);
+//            t.start();
+//         }
+//      }
+//         
+//   }   
    
    // -------------------------------------------------------------  Lifecycle
 
@@ -862,6 +936,9 @@
                                       "TransactionManager from TreeCache", e);
       }
       
+//    DISABLED UNTIL 4.0.6.CR1
+//      initThreadPool();
+      
       started = true;
 
       // Notify our interested LifecycleListeners
@@ -896,11 +973,20 @@
    
    // -------------------------------------------------------  Private Methods
 
-   private Object getFromTreeCache(Fqn fqn) throws Exception
+   private Object getFromTreeCache(Fqn fqn, boolean localOnly) throws Exception
    {
-      Object[] args = new Object[]{fqn, KEY, GRAVITATE_OPTION};
+      Object[] args = new Object[]{fqn, KEY, localOnly ? LOCAL_OPTION : GRAVITATE_OPTION};
       return server.invoke(getCacheObjectName(), "get", args, GET_SIGNATURE);
    }
+   
+// DISABLED UNTIL 4.0.6.CR1
+//   private Set getSSOIds() throws Exception
+//   {
+//      Fqn ssoRoot = new Fqn(new Object[] {SSO});
+//      return (Set) server.invoke(getCacheObjectName(), "getChildrenNames", 
+//                                 new Object[]{ ssoRoot }, 
+//                                 new String[]{ Fqn.class.getName() });
+//   }
 
    private Fqn getCredentialsFqn(String ssoid)
    {
@@ -936,10 +1022,10 @@
       return id;
    }
 
-   private Set getSessionSet(Fqn fqn, boolean create)
+   private Set getSessionSet(Fqn fqn, boolean create, boolean localOnly)
       throws Exception
    {
-      Set sessions = (Set) getFromTreeCache(fqn);
+      Set sessions = (Set) getFromTreeCache(fqn, localOnly);
       if (create && sessions == null)
       {
          sessions = new HashSet();
@@ -1068,9 +1154,9 @@
       return avail;
    }
 
-   private void putInTreeCache(Fqn fqn, Object data) throws Exception
+   private void putInTreeCache(Fqn fqn, Object data, boolean localOnly) throws Exception
    {
-      Object[] args = new Object[]{fqn, KEY, data, GRAVITATE_OPTION};
+      Object[] args = new Object[]{fqn, KEY, data, localOnly ? LOCAL_OPTION : GRAVITATE_OPTION};
       server.invoke(getCacheObjectName(), "put", args, PUT_SIGNATURE);
    }
 
@@ -1172,6 +1258,24 @@
          new Object[]{fqn, GRAVITATE_OPTION},
          REMOVE_SIGNATURE);
    }
+   
+// DISABLED UNTIL 4.0.6.CR1   
+//   private void initThreadPool()
+//   {      
+//      if (threadPoolName != null)
+//      {
+//         try
+//         {
+//            ObjectName on = new ObjectName(threadPoolName);
+//            threadPool = (ThreadPool) MBeanProxyExt.create(ThreadPoolMBean.class, on);
+//         }
+//         catch (Exception e)
+//         {
+//            log.info("Unable to access ThreadPool at " + threadPoolName + 
+//                     " -- will use individual threads for cleanup work");
+//         }
+//      }
+//   }
 
    /**
     * Stores the given data to the clustered cache in a tree branch whose FQN
@@ -1200,7 +1304,7 @@
       
       try
       {
-         putInTreeCache(getCredentialsFqn(ssoId), data);
+         putInTreeCache(getCredentialsFqn(ssoId), data, false);
       }
       catch (Exception e)
       {
@@ -1345,5 +1449,97 @@
       
    }
 
+// DISABLED UNTIL 4.0.6.CR1
+//   /**
+//    * Runnable that's run when the removal of a node from the cluster has been detected. 
+//    * Removes any SessionAddress objects associated with dead members from the
+//    * session set of each SSO.  Operates locally only so each node can independently clean
+//    * its SSOs without concern about replication lock conflicts.
+//    */
+//   private class DeadMemberCleaner implements Runnable
+//   {    
+//      public void run()
+//      {
+//         synchronized (cleanupMutex)
+//         {
+//            try
+//            {      
+//               // Ensure we have a TransactionManager
+//               if (tm == null)
+//                  configureFromCache();
+//               
+//               Set ids = getSSOIds();
+//               
+//               for (Iterator iter = ids.iterator(); iter.hasNext();)
+//               {
+//                  cleanDeadMembersFromSSO((String) iter.next());
+//                  
+//               }
+//            }
+//            catch (Exception e)
+//            {
+//               log.error("Caught exception cleaning sessions from dead cluster members from SSOs ", e);
+//            }
+//         }
+//      }
+//      
+//      private void cleanDeadMembersFromSSO(String ssoId)
+//      {
+//         Fqn fqn = getSessionsFqn(ssoId);
+//         boolean doTx = false;
+//         try
+//         {
+//            // Don't start tx if there is already one associated with this thread.
+//            if(tm.getTransaction() == null)
+//               doTx = true;
+//
+//            if(doTx)
+//               tm.begin();
+//            
+//            Set sessions = getSessionSet(fqn, false, true);
+//            if (sessions != null && sessions.size() > 0)
+//            {
+//               boolean changed = false;
+//               for (Iterator iter = sessions.iterator(); iter.hasNext();)
+//               {
+//                  SessionAddress session = (SessionAddress) iter.next();
+//                  if (!currentView.containsMember((Address) session.address))
+//                  {
+//                     iter.remove();
+//                     changed = true;
+//                  }
+//               }
+//               
+//               if (changed)
+//               {                  
+//                  if (sessions.size() == 0)
+//                  {
+//                     ssoValve.notifySSOEmpty(ssoId);
+//                  }
+//                  
+//                  putInTreeCache(fqn, sessions, true);
+//               }
+//            }
+//         }
+//         catch (Exception e)
+//         {
+//            try
+//            {
+//               if(doTx)
+//                  tm.setRollbackOnly();
+//            }
+//            catch (Exception ignored)
+//            {
+//            }
+//            log.error("caught exception cleaning dead members from SSO " + ssoId, e);
+//         }
+//         finally
+//         {
+//            if (doTx)
+//               endTransaction();
+//         }         
+//      }
+//   }
+
 } // end TreeCacheSSOClusterManager
 

Modified: branches/Branch_4_0/tomcat/src/main/org/jboss/web/tomcat/tc5/sso/mbeans-descriptors.xml
===================================================================
--- branches/Branch_4_0/tomcat/src/main/org/jboss/web/tomcat/tc5/sso/mbeans-descriptors.xml	2006-10-01 14:22:21 UTC (rev 57325)
+++ branches/Branch_4_0/tomcat/src/main/org/jboss/web/tomcat/tc5/sso/mbeans-descriptors.xml	2006-10-01 15:30:51 UTC (rev 57326)
@@ -20,6 +20,19 @@
          description="Should we attempt to reauthenticate each request against the security Realm?"
          type="boolean"/>
 
+      <attribute name="maxEmptyLife"
+         description="The maximum number of seconds an SSO with no active sessions will be usable by a request"
+         type="int"/>
+
+      <attribute name="processExpiresInterval"
+         description="The maximum number of seconds an SSO with no active sessions will be usable by a request"
+         type="int"/>
+
+      <attribute name="lastProcessExpires"
+         writeable="false"
+         description="The timestamp of the start of the last check for overaged SSO's with no active sessions."
+         type="long"/>
+
       <attribute name="clusterManager"
          description="SSOClusterManager to use for cluster support"
          type="org.jboss.web.tomcat.tc5.sso.SSOClusterManager"/>




More information about the jboss-cvs-commits mailing list