[jboss-cvs] JBossAS SVN: r77662 - in projects/cluster/ha-server-api/trunk/src: test/java/org/jboss/test/ha/framework/server and 1 other directory.

jboss-cvs-commits at lists.jboss.org jboss-cvs-commits at lists.jboss.org
Fri Aug 29 15:29:15 EDT 2008


Author: pferraro
Date: 2008-08-29 15:29:15 -0400 (Fri, 29 Aug 2008)
New Revision: 77662

Modified:
   projects/cluster/ha-server-api/trunk/src/main/java/org/jboss/ha/framework/server/HASingletonImpl.java
   projects/cluster/ha-server-api/trunk/src/test/java/org/jboss/test/ha/framework/server/HASingletonTestCase.java
Log:
[JBAS-2647] Remove potential deadlock condition from HASingletonSupport
Fixed assertions in test case to use JUnit's static methods.

Modified: projects/cluster/ha-server-api/trunk/src/main/java/org/jboss/ha/framework/server/HASingletonImpl.java
===================================================================
--- projects/cluster/ha-server-api/trunk/src/main/java/org/jboss/ha/framework/server/HASingletonImpl.java	2008-08-29 17:15:58 UTC (rev 77661)
+++ projects/cluster/ha-server-api/trunk/src/main/java/org/jboss/ha/framework/server/HASingletonImpl.java	2008-08-29 19:29:15 UTC (rev 77662)
@@ -24,6 +24,7 @@
 import java.util.EventObject;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
 
 import org.jboss.ha.framework.interfaces.ClusterNode;
 import org.jboss.ha.framework.interfaces.DistributedReplicantManager;
@@ -41,6 +42,8 @@
    private final AtomicBoolean master = new AtomicBoolean(false);
    private final HASingletonRpcHandler<E> rpcHandler = new RpcHandler();
    private final HASingletonLifecycle singletonLifecycle;
+
+   AtomicReference<ReplicantView> viewReference = new AtomicReference<ReplicantView>();
    
    private volatile HASingletonElectionPolicy electionPolicy;
    private volatile boolean restartOnMerge = true;
@@ -85,6 +88,44 @@
    }
    
    /**
+    * @{inheritDoc}
+    * Overridden to circumvent deadlock in the unlikely scenario that the view changes between
+    * drm listener registration and drm replicant addition.  See JBAS-2647 for details.
+    * @see org.jboss.ha.framework.server.HAServiceImpl#registerDRMListener()
+    */
+   @Override
+   protected void registerDRMListener() throws Exception
+   {
+      DistributedReplicantManager drm = this.getHAPartition().getDistributedReplicantManager();
+      
+      final String key = this.getHAServiceKey();
+      
+      // Temporary drm listener
+      RecordingReplicantListener listener = new RecordingReplicantListener();
+      
+      // record replicant changes, but don't handle them just yet
+      drm.registerListener(key, listener);
+
+      // this ensures that the DRM knows that this node has the singleton deployed
+      drm.add(key, this.getReplicant());
+      
+      // Now register the real listener
+      drm.registerListener(key, this);
+      
+      // ...and unregister our temporary one
+      drm.unregisterListener(key, listener);
+      
+      ReplicantView view = this.viewReference.getAndSet(null);
+      
+      // Process the recorded replicant change
+      // Typically this will be the replicant change from drm.add(...)
+      if (view != null)
+      {
+         this.partitionTopologyChanged(view.getReplicants(), view.getId(), view.isMerge());
+      }
+   }
+   
+   /**
     * @see org.jboss.ha.framework.interfaces.HASingletonLifecycle#startSingleton()
     */
    public void startSingleton()
@@ -108,13 +149,21 @@
     * @see org.jboss.ha.framework.interfaces.DistributedReplicantManager#isMasterReplica(String)
     */
    @Override
