[jboss-cvs] JBossAS SVN: r60265 - branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso.

jboss-cvs-commits at lists.jboss.org jboss-cvs-commits at lists.jboss.org
Sun Feb 4 12:55:29 EST 2007


Author: bstansberry at jboss.com
Date: 2007-02-04 12:55:29 -0500 (Sun, 04 Feb 2007)
New Revision: 60265

Modified:
   branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/TreeCacheSSOClusterManager.java
Log:
[JBAS-3721] Clean up SSO entries if a server crashes

Modified: branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/TreeCacheSSOClusterManager.java
===================================================================
--- branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/TreeCacheSSOClusterManager.java	2007-02-04 17:51:13 UTC (rev 60264)
+++ branches/Branch_4_2/tomcat/src/main/org/jboss/web/tomcat/service/sso/TreeCacheSSOClusterManager.java	2007-02-04 17:55:29 UTC (rev 60265)
@@ -96,6 +96,12 @@
       JBossWeb.DEFAULT_CACHE_NAME;
 
    /**
+    * Default global value for the threadPoolName property
+    */
+   public static final String DEFAULT_THREAD_POOL_NAME =
+      "jboss.system:service=ThreadPool";
+
+   /**
     * Parameter signature used for TreeCache.get calls over JMX
     */
    private static final String[] GET_SIGNATURE =
@@ -146,10 +152,9 @@
     */
    private ObjectName cacheObjectName = null;
    
-// DISABLED UNTIL 4.0.6.CR1
-//   private String threadPoolName = "jboss.system:service=ThreadPool";
-//
-//   private ThreadPool threadPool;
+   private String threadPoolName = DEFAULT_THREAD_POOL_NAME;
+
+   private ThreadPool threadPool;
    
    /**
     * String version of the object name to use to access the TreeCache
@@ -206,13 +211,14 @@
     */
    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();
+   /** The members of the last view passed to viewChange() */
+   private Set currentView;
    
+   /** Mutex lock to ensure only one view change at a time is being processed */
+   private Object cleanupMutex = new Object();
+   
+   private boolean checkForInvalidAddresses = false;
+   
    // ----------------------------------------------------------  Constructors
 
    
@@ -298,25 +304,29 @@
       }
    }
    
-// 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;
-//      }
-//   }
+   public String getThreadPoolName()
+   {
+      return threadPoolName;
+   }
+
+
+   public void setThreadPoolName(String threadPoolName)
+   {
+      if (started)
+      {
+         log.info("Call to setThreadPoolName() ignored; already started");
+      }
+      else
+      {
+         this.threadPoolName = threadPoolName;
+      }
+   }
    
+   public boolean isUsingThreadPool()
+   {
+      return threadPool != null;
+   }
+   
    // -----------------------------------------------------  SSOClusterManager
 
 
@@ -363,6 +373,11 @@
          
          Set sessions = getSessionSet(fqn, true, false);
          sessions.add(new SessionAddress(session.getId(), localAddress));
+         
+         if (checkForInvalidAddresses)
+         {
+            cleanDeadMembers(sessions, new HashSet(currentView));
+         }
          putInTreeCache(fqn, sessions, false);
       }
       catch (Exception e)
@@ -580,20 +595,13 @@
                   sessions.size() + " sessions remain");
             }
             
-//            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);
-//            }
+
+            if (checkForInvalidAddresses)
+            {
+               cleanDeadMembers(sessions, new HashSet(currentView));
+            }
+            putInTreeCache(fqn, sessions, false);
+
          }
       }
       catch (Exception e)
@@ -613,18 +621,8 @@
       }
       finally
       {
-//         try
-//         {
-//            if (removing)
-//            {
-//               beingLocallyRemoved.set(null);
-//            }
-//         }
-//         finally
-//         {
-            if (doTx)
-               endTransaction();
-//         }
+         if (doTx)
+            endTransaction();
       }
    }
 
@@ -756,14 +754,11 @@
       }
 
       // Put this SSO in the queue of those to be updated
-//      credentialUpdater.enqueue(sso, ssoId);
       try
       {
          SSOCredentials data = (SSOCredentials) getFromTreeCache(fqn, false);
          if (data != null)
          {
-            // We want to release our read lock quickly, so get the needed
-            // data from the cache, commit the tx, and then use the data
             String authType = data.getAuthType();
             String username = data.getUsername();
             String password = data.getPassword();
@@ -821,57 +816,49 @@
       }
    }
 