-   protected void partitionTopologyChanged(List<?> newReplicants, int newViewID, boolean merge)
+   protected void partitionTopologyChanged(List<?> newReplicants, int newViewId, boolean merge)
    {
+      ReplicantView view = this.viewReference.getAndSet(null);
+      
+      // Pre-process any recorded replicant changes
+      if (view != null)
+      {
+         this.partitionTopologyChanged(view.getReplicants(), view.getId(), view.isMerge());
+      }
+      
       boolean isElectedNewMaster = this.elected();
       
       boolean isMaster = this.master.get();
       
-      this.log.debug("partitionTopologyChanged, isElectedNewMaster=" + isElectedNewMaster + ", isMasterNode=" + isMaster + ", viewID=" + newViewID);
+      this.log.debug("partitionTopologyChanged, isElectedNewMaster=" + isElectedNewMaster + ", isMasterNode=" + isMaster + ", viewID=" + newViewId);
       
       if (isElectedNewMaster)
       {
@@ -282,4 +331,46 @@
          HASingletonImpl.this.stopIfMaster();
       }
    }
+   
+   private static class ReplicantView
+   {
+      private List<?> replicants;
+      private int id;
+      private boolean merge;
+      
+      public ReplicantView(List<?> replicants, int viewId, boolean merge)
+      {
+         this.replicants = replicants;
+         this.id = viewId;
+         this.merge = merge;
+      }
+      
+      public List<?> getReplicants()
+      {
+         return this.replicants;
+      }
+      
+      public int getId()
+      {
+         return this.id;
+      }
+      
+      public boolean isMerge()
+      {
+         return this.merge;
+      }
+   }
+   
+   class RecordingReplicantListener implements DistributedReplicantManager.ReplicantListener
+   {
+      @SuppressWarnings("unchecked")
+      public void replicantsChanged(String key, List newReplicants, int newReplicantsViewId, boolean merge)
+      {
+         if (key.equals(key))
+         {
+            // Record replicant change - overwriting any previously recorded view
+            HASingletonImpl.this.viewReference.set(new ReplicantView(newReplicants, newReplicantsViewId, merge));
+         }
+      }
+   }
 }

Modified: projects/cluster/ha-server-api/trunk/src/test/java/org/jboss/test/ha/framework/server/HASingletonTestCase.java
===================================================================
--- projects/cluster/ha-server-api/trunk/src/test/java/org/jboss/test/ha/framework/server/HASingletonTestCase.java	2008-08-29 17:15:58 UTC (rev 77661)
+++ projects/cluster/ha-server-api/trunk/src/test/java/org/jboss/test/ha/framework/server/HASingletonTestCase.java	2008-08-29 19:29:15 UTC (rev 77662)
@@ -25,8 +25,10 @@
 import java.util.Collections;
 import java.util.List;
 
+import org.easymock.Capture;
 import org.easymock.EasyMock;
 import org.jboss.ha.framework.interfaces.ClusterNode;
+import org.jboss.ha.framework.interfaces.DistributedReplicantManager;
 import org.jboss.ha.framework.interfaces.HASingletonElectionPolicy;
 import org.jboss.ha.framework.interfaces.HASingletonLifecycle;
 import org.jboss.ha.framework.server.HAServiceEvent;
@@ -48,11 +50,13 @@
    
    private final HASingletonImpl<HAServiceEvent> customSingleton = new HASingletonImpl<HAServiceEvent>(new HAServiceEventFactory())
    {
+      @Override
       public void startSingleton()
       {
          HASingletonTestCase.this.lifecycle.startSingleton();
       }
       
+      @Override
       public void stopSingleton()
       {
          HASingletonTestCase.this.lifecycle.stopSingleton();
@@ -89,37 +93,86 @@
       return EasyMock.isA(HASingletonRpcHandler.class);
    }
    
+   @Override
+   public void testStart() throws Exception
+   {
+      Capture<DistributedReplicantManager.ReplicantListener> capturedListener1 = new Capture<DistributedReplicantManager.ReplicantListener>();
+      Capture<DistributedReplicantManager.ReplicantListener> capturedListener2 = new Capture<DistributedReplicantManager.ReplicantListener>();
+      
+      // Test isRegisterThreadContextClassLoader() = default value
+      this.partition.registerRPCHandler(EasyMock.same(SERVICE_HA_NAME), this.getRpcHandler());
+      
+      EasyMock.expect(this.partition.getDistributedReplicantManager()).andReturn(this.drm);
+      
+      this.drm.registerListener(EasyMock.same(SERVICE_HA_NAME), EasyMock.capture(capturedListener1));
+      this.drm.add(SERVICE_HA_NAME, "");
+      this.drm.registerListener(SERVICE_HA_NAME, this.singleton);
+      this.drm.unregisterListener(EasyMock.same(SERVICE_HA_NAME), EasyMock.capture(capturedListener2));
+      
+      EasyMock.replay(this.partition, this.drm);
+      
+      this.singleton.start();
+      
+      EasyMock.verify(this.partition, this.drm);
+      
+      assertSame(capturedListener1.getValue(), capturedListener2.getValue());
+      
+      EasyMock.reset(this.partition, this.drm);
+      
+      // Test isRegisterThreadContextClassLoader() = true
+      this.singleton.setRegisterThreadContextClassLoader(true);
+      
+      this.partition.registerRPCHandler(EasyMock.same(SERVICE_HA_NAME), this.getRpcHandler(), EasyMock.same(Thread.currentThread().getContextClassLoader()));
+      
+      EasyMock.expect(this.partition.getDistributedReplicantManager()).andReturn(this.drm);
+      
+      this.drm.registerListener(EasyMock.same(SERVICE_HA_NAME), EasyMock.capture(capturedListener1));
+      this.drm.add(SERVICE_HA_NAME, "");
+      this.drm.registerListener(SERVICE_HA_NAME, this.singleton);
+      this.drm.unregisterListener(EasyMock.same(SERVICE_HA_NAME), EasyMock.capture(capturedListener2));
+      
+      EasyMock.replay(this.partition, this.drm);
+      
+      this.singleton.start();
+      
+      EasyMock.verify(this.partition, this.drm);
+      
+      assertSame(capturedListener1.getValue(), capturedListener2.getValue());
+      
+      EasyMock.reset(this.partition, this.drm);
+   }
+   
    public void testRestartOnMerge()
    {
       boolean restart = this.singleton.getRestartOnMerge();
       
-      assert restart;
+      assertTrue(restart);
       
       this.singleton.setRestartOnMerge(false);
       
       restart = this.singleton.getRestartOnMerge();
-      
-      assert !restart;
+
+      assertFalse(restart);
    }
    
    public void testElectionPolicy()
    {
       HASingletonElectionPolicy policy = this.singleton.getElectionPolicy();
       
-      assert policy == null;
+      assertNull(policy);
       
       this.singleton.setElectionPolicy(this.electionPolicy);
       
       policy = this.singleton.getElectionPolicy();
-      
-      assert policy == this.electionPolicy;
+
+      assertSame(this.electionPolicy, policy);
    }
    
    public void testIsMaster()
    {
       boolean master = this.singleton.isMasterNode();
       
-      assert !master;
+      assertFalse(master);
    }
    
    public void testStopOldMaster() throws Exception
@@ -147,7 +200,7 @@
       
       EasyMock.verify(this.partition, this.drm, this.lifecycle);
       
-      assert !this.singleton.isMasterNode();
+      assertFalse(this.singleton.isMasterNode());
       
       EasyMock.reset(this.partition, this.drm, this.lifecycle);
       
@@ -163,7 +216,7 @@
       
       EasyMock.verify(this.partition, this.drm, this.lifecycle);
       
-      assert !this.singleton.isMasterNode();
+      assertFalse(this.singleton.isMasterNode());
       
       EasyMock.reset(this.partition, this.drm, this.lifecycle);
       
@@ -182,7 +235,7 @@
       
       EasyMock.verify(this.partition, this.drm, this.lifecycle);
       
-      assert this.singleton.isMasterNode();
+      assertTrue(this.singleton.isMasterNode());
       
       EasyMock.reset(this.partition, this.drm, this.lifecycle);
 
@@ -198,7 +251,7 @@
       
       EasyMock.verify(this.partition, this.drm, this.lifecycle);
       
-      assert this.singleton.isMasterNode();
+      assertTrue(this.singleton.isMasterNode());
       
       EasyMock.reset(this.partition, this.drm, this.lifecycle);
 
@@ -217,7 +270,7 @@
       
       EasyMock.verify(this.partition, this.drm, this.lifecycle);
       
-      assert this.singleton.isMasterNode();
+      assertTrue(this.singleton.isMasterNode());
       
       EasyMock.reset(this.partition, this.drm, this.lifecycle);
 
@@ -235,7 +288,7 @@
       
       EasyMock.verify(this.partition, this.drm, this.lifecycle);
       
-      assert this.singleton.isMasterNode();
+      assertTrue(this.singleton.isMasterNode());
       
       EasyMock.reset(this.partition, this.drm, this.lifecycle);
       
@@ -253,7 +306,7 @@
       
       EasyMock.verify(this.partition, this.drm, this.lifecycle);
       
-      assert !this.singleton.isMasterNode();
+      assertFalse(this.singleton.isMasterNode());
       
       EasyMock.reset(this.partition, this.drm, this.lifecycle);
       
@@ -274,7 +327,7 @@
       
       EasyMock.verify(this.partition, this.drm, this.lifecycle);
       
-      assert !this.singleton.isMasterNode();
+      assertFalse(this.singleton.isMasterNode());
       
       EasyMock.reset(this.partition, this.drm, this.lifecycle);
       
@@ -292,7 +345,7 @@
       
       EasyMock.verify(this.partition, this.drm, this.lifecycle);
       
-      assert !this.singleton.isMasterNode();
+      assertFalse(this.singleton.isMasterNode());
       
       EasyMock.reset(this.partition, this.drm, this.lifecycle);
       
@@ -314,7 +367,7 @@
       
       EasyMock.verify(this.partition, this.drm, this.lifecycle);
       
-      assert !this.singleton.isMasterNode();
+      assertFalse(this.singleton.isMasterNode());
       
       EasyMock.reset(this.partition, this.drm, this.lifecycle);
       
@@ -333,7 +386,7 @@
       
       EasyMock.verify(this.partition, this.drm, this.lifecycle, this.electionPolicy);
       
-      assert !this.singleton.isMasterNode();
+      assertFalse(this.singleton.isMasterNode());
       
       EasyMock.reset(this.partition, this.drm, this.lifecycle, this.electionPolicy);
       
@@ -354,7 +407,7 @@
       
       EasyMock.verify(this.partition, this.drm, this.lifecycle, this.electionPolicy);
       
-      assert this.singleton.isMasterNode();
+      assertTrue(this.singleton.isMasterNode());
       
       EasyMock.reset(this.partition, this.drm, this.lifecycle, this.electionPolicy);
 
@@ -374,7 +427,7 @@
       
       EasyMock.verify(this.partition, this.drm, this.lifecycle, this.electionPolicy);
       
-      assert this.singleton.isMasterNode();
+      assertTrue(this.singleton.isMasterNode());
       
       EasyMock.reset(this.partition, this.drm, this.lifecycle, this.electionPolicy);
       
@@ -393,7 +446,7 @@
       
       EasyMock.verify(this.partition, this.drm, this.lifecycle, this.electionPolicy);
       
-      assert this.singleton.isMasterNode();
+      assertTrue(this.singleton.isMasterNode());
       
       EasyMock.reset(this.partition, this.drm, this.lifecycle, this.electionPolicy);
       
@@ -415,7 +468,7 @@
       
       EasyMock.verify(this.partition, this.drm, this.lifecycle, this.electionPolicy);
       
-      assert this.singleton.isMasterNode();
+      assertTrue(this.singleton.isMasterNode());
       
       EasyMock.reset(this.partition, this.drm, this.lifecycle, this.electionPolicy);
 
@@ -427,7 +480,6 @@
       EasyMock.expect(this.electionPolicy.elect(nodes)).andReturn(ourNode);
       EasyMock.expect(this.partition.getClusterNode()).andReturn(ourNode);
 
-      System.out.println("restart on merge = " + this.singleton.getRestartOnMerge());
       this.lifecycle.stopSingleton();
       this.lifecycle.startSingleton();
       
@@ -437,7 +489,7 @@
       
       EasyMock.verify(this.partition, this.drm, this.lifecycle, this.electionPolicy);
       
-      assert this.singleton.isMasterNode();
+      assertTrue(this.singleton.isMasterNode());
       
       EasyMock.reset(this.partition, this.drm, this.lifecycle, this.electionPolicy);
 
@@ -457,7 +509,7 @@
       
       EasyMock.verify(this.partition, this.drm, this.lifecycle, this.electionPolicy);
       
-      assert !this.singleton.isMasterNode();
+      assertFalse(this.singleton.isMasterNode());
       
       EasyMock.reset(this.partition, this.drm, this.lifecycle, this.electionPolicy);
    }




More information about the jboss-cvs-commits mailing list