-// 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();
-//         }
-//      }
-//         
-//   }   
+   /**
+    * 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 synchronized void viewChange(View new_view)
+   {
+      Set oldMembers = currentView;      
+      currentView = new HashSet(new_view.getMembers());
+      
+      if (oldMembers == null)
+         return;  // we must not be started yet
+      
+      // If we're not in the view, just exit
+      if (localAddress == null || !currentView.contains((Address) localAddress))
+         return;
+      
+      // Remove all the current members from the old set; any left 
+      // are the dead members
+      oldMembers.removeAll(currentView);
+      
+      if (oldMembers.size() > 0)
+      {
+         // Inform threads updating our SSOs to check for dead members so
+         // we don't replicate a dead member and thus override another
+         // node's cleanup work
+         this.checkForInvalidAddresses = true;
+         DeadMemberCleaner cleaner = new DeadMemberCleaner();
+         if (threadPool != null)
+         {
+            threadPool.run(cleaner);
+         }
+         else
+         {
+            Thread t = new Thread(cleaner, "ClusteredSSOCleaner");
+            t.setDaemon(true);
+            t.start();
+         }
+      }
+         
+   }   
    
    // -------------------------------------------------------------  Lifecycle
 
@@ -938,8 +925,7 @@
                                       "TransactionManager from TreeCache", e);
       }
       
-//    DISABLED UNTIL 4.0.6.CR1
-//      initThreadPool();
+      initThreadPool();
       
       started = true;
 
@@ -981,14 +967,13 @@
       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 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)
    {
@@ -1081,6 +1066,12 @@
          localAddress = (Serializable) address;
       else
          localAddress = address.toString();
+      
+      // Get the currentView
+      Vector members = (Vector) server.getAttribute(getCacheObjectName(), 
+                                                   "Members");
+      if (members != null)
+         currentView = new HashSet(members);
    }
    
 
@@ -1261,23 +1252,29 @@
          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");
-//         }
-//      }
-//   }
+   private void initThreadPool()
+   {      
+      if (threadPoolName != null)
+      {
+         try
+         {
+            ObjectName on = new ObjectName(threadPoolName);
+            ThreadPoolMBean mbean = (ThreadPoolMBean) MBeanProxyExt.create(ThreadPoolMBean.class, on, server);
+            threadPool = mbean.getInstance();
+            log.debug("Using ThreadPool at " + threadPoolName + " to clean dead members");
+         }
+         catch (Exception e)
+         {
+            log.info("Unable to access ThreadPool at " + threadPoolName + 
+                     " -- will use individual threads for cleanup work");
+            log.debug("Failure to access ThreadPool due to: " + e);
+         }
+      }
+      else
+      {
+         log.debug("No ThreadPool configured -- will use individual threads for cleanup work");         
+      }
+   }
 
    /**
     * Stores the given data to the clustered cache in a tree branch whose FQN
@@ -1348,6 +1345,27 @@
          setMissingCacheErrorLogged(true);
       }
    }
+   
+   private static boolean cleanDeadMembers(Set sessionSet, Set currentMembers)
+   {
+      boolean changed = false;
+      synchronized (sessionSet)
+      {
+         for (Iterator iter = sessionSet.iterator(); iter.hasNext(); )
+         {
+            Object obj = iter.next();
+            if (obj instanceof SessionAddress)
+            {
+               if (!currentMembers.contains(((SessionAddress) obj).address))
+               {
+                  iter.remove();
+                  changed = true;
+               }
+            }
+         }
+      }
+      return changed;
+   }
 
    // ---------------------------------------------------------  Outer Classes
 
@@ -1451,97 +1469,92 @@
       
    }
 
-// 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();
-//         }         
-//      }
-//   }
+   /**
+    * 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();
+               // Copy off the currentView
+               Set members = new HashSet(currentView);
+               for (Iterator iter = ids.iterator(); iter.hasNext();)
+               {
+                  cleanDeadMembersFromSSO((String) iter.next(), members);                  
+               }
+               
+               // We've cleaned up; no need for other threads to help
+               checkForInvalidAddresses = false;
+            }
+            catch (Exception e)
+            {
+               log.error("Caught exception cleaning sessions from dead cluster members from SSOs ", e);
+            }
+         }
+      }
+      
+      private void cleanDeadMembersFromSSO(String ssoId, Set currentMembers)
+      {
+         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 = cleanDeadMembers(sessions, currentMembers);
+               
+               if (changed)
+               {                  
+                  if (sessions.size() == 0)
+                  {
+                     ssoValve.notifySSOEmpty(ssoId);
+                  }
+                  
+                  // Don't bother with the local-onlyput; we have a lock on the node 
+                  // from the get and the cached data is a ref to the set we just changed
+                  //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
 




More information about the jboss-cvs-commits mailing list