[Jboss-cvs] JBoss Messaging SVN: r1336 - in trunk: src/etc/server/default/deploy src/etc/xmdesc src/main/org/jboss/jms/server/destination src/main/org/jboss/jms/server/endpoint src/main/org/jboss/messaging/core src/main/org/jboss/messaging/core/plugin src/main/org/jboss/messaging/core/plugin/contract src/main/org/jboss/messaging/core/plugin/postoffice src/main/org/jboss/messaging/core/plugin/postoffice/cluster tests/src/org/jboss/test/messaging/core/plugin/postoffice/cluster

jboss-cvs-commits at lists.jboss.org jboss-cvs-commits at lists.jboss.org
Thu Sep 21 05:41:53 EDT 2006


Author: timfox
Date: 2006-09-21 05:41:24 -0400 (Thu, 21 Sep 2006)
New Revision: 1336

Added:
   trunk/src/main/org/jboss/messaging/core/plugin/postoffice/DefaultBinding.java
   trunk/src/main/org/jboss/messaging/core/plugin/postoffice/DefaultBindings.java
   trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/ClusterRouter.java
   trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/DefaultClusteredBindings.java
   trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/DefaultClusteredPostOffice.java
   trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/DefaultMessagePullPolicy.java
   trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/MessagePullPolicy.java
   trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/NullMessagePullPolicy.java
   trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/PullMessagesRequest.java
   trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/PullMessagesResponse.java
Removed:
   trunk/src/main/org/jboss/messaging/core/plugin/postoffice/BindingImpl.java
   trunk/src/main/org/jboss/messaging/core/plugin/postoffice/BindingsImpl.java
   trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/ClusteredBindingsImpl.java
   trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/ClusteredPostOfficeImpl.java
   trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/MessageRedistributor.java
   trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/MoveMessagesCallback.java
   trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/MoveTransactionRequest.java
   trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/RedistributionOrder.java
   trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/RedistributionPolicy.java
   trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/RouterFactory.java
   trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/StandardRedistributionPolicy.java
   trunk/tests/src/org/jboss/test/messaging/core/plugin/postoffice/cluster/NullRedistributionPolicy.java
Modified:
   trunk/src/etc/server/default/deploy/hsqldb-persistence-service.xml
   trunk/src/etc/xmdesc/ClusteredPostOffice-xmbean.xml
   trunk/src/main/org/jboss/jms/server/destination/QueueService.java
   trunk/src/main/org/jboss/jms/server/endpoint/ServerSessionEndpoint.java
   trunk/src/main/org/jboss/messaging/core/ChannelSupport.java
   trunk/src/main/org/jboss/messaging/core/plugin/ClusteredPostOfficeService.java
   trunk/src/main/org/jboss/messaging/core/plugin/contract/ClusteredPostOffice.java
   trunk/src/main/org/jboss/messaging/core/plugin/postoffice/DefaultPostOffice.java
   trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/BindRequest.java
   trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/CastMessagesCallback.java
   trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/CheckRequest.java
   trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/ClusterRequest.java
   trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/ClusterRouterFactory.java
   trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/ClusterTransaction.java
   trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/ClusteredBindings.java
   trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/FavourLocalRouter.java
   trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/FavourLocalRouterFactory.java
   trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/LocalClusteredQueue.java
   trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/MessageRequest.java
   trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/MessagesRequest.java
   trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/PostOfficeInternal.java
   trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/QueueStats.java
   trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/QueueStatsRequest.java
   trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/RemoteQueueStub.java
   trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/SendNodeIdRequest.java
   trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/SendTransactionRequest.java
   trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/TransactionRequest.java
   trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/UnbindRequest.java
   trunk/tests/src/org/jboss/test/messaging/core/plugin/postoffice/cluster/ClusteredPostOfficeTest.java
   trunk/tests/src/org/jboss/test/messaging/core/plugin/postoffice/cluster/FavourLocalRouterTest.java
Log:
More clustering work



Modified: trunk/src/etc/server/default/deploy/hsqldb-persistence-service.xml
===================================================================
--- trunk/src/etc/server/default/deploy/hsqldb-persistence-service.xml	2006-09-21 00:50:21 UTC (rev 1335)
+++ trunk/src/etc/server/default/deploy/hsqldb-persistence-service.xml	2006-09-21 09:41:24 UTC (rev 1336)
@@ -60,6 +60,8 @@
       <attribute name="StateTimeout">5000</attribute>
       <attribute name="CastTimeout">5000</attribute>
       <attribute name="RedistributionPeriod">5000</attribute>
+      <attribute name="SyncPullSize">1</attribute>
+      <attribute name="AsyncPullSize">50</attribute>
       <attribute name="SyncChannelConfig">
          <UDP mcast_addr="228.8.8.8" mcast_port="45568"
               ip_ttl="8" ip_mcast="true"

Modified: trunk/src/etc/xmdesc/ClusteredPostOffice-xmbean.xml
===================================================================
--- trunk/src/etc/xmdesc/ClusteredPostOffice-xmbean.xml	2006-09-21 00:50:21 UTC (rev 1335)
+++ trunk/src/etc/xmdesc/ClusteredPostOffice-xmbean.xml	2006-09-21 09:41:24 UTC (rev 1336)
@@ -63,6 +63,18 @@
       <description>The period between which successive message redistribution calculations will be performed</description>
       <name>RedistributionPeriod</name>
       <type>long</type>
+   </attribute>   
+   
+   <attribute access="read-write" getMethod="getSyncPullSize" setMethod="setSyncPullSize">
+      <description>The maximum number of message to pull synchronously in one go from a remote queue when the local queue consumers are starving</description>
+      <name>SyncPullSize</name>
+      <type>int</type>
+   </attribute> 
+   
+   <attribute access="read-write" getMethod="getAsyncPullSize" setMethod="setAsyncPullSize">
+      <description>The maximum number of message to pull aysynchronously in one go from a remote queue when the local queue consumers are starving</description>
+      <name>AsyncPullSize</name>
+      <type>int</type>
    </attribute>    
    
    <attribute access="read-write" getMethod="getSyncChannelConfig" setMethod="setSyncChannelConfig">

Modified: trunk/src/main/org/jboss/jms/server/destination/QueueService.java
===================================================================
--- trunk/src/main/org/jboss/jms/server/destination/QueueService.java	2006-09-21 00:50:21 UTC (rev 1335)
+++ trunk/src/main/org/jboss/jms/server/destination/QueueService.java	2006-09-21 09:41:24 UTC (rev 1336)
@@ -115,8 +115,8 @@
                postOffice.bindQueue(destination.getName(), queue);
             }
             else
-            {
-               queue = new LocalClusteredQueue(nodeId, destination.getName(), idm.getId(), ms, pm, true, true,                        
+            {               
+               queue = new LocalClusteredQueue(postOffice, nodeId, destination.getName(), idm.getId(), ms, pm, true, true,                        
                                                executor, null,
                                                destination.getFullSize(), destination.getPageSize(), destination.getDownCacheSize());
                

Modified: trunk/src/main/org/jboss/jms/server/endpoint/ServerSessionEndpoint.java
===================================================================
--- trunk/src/main/org/jboss/jms/server/endpoint/ServerSessionEndpoint.java	2006-09-21 00:50:21 UTC (rev 1335)
+++ trunk/src/main/org/jboss/jms/server/endpoint/ServerSessionEndpoint.java	2006-09-21 09:41:24 UTC (rev 1336)
@@ -243,7 +243,7 @@
                   }
                   else
                   {
-                     q = new LocalClusteredQueue(nodeId, name, idm.getId(), ms, pm, true, true,                              
+                     q = new LocalClusteredQueue(topicPostOffice, nodeId, name, idm.getId(), ms, pm, true, true,                              
                                                  executor, selector,
                                                  mDest.getFullSize(),
                                                  mDest.getPageSize(),
@@ -301,7 +301,7 @@
                      }
                      else
                      {
-                        q = new LocalClusteredQueue(nodeId, name, idm.getId(), ms, pm, true, true,                              
+                        q = new LocalClusteredQueue(topicPostOffice, nodeId, name, idm.getId(), ms, pm, true, true,                              
                                                     executor, selector,
                                                     mDest.getFullSize(),
                                                     mDest.getPageSize(),

Modified: trunk/src/main/org/jboss/messaging/core/ChannelSupport.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/ChannelSupport.java	2006-09-21 00:50:21 UTC (rev 1335)
+++ trunk/src/main/org/jboss/messaging/core/ChannelSupport.java	2006-09-21 09:41:24 UTC (rev 1336)
@@ -82,7 +82,7 @@
 
    protected QueuedExecutor executor;
 
-   protected boolean receiversReady;
+   protected volatile boolean receiversReady;
 
    protected PrioritizedDeque messageRefs;
 
@@ -187,7 +187,7 @@
          // acknowledge non transactionally
 
          // TODO We should consider also executing acks on the event queue
-         acknowledgeInternal(d);
+         acknowledgeInternal(d, true);
       }
       else
       {
@@ -219,6 +219,7 @@
       if (trace) { log.trace("receiver " + r + (added ? "" : " NOT") + " added"); }
       
       receiversReady = true;
+      
       return added;
    }
 
@@ -525,7 +526,7 @@
     * 
     * @see org.jboss.messaging.core.Channel#deliver()
     */
-   protected void deliverInternal()
+   protected void deliverInternal(boolean handle) throws Throwable
    {
       try
       {
@@ -577,7 +578,7 @@
 
                   Delivery delivery = new SimpleDelivery(this, ref, true);
 
-                  acknowledgeInternal(delivery);
+                  acknowledgeInternal(delivery, true);
                }
                else
                {
@@ -675,7 +676,7 @@
             }
             else
             {
-               // No more refs in channel
+               // No more refs in channel or only ones that don't match any selectors
                if (trace) { log.trace(this + " no more refs to deliver "); }
                break;
             }
@@ -745,7 +746,7 @@
             if (receiversReady)
             {
                // Prompt delivery
-               deliverInternal();
+               deliverInternal(true);
             }
          }
          else
@@ -791,14 +792,14 @@
       return new SimpleDelivery(this, ref, true);
    }
 
-   protected void acknowledgeInternal(Delivery d) throws Exception
+   protected void acknowledgeInternal(Delivery d, boolean persist) throws Exception
    {      
       synchronized (deliveryLock)
       {
          acknowledgeInMemory(d);
       }
          
-      if (recoverable && d.getReference().isReliable())
+      if (persist && recoverable && d.getReference().isReliable())
       {
          pm.removeReference(channelID, d.getReference(), null);
       }
@@ -910,7 +911,7 @@
             // prompt delivery
             if (receiversReady)
             {
-               deliverInternal();
+               deliverInternal(true);
             }
 
             result.setResult(null);
@@ -1079,14 +1080,24 @@
       
       public void run()
       {
-         receiversReady = true;
-         deliverInternal();
-         if (result != null)
+         try
          {
-            result.setResult(null);
+            receiversReady = true;
+            
+            deliverInternal(false);                  
+            
+            if (result != null)
+            {
+               result.setResult(null);
+            }
          }
+         catch (Throwable t)
+         {
+            log.error("Failed to deliver", t);
+            result.setException(t);
+         }
       }
-   }
+   }   
 
    protected class HandleRunnable implements Runnable
    {

Modified: trunk/src/main/org/jboss/messaging/core/plugin/ClusteredPostOfficeService.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/plugin/ClusteredPostOfficeService.java	2006-09-21 00:50:21 UTC (rev 1335)
+++ trunk/src/main/org/jboss/messaging/core/plugin/ClusteredPostOfficeService.java	2006-09-21 09:41:24 UTC (rev 1336)
@@ -33,10 +33,10 @@
 import org.jboss.messaging.core.plugin.contract.MessagingComponent;
 import org.jboss.messaging.core.plugin.contract.PersistenceManager;
 import org.jboss.messaging.core.plugin.postoffice.cluster.ClusterRouterFactory;
-import org.jboss.messaging.core.plugin.postoffice.cluster.ClusteredPostOfficeImpl;
+import org.jboss.messaging.core.plugin.postoffice.cluster.DefaultClusteredPostOffice;
 import org.jboss.messaging.core.plugin.postoffice.cluster.FavourLocalRouterFactory;
-import org.jboss.messaging.core.plugin.postoffice.cluster.RedistributionPolicy;
-import org.jboss.messaging.core.plugin.postoffice.cluster.StandardRedistributionPolicy;
+import org.jboss.messaging.core.plugin.postoffice.cluster.MessagePullPolicy;
+import org.jboss.messaging.core.plugin.postoffice.cluster.NullMessagePullPolicy;
 import org.jboss.messaging.core.tx.TransactionRepository;
 import org.w3c.dom.Element;
 
@@ -53,7 +53,7 @@
  */
 public class ClusteredPostOfficeService extends JDBCServiceSupport
 {
-   private ClusteredPostOfficeImpl postOffice;
+   private DefaultClusteredPostOffice postOffice;
    
    private ObjectName serverPeerObjectName;
    
@@ -68,11 +68,11 @@
    private long stateTimeout = 5000;
    
    private long castTimeout = 5000;
+     
+   private String groupName;
    
-   private long redistPeriod = 5000;
+   private int pullSize = 1;   
    
-   private String groupName;
-   
    // Constructors --------------------------------------------------------
    
    public ClusteredPostOfficeService()
@@ -158,24 +158,24 @@
       return castTimeout;
    }
    
-   public void setRedistributionPeriod(long period)
+   public void setGroupName(String groupName)
    {
-      this.redistPeriod = period;
+      this.groupName = groupName;
    }
    
-   public long getRedistributionPeriod()
+   public String getGroupName()
    {
-      return redistPeriod;
+      return groupName;
    }
    
-   public void setGroupName(String groupName)
+   public void setPullSize(int size)
    {
-      this.groupName = groupName;
+      this.pullSize = size;
    }
    
-   public String getGroupName()
+   public int getPullSize()
    {
-      return groupName;
+      return pullSize;
    }
    
    // ServiceMBeanSupport overrides ---------------------------------
@@ -205,19 +205,20 @@
                   
          String nodeId = serverPeer.getServerPeerID();
          
-         RedistributionPolicy redistPolicy = new StandardRedistributionPolicy(nodeId);
+         MessagePullPolicy pullPolicy = new NullMessagePullPolicy();
          
          FilterFactory ff = new SelectorFactory();
          
          ClusterRouterFactory rf = new FavourLocalRouterFactory();
                   
-         postOffice =  new ClusteredPostOfficeImpl(ds, tm, sqlProperties, createTablesOnStartup,
+         postOffice =  new DefaultClusteredPostOffice(ds, tm, sqlProperties, createTablesOnStartup,
                                                nodeId, officeName, ms,
                                                pm, tr, ff, pool, 
                                                groupName,
                                                syncChannelConfig, asyncChannelConfig,
                                                stateTimeout, castTimeout,
-                                               redistPolicy, redistPeriod, rf);
+                                               pullPolicy, rf,
+                                               pullSize);
          
          postOffice.start();
          

Modified: trunk/src/main/org/jboss/messaging/core/plugin/contract/ClusteredPostOffice.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/plugin/contract/ClusteredPostOffice.java	2006-09-21 00:50:21 UTC (rev 1335)
+++ trunk/src/main/org/jboss/messaging/core/plugin/contract/ClusteredPostOffice.java	2006-09-21 09:41:24 UTC (rev 1336)
@@ -52,6 +52,5 @@
     * @return
     * @throws Throwable
     */
-   Binding unbindClusteredQueue(String queueName) throws Throwable;
-  
+   Binding unbindClusteredQueue(String queueName) throws Throwable;   
 }

Deleted: trunk/src/main/org/jboss/messaging/core/plugin/postoffice/BindingImpl.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/plugin/postoffice/BindingImpl.java	2006-09-21 00:50:21 UTC (rev 1335)
+++ trunk/src/main/org/jboss/messaging/core/plugin/postoffice/BindingImpl.java	2006-09-21 09:41:24 UTC (rev 1336)
@@ -1,72 +0,0 @@
-/*
- * JBoss, Home of Professional Open Source
- * Copyright 2005, JBoss Inc., and individual contributors as indicated
- * by the @authors tag. See the copyright.txt in the distribution for a
- * full listing of individual contributors.
- *
- * This is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
- */
-package org.jboss.messaging.core.plugin.postoffice;
-
-import org.jboss.messaging.core.Queue;
-
-/**
- * 
- * A BindingImpl
- *
- * @author <a href="mailto:tim.fox at jboss.com">Tim Fox</a>
- * @version <tt>$Revision: 1.1 $</tt>
- *
- * $Id$
- *
- */
-public class BindingImpl implements Binding
-{
-   private String nodeId;
-   
-   private String condition;
-   
-   private Queue queue;
-       
-   public BindingImpl()
-   {      
-   }
-
-   public BindingImpl(String nodeId, String condition, Queue queue)
-   {
-      this.nodeId = nodeId;
-      
-      this.condition = condition;     
-      
-      this.queue = queue;
-   }
-   
-   public String getNodeId()
-   {
-      return nodeId;
-   }
-      
-   public String getCondition()
-   {
-      return condition;
-   }
-   
-   public Queue getQueue()
-   {
-      return queue;
-   }
-   
-}

Deleted: trunk/src/main/org/jboss/messaging/core/plugin/postoffice/BindingsImpl.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/plugin/postoffice/BindingsImpl.java	2006-09-21 00:50:21 UTC (rev 1335)
+++ trunk/src/main/org/jboss/messaging/core/plugin/postoffice/BindingsImpl.java	2006-09-21 09:41:24 UTC (rev 1336)
@@ -1,89 +0,0 @@
-/*
- * JBoss, Home of Professional Open Source
- * Copyright 2005, JBoss Inc., and individual contributors as indicated
- * by the @authors tag. See the copyright.txt in the distribution for a
- * full listing of individual contributors.
- *
- * This is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
- */
-package org.jboss.messaging.core.plugin.postoffice;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-/**
- * A BindingsImpl
- *
- * @author <a href="mailto:tim.fox at jboss.com">Tim Fox</a>
- * @version <tt>$Revision: 1.1 $</tt>
- *
- * $Id$
- *
- */
-public class BindingsImpl implements Bindings
-{
-   private List bindings;
-   
-   private int durableCount;
-   
-   public BindingsImpl()
-   {
-      bindings = new ArrayList();
-   }
-
-   public void addBinding(Binding binding)
-   {
-      if (bindings.contains(binding))
-      {
-         throw new IllegalArgumentException("Bindings already contains binding: " + binding);
-      }
-      bindings.add(binding);
-      
-      if (binding.getQueue().isRecoverable())
-      {
-         durableCount++;
-      }
-   }
-
-   public Collection getAllBindings()
-   {
-      return bindings;
-   }
-
-   public boolean removeBinding(Binding binding)
-   {
-      boolean removed = bindings.remove(binding);
-      
-      if (removed && binding.getQueue().isRecoverable())
-      {
-         durableCount--;
-      }
-      
-      return removed;
-   }
-   
-   public int getDurableCount()
-   {
-      return durableCount;
-   }
-   
-   public boolean isEmpty()
-   {
-      return bindings.isEmpty();
-   }
-
-}

Copied: trunk/src/main/org/jboss/messaging/core/plugin/postoffice/DefaultBinding.java (from rev 1321, trunk/src/main/org/jboss/messaging/core/plugin/postoffice/BindingImpl.java)
===================================================================
--- trunk/src/main/org/jboss/messaging/core/plugin/postoffice/BindingImpl.java	2006-09-19 19:17:09 UTC (rev 1321)
+++ trunk/src/main/org/jboss/messaging/core/plugin/postoffice/DefaultBinding.java	2006-09-21 09:41:24 UTC (rev 1336)
@@ -0,0 +1,72 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.messaging.core.plugin.postoffice;
+
+import org.jboss.messaging.core.Queue;
+
+/**
+ * 
+ * A DefaultBinding
+ *
+ * @author <a href="mailto:tim.fox at jboss.com">Tim Fox</a>
+ * @version <tt>$Revision: 1.1 $</tt>
+ *
+ * $Id$
+ *
+ */
+public class DefaultBinding implements Binding
+{
+   private String nodeId;
+   
+   private String condition;
+   
+   private Queue queue;
+       
+   public DefaultBinding()
+   {      
+   }
+
+   public DefaultBinding(String nodeId, String condition, Queue queue)
+   {
+      this.nodeId = nodeId;
+      
+      this.condition = condition;     
+      
+      this.queue = queue;
+   }
+   
+   public String getNodeId()
+   {
+      return nodeId;
+   }
+      
+   public String getCondition()
+   {
+      return condition;
+   }
+   
+   public Queue getQueue()
+   {
+      return queue;
+   }
+   
+}

Copied: trunk/src/main/org/jboss/messaging/core/plugin/postoffice/DefaultBindings.java (from rev 1298, trunk/src/main/org/jboss/messaging/core/plugin/postoffice/BindingsImpl.java)
===================================================================
--- trunk/src/main/org/jboss/messaging/core/plugin/postoffice/BindingsImpl.java	2006-09-17 17:58:08 UTC (rev 1298)
+++ trunk/src/main/org/jboss/messaging/core/plugin/postoffice/DefaultBindings.java	2006-09-21 09:41:24 UTC (rev 1336)
@@ -0,0 +1,90 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.messaging.core.plugin.postoffice;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 
+ * A DefaultBindings
+ *
+ * @author <a href="mailto:tim.fox at jboss.com">Tim Fox</a>
+ * @version <tt>$Revision: 1.1 $</tt>
+ *
+ * $Id$
+ *
+ */
+public class DefaultBindings implements Bindings
+{
+   private List bindings;
+   
+   private int durableCount;
+   
+   public DefaultBindings()
+   {
+      bindings = new ArrayList();
+   }
+
+   public void addBinding(Binding binding)
+   {
+      if (bindings.contains(binding))
+      {
+         throw new IllegalArgumentException("Bindings already contains binding: " + binding);
+      }
+      bindings.add(binding);
+      
+      if (binding.getQueue().isRecoverable())
+      {
+         durableCount++;
+      }
+   }
+
+   public Collection getAllBindings()
+   {
+      return bindings;
+   }
+
+   public boolean removeBinding(Binding binding)
+   {
+      boolean removed = bindings.remove(binding);
+      
+      if (removed && binding.getQueue().isRecoverable())
+      {
+         durableCount--;
+      }
+      
+      return removed;
+   }
+   
+   public int getDurableCount()
+   {
+      return durableCount;
+   }
+   
+   public boolean isEmpty()
+   {
+      return bindings.isEmpty();
+   }
+
+}

Modified: trunk/src/main/org/jboss/messaging/core/plugin/postoffice/DefaultPostOffice.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/plugin/postoffice/DefaultPostOffice.java	2006-09-21 00:50:21 UTC (rev 1335)
+++ trunk/src/main/org/jboss/messaging/core/plugin/postoffice/DefaultPostOffice.java	2006-09-21 09:41:24 UTC (rev 1336)
@@ -43,6 +43,7 @@
 import org.jboss.messaging.core.FilterFactory;
 import org.jboss.messaging.core.MessageReference;
 import org.jboss.messaging.core.Queue;
+import org.jboss.messaging.core.local.PagingFilteredQueue;
 import org.jboss.messaging.core.plugin.JDBCSupport;
 import org.jboss.messaging.core.plugin.contract.MessageStore;
 import org.jboss.messaging.core.plugin.contract.PersistenceManager;
@@ -172,7 +173,7 @@
             throw new IllegalArgumentException("Binding already exists for name " + queue.getName());
          }
                  
-         binding = new BindingImpl(this.nodeId, condition, queue); 
+         binding = new DefaultBinding(this.nodeId, condition, queue); 
          
          addBinding(binding);
                
@@ -440,7 +441,7 @@
       {
          QueuedExecutor executor = (QueuedExecutor)pool.get();
          
-         queue = new LocalClusteredQueue(nodeId, queueName, channelId, ms, pm, true,
+         queue = new PagingFilteredQueue(queueName, channelId, ms, pm, true,
                                          true, executor, filter);
       }
       else
@@ -448,7 +449,7 @@
          throw new IllegalStateException("This is a non clustered post office - should not have bindings from different nodes!");
       }
       
-      Binding binding = new BindingImpl(nodeId, condition, queue);
+      Binding binding = new DefaultBinding(nodeId, condition, queue);
       
       return binding;
    }
@@ -533,6 +534,22 @@
 
    protected void addBinding(Binding binding)
    {
+      addToNameMap(binding);
+      
+      addToConditionMap(binding);
+   }   
+   
+   protected Binding removeBinding(String nodeId, String queueName)
+   {
+      Binding binding = removeFromNameMap(nodeId, queueName);
+                  
+      removeFromConditionMap(binding);
+      
+      return binding;
+   }
+   
+   protected void addToNameMap(Binding binding)
+   {
       Map nameMap = (Map)nameMaps.get(binding.getNodeId());
       
       if (nameMap == null)
@@ -543,22 +560,25 @@
       }
       
       nameMap.put(binding.getQueue().getName(), binding);
+   }
+   
+   protected void addToConditionMap(Binding binding)
+   {
+      String condition = binding.getCondition();
       
-      String condition = binding.getCondition();
-            
       Bindings bindings = (Bindings)conditionMap.get(condition);
       
       if (bindings == null)
       {
-         bindings = createBindings();
+         bindings = new DefaultBindings();
          
          conditionMap.put(condition, bindings);
       }
       
       bindings.addBinding(binding);
-   }   
+   }
    
-   protected Binding removeBinding(String nodeId, String queueName)
+   protected Binding removeFromNameMap(String nodeId, String queueName)
    {
       if (queueName == null)
       {
@@ -588,7 +608,12 @@
       {
          nameMaps.remove(nodeId);
       }
-                  
+      
+      return binding;
+   }
+   
+   protected void removeFromConditionMap(Binding binding)
+   {
       Bindings bindings = (Bindings)conditionMap.get(binding.getCondition());
       
       if (bindings == null)
@@ -607,15 +632,8 @@
       {
          conditionMap.remove(binding.getCondition());
       }        
-      
-      return binding;
-   }
+   }         
    
-   protected Bindings createBindings()
-   {
-      return new BindingsImpl();
-   }
-   
    protected Map getDefaultDMLStatements()
    {                
       Map map = new HashMap();

Modified: trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/BindRequest.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/BindRequest.java	2006-09-21 00:50:21 UTC (rev 1335)
+++ trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/BindRequest.java	2006-09-21 09:41:24 UTC (rev 1336)
@@ -50,14 +50,14 @@
                                     channelId, durable);
    }
 
-   public void execute(PostOfficeInternal office) throws Exception
+   Object execute(PostOfficeInternal office) throws Exception
    {
       office.addBindingFromCluster(bindingInfo.getNodeId(), bindingInfo.getQueueName(), bindingInfo.getCondition(),
                                    bindingInfo.getFilterString(), bindingInfo.getChannelId(), bindingInfo.isDurable());
-      
+      return null;
    }
    
-   public byte getType()
+   byte getType()
    {
       return TYPE;
    }

Modified: trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/CastMessagesCallback.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/CastMessagesCallback.java	2006-09-21 00:50:21 UTC (rev 1335)
+++ trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/CastMessagesCallback.java	2006-09-21 09:41:24 UTC (rev 1336)
@@ -76,8 +76,16 @@
    private boolean multicast;
    
    private String toNodeId;
+      
+   /*
+    * We store the id of one of the channels that the ref was inserted into
+    * this is used after node failure to determine whether the tx has to be committed
+    * or rolled back on the remote node
+    */
+   private long checkChannelID;
    
-   void addMessage(String routingKey, Message message, Map queueNameToNodeIdMap, String lastNodeId)
+   void addMessage(String routingKey, Message message, Map queueNameToNodeIdMap,
+                   String lastNodeId, long channelID)
    {
       //If we only ever send messages to the same node for this tx, then we can unicast rather than multicast
       //This is how we determine that
@@ -106,6 +114,8 @@
             persistent = new ArrayList();
          }
          persistent.add(holder);
+         
+         checkChannelID = channelID;
       }
       else
       {
@@ -161,7 +171,7 @@
       {
          //We send the persistent messages which go into the "holding area" on
          //the receiving nodes
-         ClusterRequest req = new SendTransactionRequest(nodeId, txId, persistent);
+         ClusterRequest req = new SendTransactionRequest(nodeId, txId, persistent, checkChannelID);
          
          sendRequest(req);
       }

Modified: trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/CheckRequest.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/CheckRequest.java	2006-09-21 00:50:21 UTC (rev 1335)
+++ trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/CheckRequest.java	2006-09-21 09:41:24 UTC (rev 1336)
@@ -50,12 +50,13 @@
       this.nodeId = nodeId;
    }
    
-   public void execute(PostOfficeInternal office) throws Exception
+   Object execute(PostOfficeInternal office) throws Throwable
    {
       office.check(nodeId);
+      return null;
    }
    
-   public byte getType()
+   byte getType()
    {
       return TYPE;
    }

Modified: trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/ClusterRequest.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/ClusterRequest.java	2006-09-21 00:50:21 UTC (rev 1335)
+++ trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/ClusterRequest.java	2006-09-21 09:41:24 UTC (rev 1336)
@@ -69,9 +69,9 @@
             request = new MessagesRequest();
             break;
          }
-         case MoveTransactionRequest.TYPE:
+         case PullMessagesRequest.TYPE:
          {
-            request = new MoveTransactionRequest();
+            request = new PullMessagesRequest();
             break;
          }
          case QueueStatsRequest.TYPE:
@@ -112,7 +112,7 @@
       request.write(daos);
    }
    
-   abstract void execute(PostOfficeInternal office) throws Exception;
+   abstract Object execute(PostOfficeInternal office) throws Throwable;
    
    abstract byte getType();
 }

Added: trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/ClusterRouter.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/ClusterRouter.java	2006-09-21 00:50:21 UTC (rev 1335)
+++ trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/ClusterRouter.java	2006-09-21 09:41:24 UTC (rev 1336)
@@ -0,0 +1,42 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.messaging.core.plugin.postoffice.cluster;
+
+import java.util.List;
+
+import org.jboss.messaging.core.Router;
+
+/**
+ * A ClusterRouter
+ *
+ * @author <a href="mailto:tim.fox at jboss.com">Tim Fox</a>
+ * @version <tt>$Revision: 1.1 $</tt>
+ *
+ * $Id$
+ *
+ */
+public interface ClusterRouter extends Router
+{
+   List getQueues();
+   
+   LocalClusteredQueue getLocalQueue();
+}

Modified: trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/ClusterRouterFactory.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/ClusterRouterFactory.java	2006-09-21 00:50:21 UTC (rev 1335)
+++ trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/ClusterRouterFactory.java	2006-09-21 09:41:24 UTC (rev 1336)
@@ -21,7 +21,6 @@
  */
 package org.jboss.messaging.core.plugin.postoffice.cluster;
 
-import org.jboss.messaging.core.Router;
 
 
 /**
@@ -35,5 +34,5 @@
  */
 public interface ClusterRouterFactory
 {
-   Router createRouter();
+   ClusterRouter createRouter();
 }

Modified: trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/ClusterTransaction.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/ClusterTransaction.java	2006-09-21 00:50:21 UTC (rev 1335)
+++ trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/ClusterTransaction.java	2006-09-21 09:41:24 UTC (rev 1336)
@@ -32,5 +32,9 @@
  */
 interface ClusterTransaction
 {
-   void commit(PostOfficeInternal office) throws Exception;
+   void commit(PostOfficeInternal office) throws Throwable;
+   
+   void rollback(PostOfficeInternal office) throws Throwable;
+   
+   boolean check(PostOfficeInternal office) throws Exception;
 }

Modified: trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/ClusteredBindings.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/ClusteredBindings.java	2006-09-21 00:50:21 UTC (rev 1335)
+++ trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/ClusteredBindings.java	2006-09-21 09:41:24 UTC (rev 1336)
@@ -39,4 +39,8 @@
    Collection getRouters();
    
    int getLocalDurableCount();
+   
+   void addRouter(String queueName, ClusterRouter router);
+
+   void removeRouter(String queueName);
 }

Deleted: trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/ClusteredBindingsImpl.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/ClusteredBindingsImpl.java	2006-09-21 00:50:21 UTC (rev 1335)
+++ trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/ClusteredBindingsImpl.java	2006-09-21 09:41:24 UTC (rev 1336)
@@ -1,131 +0,0 @@
-/*
- * JBoss, Home of Professional Open Source
- * Copyright 2005, JBoss Inc., and individual contributors as indicated
- * by the @authors tag. See the copyright.txt in the distribution for a
- * full listing of individual contributors.
- *
- * This is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
- */
-package org.jboss.messaging.core.plugin.postoffice.cluster;
-
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.jboss.messaging.core.Router;
-import org.jboss.messaging.core.plugin.postoffice.Binding;
-import org.jboss.messaging.core.plugin.postoffice.BindingsImpl;
-
-
-/**
- * 
- * A ClusteredBindings
- *
- * @author <a href="mailto:tim.fox at jboss.com">Tim Fox</a>
- * @version <tt>$Revision: 1.1 $</tt>
- *
- * $Id$
- *
- */
-class ClusteredBindingsImpl extends BindingsImpl implements ClusteredBindings
-{
-   // Map <name, router>
-   private Map nameMap;
-   
-   private String thisNode;
-   
-   private int localDurableCount;
-   
-   private ClusterRouterFactory rf;
-   
-   ClusteredBindingsImpl(String thisNode, ClusterRouterFactory rf)
-   {
-      super();
-      
-      nameMap = new HashMap();
-      
-      this.thisNode = thisNode;
-      
-      this.rf = rf;
-   }
-   
-   public void addBinding(Binding binding)
-   {
-      super.addBinding(binding);
-               
-      Router router = (Router)nameMap.get(binding.getQueue().getName());
-      
-      if (router == null)
-      {
-         router = rf.createRouter();
-         
-         nameMap.put(binding.getQueue().getName(), router);
-      }
-      
-      router.add(binding.getQueue());      
-      
-      if (binding.getNodeId().equals(thisNode) && binding.getQueue().isRecoverable())
-      {
-         localDurableCount++;
-      }      
-   }
-   
-   public boolean removeBinding(Binding binding)
-   {
-      boolean removed = super.removeBinding(binding);
-      
-      if (!removed)
-      {
-         return false;
-      }
-           
-      Router router = (Router)nameMap.get(binding.getQueue().getName());
-      
-      if (router == null)
-      {
-         throw new IllegalStateException("Cannot find router in name map");
-      }
-      
-      removed = router.remove(binding.getQueue());
-      
-      if (!removed)
-      {
-         throw new IllegalStateException("Cannot find binding in list");
-      }
-      
-      if (!router.iterator().hasNext())
-      {
-         nameMap.remove(binding.getQueue().getName());
-      }
-      
-      if (binding.getNodeId().equals(thisNode) && binding.getQueue().isRecoverable())
-      {
-         localDurableCount--;
-      }      
-
-      return true;
-   }
-   
-   public int getLocalDurableCount()
-   {
-      return localDurableCount;
-   }
-   
-   public Collection getRouters()
-   {
-      return nameMap.values();
-   }
-}

Deleted: trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/ClusteredPostOfficeImpl.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/ClusteredPostOfficeImpl.java	2006-09-21 00:50:21 UTC (rev 1335)
+++ trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/ClusteredPostOfficeImpl.java	2006-09-21 09:41:24 UTC (rev 1336)
@@ -1,1447 +0,0 @@
-/*
- * JBoss, Home of Professional Open Source
- * Copyright 2005, JBoss Inc., and individual contributors as indicated
- * by the @authors tag. See the copyright.txt in the distribution for a
- * full listing of individual contributors.
- *
- * This is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
- */
-package org.jboss.messaging.core.plugin.postoffice.cluster;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Properties;
-
-import javax.sql.DataSource;
-import javax.transaction.TransactionManager;
-
-import org.jboss.jms.server.QueuedExecutorPool;
-import org.jboss.logging.Logger;
-import org.jboss.messaging.core.Delivery;
-import org.jboss.messaging.core.Filter;
-import org.jboss.messaging.core.FilterFactory;
-import org.jboss.messaging.core.MessageReference;
-import org.jboss.messaging.core.Queue;
-import org.jboss.messaging.core.Router;
-import org.jboss.messaging.core.plugin.contract.ClusteredPostOffice;
-import org.jboss.messaging.core.plugin.contract.MessageStore;
-import org.jboss.messaging.core.plugin.contract.PersistenceManager;
-import org.jboss.messaging.core.plugin.postoffice.Binding;
-import org.jboss.messaging.core.plugin.postoffice.BindingImpl;
-import org.jboss.messaging.core.plugin.postoffice.Bindings;
-import org.jboss.messaging.core.plugin.postoffice.DefaultPostOffice;
-import org.jboss.messaging.core.tx.Transaction;
-import org.jboss.messaging.core.tx.TransactionRepository;
-import org.jboss.messaging.util.StreamUtils;
-import org.jgroups.Address;
-import org.jgroups.Channel;
-import org.jgroups.JChannel;
-import org.jgroups.MembershipListener;
-import org.jgroups.Message;
-import org.jgroups.MessageListener;
-import org.jgroups.Receiver;
-import org.jgroups.View;
-import org.jgroups.blocks.GroupRequest;
-import org.jgroups.blocks.MessageDispatcher;
-import org.jgroups.blocks.RequestHandler;
-import org.w3c.dom.Element;
-
-import EDU.oswego.cs.dl.util.concurrent.QueuedExecutor;
-
-/**
- * 
- * A ClusteredPostOfficeImpl
- *
- * @author <a href="mailto:tim.fox at jboss.com">Tim Fox</a>
- * @version <tt>$Revision: 1.1 $</tt>
- *
- * $Id$
- *
- */
-public class ClusteredPostOfficeImpl extends DefaultPostOffice implements ClusteredPostOffice, PostOfficeInternal
-{
-   private static final Logger log = Logger.getLogger(ClusteredPostOfficeImpl.class);
-                        
-   private Channel syncChannel;
-   
-   private Channel asyncChannel;
-   
-   private String groupName;
-   
-   private MessageDispatcher controlMessageDispatcher;
-   
-   private MessageListener controlMessageListener;
-   
-   private Receiver dataReceiver;
-   
-   private MembershipListener controlMembershipListener;
-   
-   private RequestHandler requestHandler;
-   
-   private Object setStateLock = new Object();
-   
-   private boolean stateSet;
-   
-   private View currentView;
-   
-   //Map < Address, node id>
-   private Map nodeIdAddressMap;
-   
-   private Map holdingArea;
-   
-   private Element syncChannelConfigE;
-   
-   private Element asyncChannelConfigE;
-   
-   private String syncChannelConfigS;
-   
-   private String asyncChannelConfigS;
-   
-   private long stateTimeout;
-   
-   private long castTimeout;
-   
-   private RedistributionPolicy redistributionPolicy;
-   
-   private MessageRedistributor redistributor;
-   
-   private long redistributePeriod;
-   
-   private ClusterRouterFactory routerFactory;
-      
-   public ClusteredPostOfficeImpl()
-   {        
-      init();
-   }
-   
-   private void init()
-   {
-      this.nodeIdAddressMap = new HashMap();
-      
-      this.holdingArea = new HashMap();
-   }
-   
-   /*
-    * Constructor using Element for configuration
-    */
-   public ClusteredPostOfficeImpl(DataSource ds, TransactionManager tm, Properties sqlProperties,
-            boolean createTablesOnStartup,
-            String nodeId, String officeName, MessageStore ms,
-            PersistenceManager pm,
-            TransactionRepository tr,
-            FilterFactory filterFactory,
-            QueuedExecutorPool pool,                              
-            String groupName,
-            Element syncChannelConfig,
-            Element asyncChannelConfig,
-            long stateTimeout, long castTimeout,
-            RedistributionPolicy redistributionPolicy,
-            long redistributePeriod,
-            ClusterRouterFactory rf) throws Exception
-   {            
-      this(ds, tm, sqlProperties, createTablesOnStartup, nodeId, officeName, ms,
-           pm, tr, filterFactory, pool, groupName, stateTimeout, castTimeout, redistributionPolicy, redistributePeriod, rf);
-      
-      this.syncChannelConfigE = syncChannelConfig;      
-      this.asyncChannelConfigE = asyncChannelConfig;     
-   }
-     
-   /*
-    * Constructor using String for configuration
-    */
-   public ClusteredPostOfficeImpl(DataSource ds, TransactionManager tm, Properties sqlProperties,
-                              boolean createTablesOnStartup,
-                              String nodeId, String officeName, MessageStore ms,
-                              PersistenceManager pm,
-                              TransactionRepository tr,
-                              FilterFactory filterFactory,
-                              QueuedExecutorPool pool,                              
-                              String groupName,
-                              String syncChannelConfig,
-                              String asyncChannelConfig,
-                              long stateTimeout, long castTimeout,
-                              RedistributionPolicy redistributionPolicy,
-                              long redistributePeriod,
-                              ClusterRouterFactory rf) throws Exception
-   {            
-      this(ds, tm, sqlProperties, createTablesOnStartup, nodeId, officeName, ms,
-           pm, tr, filterFactory, pool, groupName, stateTimeout, castTimeout, redistributionPolicy, redistributePeriod, rf);
-
-      this.syncChannelConfigS = syncChannelConfig;      
-      this.asyncChannelConfigS = asyncChannelConfig;     
-   }
-   
-   private ClusteredPostOfficeImpl(DataSource ds, TransactionManager tm, Properties sqlProperties,
-                               boolean createTablesOnStartup,
-                               String nodeId, String officeName, MessageStore ms,
-                               PersistenceManager pm,                               
-                               TransactionRepository tr,
-                               FilterFactory filterFactory,
-                               QueuedExecutorPool pool,
-                               String groupName,
-                               long stateTimeout, long castTimeout,                             
-                               RedistributionPolicy redistributionPolicy,
-                               long redistributePeriod,
-                               ClusterRouterFactory rf)
-   {
-      super (ds, tm, sqlProperties, createTablesOnStartup, nodeId, officeName, ms, pm, tr, filterFactory,
-             pool);
-             
-      this.pm = pm;
-      
-      this.groupName = groupName;
-      
-      this.stateTimeout = stateTimeout;
-      
-      this.castTimeout = castTimeout;
-      
-      this.redistributionPolicy = redistributionPolicy;
-      
-      this.redistributePeriod = redistributePeriod;
-      
-      this.routerFactory = rf;
-      
-      init();
-   }
-
-   // MessagingComponent overrides
-   // --------------------------------------------------------------
-   
-   public void start() throws Exception
-   {
-      if (syncChannelConfigE != null)
-      {        
-         this.syncChannel = new JChannel(syncChannelConfigE);
-         this.asyncChannel = new JChannel(asyncChannelConfigE); 
-      }
-      else
-      {
-         this.syncChannel = new JChannel(syncChannelConfigS);
-         this.asyncChannel = new JChannel(asyncChannelConfigS); 
-      }
-      
-      //We don't want to receive local messages on any of the channels
-      syncChannel.setOpt(Channel.LOCAL, Boolean.FALSE);
-      
-      asyncChannel.setOpt(Channel.LOCAL, Boolean.FALSE);
-      
-      this.controlMessageListener = new ControlMessageListener();
-      
-      this.requestHandler = new PostOfficeRequestHandler();
-      
-      this.controlMembershipListener = new ControlMembershipListener();
-      
-      this.controlMessageDispatcher = new MessageDispatcher(syncChannel, controlMessageListener,
-                                                            controlMembershipListener, requestHandler, true);      
-      this.dataReceiver = new DataReceiver();
-      
-      asyncChannel.setReceiver(dataReceiver);    
-             
-      syncChannel.connect(groupName);
-      
-      asyncChannel.connect(groupName);
-      
-      super.start();
-      
-      Address currentAddress = syncChannel.getLocalAddress();
-      
-      log.info(this.nodeId + " address is " + currentAddress);
-             
-      handleAddressNodeMapping(currentAddress, nodeId);
-      
-      syncSendRequest(new SendNodeIdRequest(currentAddress, nodeId));
-            
-      redistributor = new MessageRedistributor(this, redistributePeriod);
-      
-      redistributor.start();
-   }
-
-   public void stop() throws Exception
-   {
-      super.stop();
-      
-      redistributor.stop();
-      
-      syncChannel.close();
-      
-      asyncChannel.close();
-   }  
-   
-   // PostOffice implementation ---------------------------------------        
-      
-   public Binding bindClusteredQueue(String condition, LocalClusteredQueue queue) throws Exception
-   {           
-      if (!queue.getNodeId().equals(this.nodeId))
-      {
-         throw new IllegalArgumentException("Queue node id does not match office node id");
-      }
-      
-      Binding binding = (Binding)super.bindQueue(condition, queue);
-      
-      BindRequest request =
-         new BindRequest(nodeId, queue.getName(), condition, queue.getFilter() == null ? null : queue.getFilter().getFilterString(),
-                         binding.getQueue().getChannelID(), queue.isRecoverable());
-      
-      syncSendRequest(request);
-      
-      return binding;
-   }
-   
-   public Binding unbindClusteredQueue(String queueName) throws Throwable
-   {
-      Binding binding = (Binding)super.unbindQueue(queueName);
-      
-      UnbindRequest request = new UnbindRequest(nodeId, queueName);
-      
-      syncSendRequest(request);
-      
-      return binding;
-   }
-   
-   /*
-    * This is called by the server peer if it determines that the server crashed last time it was run
-    */
-   public void recover() throws Exception
-   {
-      //We send a "check" message to all nodes of the cluster
-      asyncSendRequest(new CheckRequest(nodeId));
-   }
-   
-   public boolean route(MessageReference ref, String condition, Transaction tx) throws Exception
-   {
-      if (ref == null)
-      {
-         throw new IllegalArgumentException("Message reference is null");
-      }
-      
-      if (condition == null)
-      {
-         throw new IllegalArgumentException("Condition is null");
-      }
-      
-      boolean routed = false;
-      
-      lock.readLock().acquire();
-      
-      try
-      {      
-         ClusteredBindings cb = (ClusteredBindings)conditionMap.get(condition);
-         
-         boolean startInternalTx = false;
-         
-         String lastNodeId = null;
-         
-         if (cb != null)
-         {
-            if (tx == null && ref.isReliable())
-            {                
-               if (!(cb.getDurableCount() == 0 || (cb.getDurableCount() == 1 && cb.getLocalDurableCount() == 1)))
-               {
-                  // When routing a persistent message without a transaction then we may need to start an 
-                  // internal transaction in order to route it.
-                  // This is so we can guarantee the message is delivered to all or none of the subscriptions.
-                  // We need to do this if there is anything other than
-                  // No durable subs or exactly one local durable sub
-                  startInternalTx = true;
-               }
-            }                        
-            
-            if (startInternalTx)
-            {
-               tx = tr.createTransaction();
-            }
-                
-            int numberRemote = 0;
-            
-            Map queueNameNodeIdMap = null;
-            
-            Collection routers = cb.getRouters();
-
-            Iterator iter = routers.iterator();
-                     
-            while (iter.hasNext())
-            {
-               Router router = (Router)iter.next();
-               
-               Delivery del = router.handle(null, ref, tx);
-               
-               if (del != null && del.isSelectorAccepted())
-               {
-                  routed = true;
-               }
-                              
-               ClusteredQueue queue = (ClusteredQueue)del.getObserver();
-               
-               log.info("sent " + ref.getMessageID() + " to " + queue.getName() + " on node " + queue.getNodeId() + " selector accepted " + del.isSelectorAccepted());
-               
-               if (del.isSelectorAccepted() && !queue.isLocal())
-               {
-                  //We need to send the message remotely
-                  numberRemote++;
-                  
-                  lastNodeId = queue.getNodeId();
-                  
-                  if (router.numberOfReceivers() > 1 && queueNameNodeIdMap == null)
-                  {
-                     //If there are more than one queues with the same node on the remote nodes
-                     //We have now chosen which one will receive the message so we need to add this
-                     //information to a map which will get sent when casting - so the the queue
-                     //on the receiving node knows whether to receive the message
-                     queueNameNodeIdMap = new HashMap();
-                     
-                     //We add an entry to the map so that on the receiving node we can work out which
-                     //queue instance will receive the message
-                     queueNameNodeIdMap.put(queue.getName(), lastNodeId);
-                  }
-               }
-            }
-            
-            //Now we've sent the message to any local queues, we might also need
-            //to send the message to the other office instances on the cluster if there are
-            //queues on those nodes that need to receive the message
-            
-            //FIXME - there is a bug here, numberRemote does not take into account that more than one
-            //of the number remote may be on the same node, so we could end up multicasting
-            //when unicast would do
-            if (numberRemote > 0)
-            {
-               log.info("Need to send remotely");
- 
-               if (tx == null)
-               {
-                  if (numberRemote == 1)
-                  {
-                     log.info("unicast no tx");
-                     //Unicast - only one node is interested in the message
-                     
-                     //FIXME - temporarily commented out until can get unicast to work
-                     //asyncSendRequest(new MessageRequest(condition, ref.getMessage(), null), lastNodeId);
-                     asyncSendRequest(new MessageRequest(condition, ref.getMessage(), null));
-                  }
-                  else
-                  {
-                     log.info("multicast no tx");
-                     //Multicast - more than one node is interested
-                     asyncSendRequest(new MessageRequest(condition, ref.getMessage(), queueNameNodeIdMap));
-                  }                                 
-               }
-               else
-               {
-                  CastMessagesCallback callback = (CastMessagesCallback)tx.getCallback(this);
-                  
-                  if (callback == null)
-                  {
-                     callback = new CastMessagesCallback(nodeId, tx.getId(), ClusteredPostOfficeImpl.this);
-                     
-                     //This callback MUST be executed first
-                     
-                     //Execution order is as follows:
-                     //Before commit:
-                     //1. Cast messages across network - get added to holding area (if persistent) on receiving
-                     //nodes
-                     //2. Persist messages in persistent store
-                     //After commit
-                     //1. Cast commit message across network
-                     tx.addFirstCallback(callback, this);
-                  }
-                      
-                  callback.addMessage(condition, ref.getMessage(), queueNameNodeIdMap, numberRemote == 1 ? lastNodeId : null);    
-               }
-            }
-                                                
-            if (startInternalTx)
-            {               
-               tx.commit();
-            }
-         }
-      }
-      finally
-      {                  
-         lock.readLock().release();
-      }
-         
-      return routed; 
-   }
-   
-   public boolean isLocal()
-   {
-      return false;
-   }
-   
-   // PostOfficeInternal implementation ------------------------------------------------------------------
-   
-   /*
-    * Called when another node adds a binding
-    */
-   public void addBindingFromCluster(String nodeId, String queueName, String condition,
-                                     String filterString, long channelID, boolean durable)
-      throws Exception
-   {
-      lock.writeLock().acquire();
-      
-      try
-      {                     
-         //Sanity
-
-         if (!nodeIdAddressMap.containsKey(nodeId))
-         {
-            throw new IllegalStateException("Cannot find address for node: " + nodeId);
-         }
-         
-         // We currently only allow one binding per name per node
-         Map nameMap = (Map)nameMaps.get(nodeId);
-         
-         Binding binding = null;
-         
-         if (nameMap != null)
-         {
-            binding = (Binding)nameMap.get(queueName);
-         }
-         
-         if (binding != null)
-         {
-            throw new IllegalArgumentException(this.nodeId + "Binding already exists for node Id " + nodeId + " queue name " + queueName);
-         }
-            
-         binding = this.createBinding(nodeId, condition, queueName, channelID, filterString, durable);
-         
-         addBinding(binding);         
-      }
-      finally
-      {
-         lock.writeLock().release();
-      }
-   }
-   
-   /*
-    * Called when another node removes a binding
-    */
-   public void removeBindingFromCluster(String nodeId, String queueName) throws Exception
-   {
-      lock.writeLock().acquire();
-      
-      try
-      {         
-         // Sanity
-         if (!nodeIdAddressMap.containsKey(nodeId))
-         {
-            throw new IllegalStateException("Cannot find address for node: " + nodeId);
-         }
-         
-         removeBinding(nodeId, queueName);         
-      }
-      finally
-      {
-         lock.writeLock().release();
-      }
-   }
-   
-   public void handleAddressNodeMapping(Address address, String nodeId) throws Exception
-   {
-      lock.writeLock().acquire();
-      
-      try
-      { 
-         nodeIdAddressMap.put(nodeId, address);
-      }
-      finally
-      {
-         lock.writeLock().release();
-      }
-   }
-   
-   public void addToQueue(String queueName, List messages) throws Exception
-   {
-      lock.readLock().acquire();      
-            
-      try
-      {
-         Binding binding = this.getBindingForQueueName(queueName);
-         
-         if (binding == null)
-         {
-            throw new IllegalStateException("Cannot find binding for queue name " + queueName);
-         }
-         
-         LocalClusteredQueue queue = (LocalClusteredQueue)binding.getQueue();
-         
-         Iterator iter = messages.iterator();
-         
-         while (iter.hasNext())
-         {
-            MessageReference ref = null;
-            
-            try
-            {
-               org.jboss.messaging.core.Message msg = (org.jboss.messaging.core.Message)iter.next();
-               
-               ref = ms.reference(msg);
-               
-               queue.handleFromCluster(null, ref, null);
-            }
-            finally
-            {
-               if (ref != null)
-               {
-                  ref.releaseMemoryReference();
-               }
-            }
-         }    
-      }
-      finally
-      {
-         
-         lock.readLock().release();
-      }
-   }
-   
-   public void routeFromCluster(org.jboss.messaging.core.Message message, String routingKey,
-                                Map queueNameNodeIdMap) throws Exception
-   {
-      log.info(this.nodeId + " received route from cluster, ref = " + message.getMessageID() + " routing key " +
-               routingKey + " map " + queueNameNodeIdMap);
-      
-      lock.readLock().acquire();  
-      
-      // Need to reference the message
-      MessageReference ref = null;
-      try
-      {
-         ref = ms.reference(message);
-              
-         // We route on the condition
-         ClusteredBindingsImpl cb = (ClusteredBindingsImpl)conditionMap.get(routingKey);
-         
-         log.info("cb is: " + cb);
-      
-         if (cb != null)
-         {                                
-            Collection bindings = cb.getAllBindings();
-            
-            Iterator iter = bindings.iterator();
-            
-            while (iter.hasNext())
-            {
-               Binding binding = (Binding)iter.next();
-               
-               log.info("got binding: " + binding.getQueue().getName());
-                                                        
-               if (binding.getNodeId().equals(this.nodeId))
-               {  
-                  log.info("node id matches");
-                  boolean handle = true;
-                  
-                  if (queueNameNodeIdMap != null)
-                  {
-                     String desiredNodeId = (String)queueNameNodeIdMap.get(binding.getQueue().getName());
-                     
-                     //When there are more than one queues with the same name across the cluster we only
-                     //want to chose one of them
-                     
-                     if (desiredNodeId != null)
-                     {
-                        handle = desiredNodeId.equals(nodeId);
-                     }
-                  }
-                  
-                  if (handle)
-                  {
-                     //It's a local binding so we pass the message on to the subscription
-                     
-                     LocalClusteredQueue queue = (LocalClusteredQueue)binding.getQueue();
-                     
-                     log.info("sending " + message.getMessageID() + " to queue: " + queue.getName() + " on node " + this.nodeId);
-                  
-                     //TODO instead of adding a new method on the channel
-                     //we should set a header and use the same method
-                     Delivery del = queue.handleFromCluster(null, ref, null);
-                     
-                     log.info("sending " + message.getMessageID() + " to queue: " + queue.getName() + " on node " + this.nodeId + " delivery is " + del + " accepted? " + del.isSelectorAccepted());
-                  }
-               }                                              
-            }                          
-         }
-      }
-      finally
-      {
-         if (ref != null)
-         {
-            ref.releaseMemoryReference();
-         }
-         lock.readLock().release();
-      }
-   }
-   
-   /*
-    * Multicast a message to all members of the group
-    */
-   public void asyncSendRequest(ClusterRequest request) throws Exception
-   {            
-      byte[] bytes = writeRequest(request);
-         
-      log.info("async sending " + bytes);
-      
-      asyncChannel.send(new Message(null, null, bytes));
-   }
-   
-   /*
-    * Unicast a message to one members of the group
-    */
-   public void asyncSendRequest(ClusterRequest request, String nodeId) throws Exception
-   {            
-      Address address = (Address)nodeIdAddressMap.get(nodeId);
-      
-      log.info("unicasting to address: " + address);
-      
-      byte[] bytes = writeRequest(request);
-            
-      Message m = new Message(address, null, bytes);
-      
-      //TODO - handle serialization more efficiently
-      asyncChannel.send(m);
-   }
-   
-   /*
-    * We put the transaction in the holding area
-    */
-   public void holdTransaction(TransactionId id, ClusterTransaction tx) throws Exception
-   {
-      synchronized (holdingArea)
-      {
-         holdingArea.put(id, tx);
-      } 
-   }
-   
-   public void commitTransaction(TransactionId id) throws Exception
-   {
-      ClusterTransaction tx = null;
-      
-      synchronized (holdingArea)
-      {
-         tx = (ClusterTransaction)holdingArea.remove(id);
-      }
-      
-      if (tx == null)
-      {
-         throw new IllegalStateException("Cannot find transaction transaction id: " + id);
-      }
-      
-      tx.commit(this);
-   }
-   
-   /*
-    * Called by a node if it starts and it detects that it crashed since it's last start-up.
-    * This method then checks to see if there any messages from that node in the holding area
-    * and if they are also in the database they will be processed
-    */
-   public void check(String nodeId) throws Exception
-   {
-      synchronized (holdingArea)
-      {
-         Iterator iter = holdingArea.entrySet().iterator();
-         
-         List toRemove = new ArrayList();
-         
-         while (iter.hasNext())
-         {
-            Map.Entry entry = (Map.Entry)iter.next();
-            
-            TransactionId id = (TransactionId)entry.getKey();
-            
-            if (id.getNodeId().equals(nodeId))
-            {
-               List holders = (List)entry.getValue();
-               
-               boolean wasCommitted = checkTransaction(holders);               
-               
-               if (wasCommitted)
-               {
-                  //We can process the transaction
-                  Iterator iter2 = holders.iterator();
-                  
-                  while (iter2.hasNext())
-                  {
-                     MessageHolder holder = (MessageHolder)iter2.next();
-                     
-                     routeFromCluster(holder.getMessage(), holder.getRoutingKey(), holder.getQueueNameToNodeIdMap());
-                  }
-                  
-                  toRemove.add(id);
-               }
-            }
-         }
-         
-         //Remove the transactions from the holding area
-         
-         iter = toRemove.iterator();
-         
-         while (iter.hasNext())
-         {
-            TransactionId id = (TransactionId)iter.next();
-            
-            holdingArea.remove(id);
-         }
-      }
-   }
-   
-   public void calculateRedistribution() throws Throwable
-   {
-      lock.readLock().acquire();
-      
-      try
-      {
-         Iterator iter = conditionMap.values().iterator();
-         
-         while (iter.hasNext())
-         {
-            ClusteredBindings cb = (ClusteredBindings)iter.next();
-            
-            Collection routers = cb.getRouters();
-            
-            Iterator iter2 = routers.iterator();
-            
-            while (iter2.hasNext())
-            {
-               FavourLocalRouter router = (FavourLocalRouter)iter2.next();        
-            
-               RedistributionOrder order = redistributionPolicy.calculate(router.getQueues());
-               
-               if (order != null)
-               {
-                  moveMessages(order.getQueue(), order.getDestinationNodeId(), order.getNumberOfMessages());
-               }               
-            }
-         }
-      }
-      finally
-      {
-         lock.readLock().release();
-      }
-   }
-   
-   public void sendStats() throws Exception
-   {
-      lock.writeLock().acquire();
-      
-      List statsList = null;      
-      
-      try
-      {         
-         Map nameMap = (Map)nameMaps.get(nodeId);
-         
-         if (nameMap != null)
-         {            
-            Iterator iter = nameMap.values().iterator();
-                     
-            while (iter.hasNext())
-            {
-               Binding bb = (Binding)iter.next();
-               
-               LocalClusteredQueue q = (LocalClusteredQueue)bb.getQueue();
-                             
-               if (q.isActive())
-               {                                                      
-                  QueueStats stats = q.getStats();
-                                     
-                  //We don't bother sending the stats if there's no significant change in the values
-                  
-                  if (q.changedSignificantly())
-                  {
-                     if (statsList == null)
-                     {
-                        statsList = new ArrayList();
-                     }
-
-                     statsList.add(stats);
-                  } 
-               }
-            }
-         }
-      }
-      finally
-      {
-         lock.writeLock().release();
-      }
-      
-      if (statsList != null)
-      {
-         ClusterRequest req = new QueueStatsRequest(nodeId, statsList);
-         
-         asyncSendRequest(req);
-      }
-   }
-   
-   public void updateQueueStats(String nodeId, List statsList) throws Exception
-   {
-      lock.writeLock().acquire();
-      
-      log.info("I have a list of stats: " + statsList.size() + " from nodeId: " + nodeId);
-      
-      Map nameMap = (Map)nameMaps.get(nodeId);
-      
-      if (nameMap == null)
-      {
-         //This is ok, the node might have left
-         log.info("But I have no bindings for " + nodeId);
-      }
-      else
-      {     
-         log.info("I do have bindings for " + nodeId);
-         try
-         {
-            Iterator iter = statsList.iterator();
-            
-            while (iter.hasNext())
-            {
-               QueueStats st = (QueueStats)iter.next();
-               
-               Binding bb = (Binding)nameMap.get(st.getQueueName());
-               
-               if (bb == null)
-               {
-                  throw new IllegalStateException("Cannot find binding for queue name: " + st.getQueueName());
-               }
-               
-               RemoteQueueStub stub = (RemoteQueueStub)bb.getQueue();
-               
-               stub.setStats(st);
-            }         
-         }
-         finally
-         {
-            lock.writeLock().release();      
-         }
-      }
-   }      
-      
-   // Public ------------------------------------------------------------------------------------------
-      
-   // Protected ---------------------------------------------------------------------------------------
-        
-   protected Bindings createBindings()
-   {
-      return new ClusteredBindingsImpl(this.nodeId, this.routerFactory);
-   }
-   
-   protected void loadBindings() throws Exception
-   {
-      // TODO I need to know whether this call times out - how do I know this??
-      boolean isState = syncChannel.getState(null, stateTimeout);
-                              
-      if (!isState)
-      {       
-         //Must be first member in group or non clustered- we load the state ourself from the database
-         super.loadBindings();      
-      }
-      else
-      {
-         //The state will be set in due course via the MessageListener - we must wait until this happens
-         
-         synchronized (setStateLock)
-         {
-            //TODO we should implement a timeout on this
-            while (!stateSet)
-            {
-               setStateLock.wait();
-            } 
-         }
-      }
-   }
-   
-   protected Binding createBinding(String nodeId, String condition, String queueName, long channelId, String filterString, boolean durable) throws Exception
-   {            
-      Filter filter = filterFactory.createFilter(filterString);
-      
-      Queue queue;
-      if (nodeId.equals(this.nodeId))
-      {
-         QueuedExecutor executor = (QueuedExecutor)pool.get();
-         
-         queue = new LocalClusteredQueue(nodeId, queueName, channelId, ms, pm, true,
-                                         durable, executor, filter);
-      }
-      else
-      {
-         queue = new RemoteQueueStub(nodeId, queueName, channelId, durable, pm, filter);
-      }
-      
-      Binding binding = new BindingImpl(nodeId, condition, queue);
-      
-      return binding;
-   }
-   
-   // Private ------------------------------------------------------------------------------------------
-           
-   private void syncSendRequest(ClusterRequest request) throws Exception
-   {            
-      byte[] bytes = writeRequest(request);
-            
-      Message message = new Message(null, null, bytes);      
-      
-      controlMessageDispatcher.castMessage(null, message, GroupRequest.GET_ALL, castTimeout);
-   }
-   
-   private boolean checkTransaction(List messageHolders) throws Exception
-   {
-      Iterator iter = messageHolders.iterator();
-      
-      //We only need to check that one of the refs made it to the database - the refs would have
-      //been inserted into the db transactionally, so either they're all there or none are
-      MessageHolder holder = (MessageHolder)iter.next();
-      
-      Collection bindings = listBindingsForCondition(holder.getRoutingKey());
-      
-      if (bindings == null)
-      {
-         throw new IllegalStateException("Cannot find bindings for key: " + holder.getRoutingKey());
-      }
-      
-      Iterator iter2 = bindings.iterator();
-      
-      long channelID = -1;
-      boolean found = false;
-      
-      while (iter2.hasNext())
-      {
-         Binding binding = (Binding)iter2.next();
-         
-         if (binding.getQueue().isRecoverable())
-         {
-            found = true;
-            
-            channelID = binding.getQueue().getChannelID();
-         }
-      }
-      
-      if (!found)
-      {
-         throw new IllegalStateException("Cannot find bindings");
-      }
-      
-      if (pm.referenceExists(channelID, holder.getMessage().getMessageID()))
-      {
-         return true;
-      }
-      else
-      {
-         return false;
-      }
-   }
-   
-   private void removeBindingsForAddress(Address address) throws Exception
-   {
-      lock.writeLock().acquire();
-      
-      try
-      { 
-         Iterator iter = nodeIdAddressMap.entrySet().iterator();
-         
-         String nodeId = null;
-         while (iter.hasNext())
-         {
-            Map.Entry entry = (Map.Entry)iter.next();
-            
-            Address adr = (Address)entry.getValue();
-            
-            if (adr.equals(address))
-            {
-               nodeId = (String)entry.getKey();
-            }
-         }
-         
-         if (nodeId == null)
-         {
-            throw new IllegalStateException("Cannot find node id for address: " + address);
-         }
-         
-         Map nameMap = (Map)nameMaps.get(nodeId);
-
-         if (nameMap != null)
-         {
-            List toRemove = new ArrayList();
-            
-            iter = nameMap.values().iterator();
-            
-            while (iter.hasNext())
-            {
-               Binding binding = (Binding)iter.next();
-               
-               if (!binding.getQueue().isRecoverable())
-               {
-                  toRemove.add(binding);
-               }
-            }
-            
-            iter = toRemove.iterator();
-            
-            while (iter.hasNext())
-            {
-               Binding binding = (Binding)iter.next();
-               
-               removeBinding(nodeId, binding.getQueue().getName());
-            }
-         }
-         
-         //Remove the address mapping
-         nodeIdAddressMap.remove(nodeId);
-      }
-      finally
-      {
-         lock.writeLock().release();
-      }
-   }
-      
-   //TODO - Sort out serialization properly
-   
-   private byte[] getStateAsBytes() throws Exception
-   {
-      List bindings = new ArrayList();
-      
-      Iterator iter = nameMaps.values().iterator();
-      
-      while (iter.hasNext())
-      {
-         Map map  = (Map)iter.next();
-         
-         Iterator iter2 = map.values().iterator();
-         
-         while (iter2.hasNext())
-         {
-            Binding binding = (Binding)iter2.next();
-                      
-            Queue queue = binding.getQueue();        
-            
-            BindingInfo info = new BindingInfo(binding.getNodeId(), queue.getName(),
-                                               binding.getCondition(),
-                                               queue.getFilter() == null ? null : queue.getFilter().getFilterString(),
-                                               queue.getChannelID(),
-                                               queue.isRecoverable());    
-            bindings.add(info);
-         }
-      }
-      
-      SharedState state = new SharedState(bindings, nodeIdAddressMap);
-      
-      byte[] bytes = StreamUtils.toBytes(state); 
-           
-      return bytes;
-   }
-   
-   private void processStateBytes(byte[] bytes) throws Exception
-   {
-      SharedState state = new SharedState();
-      
-      StreamUtils.fromBytes(state, bytes);
-      
-      nameMaps.clear();
-      
-      conditionMap.clear();
-                 
-      List bindings = state.getBindings();
-      
-      Iterator iter = bindings.iterator();
-      
-      while (iter.hasNext())
-      {
-         BindingInfo info = (BindingInfo)iter.next();
-         
-         Binding binding = this.createBinding(info.getNodeId(), info.getCondition(), info.getQueueName(), info.getChannelId(), info.getFilterString(), info.isDurable());
-         
-         addBinding(binding);
-      }
-      
-      this.nodeIdAddressMap.clear();
-      
-      this.nodeIdAddressMap.putAll(state.getNodeIdAddressMap());
-   }
-   
-   /*
-    * Move messages from queue on one node to queue on another node
-    */
-   private void moveMessages(Queue fromQueue, String toNodeId, int num) throws Throwable
-   {      
-      log.info("Moving " + num + " messages from " + this.nodeId + " to " + toNodeId + " for queue name");
-      
-      Transaction tx = tr.createTransaction();
-               
-      List dels = ((LocalClusteredQueue)fromQueue).getDeliveries(num);
-      
-      Iterator iter = dels.iterator();
-      
-      MoveMessagesCallback cb = new MoveMessagesCallback(nodeId, toNodeId, fromQueue.getName(),
-                                                         tx.getId(), this);      
-      while (iter.hasNext())
-      {
-         Delivery del = (Delivery)iter.next();
-         
-         del.acknowledge(tx);      
-         
-         cb.addMessage(del.getReference().getMessage());
-      }
-      
-      tx.commit();
- 
-   } 
-   
-   private byte[] writeRequest(ClusterRequest request) throws Exception
-   {
-      ByteArrayOutputStream baos = new ByteArrayOutputStream(2048);
-      
-      DataOutputStream daos = new DataOutputStream(baos);
-      
-      ClusterRequest.writeToStream(daos, request);
-            
-      daos.flush();
-      
-      return baos.toByteArray();
-   }
-   
-   private ClusterRequest readRequest(byte[] bytes) throws Exception
-   {
-      ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
-      
-      DataInputStream dais = new DataInputStream(bais);
-      
-      ClusterRequest request = ClusterRequest.createFromStream(dais);
-      
-      dais.close();
-      
-      return request;            
-   }
-   
-   // Inner classes -------------------------------------------------------------------
-    
-   /*
-    * This class is used to manage state on the control channel
-    */
-   private class ControlMessageListener implements MessageListener
-   {
-      public byte[] getState()
-      {     
-         try
-         {
-            lock.writeLock().acquire();
-         }
-         catch (InterruptedException e)
-         {
-            log.error("Thread Interrupted", e);
-         }
-         try
-         {
-            return getStateAsBytes();
-         }
-         catch (Exception e)
-         {
-            log.error("Caught Exception in MessageListener", e);
-            IllegalStateException e2 = new IllegalStateException(e.getMessage());
-            e2.setStackTrace(e.getStackTrace());
-            throw e2;
-         }     
-         finally
-         {
-            lock.writeLock().release();
-         }
-      }
-      
-      public void receive(Message message)
-      {         
-         log.info("Received message on control channel: " + message);
-      }
-      
-      public void setState(byte[] bytes)
-      {
-         if (bytes != null)
-         {
-            
-            try
-            {
-               lock.writeLock().acquire();         
-            }
-            catch (InterruptedException e)
-            {
-               log.error("Thread interrupted", e);
-            }
-            try
-            {
-               processStateBytes(bytes);               
-            }
-            catch (Exception e)
-            {
-               log.error("Caught Exception in MessageListener", e);
-               IllegalStateException e2 = new IllegalStateException(e.getMessage());
-               e2.setStackTrace(e.getStackTrace());
-               throw e2;
-            }
-            finally
-            {
-               lock.writeLock().release();
-            }
-         }
-               
-         synchronized (setStateLock)
-         {
-            stateSet = true;
-            setStateLock.notify();
-         }
-      }      
-   }
-   
-   /*
-    * We use this class so we notice when members leave the group
-    */
-   private class ControlMembershipListener implements MembershipListener
-   {
-      public void block()
-      {
-         //NOOP
-      }
-
-      public void suspect(Address address)
-      {
-         //NOOP
-      }
-
-      public void viewAccepted(View view)
-      {
-         if (currentView != null)
-         {
-            Iterator iter = currentView.getMembers().iterator();
-            
-            while (iter.hasNext())
-            {
-               Address address = (Address)iter.next();
-               
-               if (!view.containsMember(address))
-               {
-                  //Member must have left                  
-                  //We don't remove bindings for ourself
-                  
-                  Address currentAddress = syncChannel.getLocalAddress();
-                  
-                  if (!address.equals(currentAddress))
-                  {                  
-                     try
-                     {
-                        removeBindingsForAddress(address);
-                     }               
-                     catch (Exception e)
-                     {
-                        log.error("Caught Exception in MembershipListener", e);
-                        IllegalStateException e2 = new IllegalStateException(e.getMessage());
-                        e2.setStackTrace(e.getStackTrace());
-                        throw e2;
-                     }
-                  }
-               }
-            }
-         }
-         
-         currentView = view;
-      }
-
-      public byte[] getState()
-      {        
-         //NOOP
-         return null;
-      }     
-   }
-   
-   
-   /*
-    * This class is used to listen for messages on the data channel
-    */
-   private class DataReceiver implements Receiver
-   {
-      public void block()
-      {   
-         //NOOP
-      }
-
-      public void suspect(Address address)
-      { 
-         //NOOP
-      }
-
-      public void viewAccepted(View view)
-      { 
-         //NOOP
-      }
-
-      public byte[] getState()
-      {         
-         //NOOP
-         return null;
-      }
-      
-      public void receive(Message message)
-      {
-         try
-         {
-            byte[] bytes = message.getBuffer();
-            
-            ClusterRequest request = readRequest(bytes);
-            
-            request.execute(ClusteredPostOfficeImpl.this);            
-         }
-         catch (Exception e)
-         {
-            log.error("Caught Exception in Receiver", e);
-            IllegalStateException e2 = new IllegalStateException(e.getMessage());
-            e2.setStackTrace(e.getStackTrace());
-            throw e2;
-         }         
-      }
-      
-      public void setState(byte[] bytes)
-      {
-         //NOOP         
-      }      
-   }
-          
-   /*
-    * This class is used to handle synchronous requests
-    */
-   private class PostOfficeRequestHandler implements RequestHandler
-   {
-      public Object handle(Message message)
-      {                
-         try
-         {   
-            byte[] bytes = message.getBuffer();
-            
-            ClusterRequest request = readRequest(bytes);
-            
-            request.execute(ClusteredPostOfficeImpl.this);
-         }
-         catch (Exception e)
-         {
-            log.error("Caught Exception in RequestHandler", e);
-            IllegalStateException e2 = new IllegalStateException(e.getMessage());
-            e2.setStackTrace(e.getStackTrace());
-            throw e2;
-         }
-         return null;
-      }      
-   }   
-}
\ No newline at end of file

Copied: trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/DefaultClusteredBindings.java (from rev 1321, trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/ClusteredBindingsImpl.java)
===================================================================
--- trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/ClusteredBindingsImpl.java	2006-09-19 19:17:09 UTC (rev 1321)
+++ trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/DefaultClusteredBindings.java	2006-09-21 09:41:24 UTC (rev 1336)
@@ -0,0 +1,107 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.messaging.core.plugin.postoffice.cluster;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.jboss.messaging.core.plugin.postoffice.Binding;
+import org.jboss.messaging.core.plugin.postoffice.DefaultBindings;
+
+
+/**
+ * 
+ * A DefaultClusteredBindings
+ *
+ * @author <a href="mailto:tim.fox at jboss.com">Tim Fox</a>
+ * @version <tt>$Revision: 1.1 $</tt>
+ *
+ * $Id$
+ *
+ */
+class DefaultClusteredBindings extends DefaultBindings implements ClusteredBindings
+{
+   // Map <name, router>
+   private Map nameMap;
+   
+   private String thisNode;
+   
+   private int localDurableCount;
+   
+   DefaultClusteredBindings(String thisNode)
+   {
+      super();
+      
+      nameMap = new HashMap();
+      
+      this.thisNode = thisNode;
+   }
+   
+   public void addBinding(Binding binding)
+   {
+      super.addBinding(binding);
+  
+      if (binding.getNodeId().equals(thisNode) && binding.getQueue().isRecoverable())
+      {
+         localDurableCount++;
+      }      
+   }
+   
+   public boolean removeBinding(Binding binding)
+   {
+      boolean removed = super.removeBinding(binding);
+      
+      if (!removed)
+      {
+         return false;
+      }
+           
+      if (binding.getNodeId().equals(thisNode) && binding.getQueue().isRecoverable())
+      {
+         localDurableCount--;
+      }      
+
+      return true;
+   }
+   
+   public int getLocalDurableCount()
+   {
+      return localDurableCount;
+   }
+   
+   public Collection getRouters()
+   {
+      return nameMap.values();
+   }
+   
+   public void addRouter(String queueName, ClusterRouter router)
+   {
+      nameMap.put(queueName, router);
+   }
+   
+   public void removeRouter(String queueName)
+   {
+      nameMap.remove(queueName);
+   }
+
+}

Copied: trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/DefaultClusteredPostOffice.java (from rev 1321, trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/ClusteredPostOfficeImpl.java)
===================================================================
--- trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/ClusteredPostOfficeImpl.java	2006-09-19 19:17:09 UTC (rev 1321)
+++ trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/DefaultClusteredPostOffice.java	2006-09-21 09:41:24 UTC (rev 1336)
@@ -0,0 +1,1562 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.messaging.core.plugin.postoffice.cluster;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import javax.sql.DataSource;
+import javax.transaction.TransactionManager;
+
+import org.jboss.jms.server.QueuedExecutorPool;
+import org.jboss.logging.Logger;
+import org.jboss.messaging.core.Delivery;
+import org.jboss.messaging.core.Filter;
+import org.jboss.messaging.core.FilterFactory;
+import org.jboss.messaging.core.MessageReference;
+import org.jboss.messaging.core.Queue;
+import org.jboss.messaging.core.SimpleDelivery;
+import org.jboss.messaging.core.plugin.contract.ClusteredPostOffice;
+import org.jboss.messaging.core.plugin.contract.MessageStore;
+import org.jboss.messaging.core.plugin.contract.PersistenceManager;
+import org.jboss.messaging.core.plugin.postoffice.Binding;
+import org.jboss.messaging.core.plugin.postoffice.DefaultBinding;
+import org.jboss.messaging.core.plugin.postoffice.DefaultPostOffice;
+import org.jboss.messaging.core.tx.Transaction;
+import org.jboss.messaging.core.tx.TransactionRepository;
+import org.jboss.messaging.util.StreamUtils;
+import org.jgroups.Address;
+import org.jgroups.Channel;
+import org.jgroups.JChannel;
+import org.jgroups.MembershipListener;
+import org.jgroups.Message;
+import org.jgroups.MessageListener;
+import org.jgroups.Receiver;
+import org.jgroups.View;
+import org.jgroups.blocks.GroupRequest;
+import org.jgroups.blocks.MessageDispatcher;
+import org.jgroups.blocks.RequestHandler;
+import org.w3c.dom.Element;
+
+import EDU.oswego.cs.dl.util.concurrent.QueuedExecutor;
+
+/**
+ * 
+ * A DefaultClusteredPostOffice
+ *
+ * @author <a href="mailto:tim.fox at jboss.com">Tim Fox</a>
+ * @version <tt>$Revision: 1.1 $</tt>
+ *
+ * $Id$
+ *
+ */
+public class DefaultClusteredPostOffice extends DefaultPostOffice implements ClusteredPostOffice, PostOfficeInternal
+{
+   private static final Logger log = Logger.getLogger(DefaultClusteredPostOffice.class);
+                        
+   private Channel syncChannel;
+   
+   private Channel asyncChannel;
+   
+   private String groupName;
+   
+   private MessageDispatcher controlMessageDispatcher;
+   
+   private MessageListener controlMessageListener;
+   
+   private Receiver dataReceiver;
+   
+   private MembershipListener controlMembershipListener;
+   
+   private RequestHandler requestHandler;
+   
+   private Object setStateLock = new Object();
+   
+   private boolean stateSet;
+   
+   private View currentView;
+   
+   //Map < Address, node id>
+   private Map nodeIdAddressMap;
+   
+   private Map holdingArea;
+   
+   private Element syncChannelConfigE;
+   
+   private Element asyncChannelConfigE;
+   
+   private String syncChannelConfigS;
+   
+   private String asyncChannelConfigS;
+   
+   private long stateTimeout;
+   
+   private long castTimeout;
+   
+   private MessagePullPolicy messagePullPolicy;
+   
+   private ClusterRouterFactory routerFactory;
+   
+   private int pullSize;
+   
+   private Map routerMap;
+      
+   public DefaultClusteredPostOffice()
+   {        
+      init();
+   }
+   
+   private void init()
+   {
+      this.nodeIdAddressMap = new HashMap();
+      
+      this.holdingArea = new HashMap();
+   }
+   
+   /*
+    * Constructor using Element for configuration
+    */
+   public DefaultClusteredPostOffice(DataSource ds, TransactionManager tm, Properties sqlProperties,
+            boolean createTablesOnStartup,
+            String nodeId, String officeName, MessageStore ms,
+            PersistenceManager pm,
+            TransactionRepository tr,
+            FilterFactory filterFactory,
+            QueuedExecutorPool pool,                              
+            String groupName,
+            Element syncChannelConfig,
+            Element asyncChannelConfig,
+            long stateTimeout, long castTimeout,
+            MessagePullPolicy redistributionPolicy,
+            ClusterRouterFactory rf,
+            int pullSize) throws Exception
+   {            
+      this(ds, tm, sqlProperties, createTablesOnStartup, nodeId, officeName, ms,
+           pm, tr, filterFactory, pool, groupName, stateTimeout, castTimeout, redistributionPolicy,
+           rf, pullSize);
+      
+      this.syncChannelConfigE = syncChannelConfig;      
+      this.asyncChannelConfigE = asyncChannelConfig;     
+   }
+     
+   /*
+    * Constructor using String for configuration
+    */
+   public DefaultClusteredPostOffice(DataSource ds, TransactionManager tm, Properties sqlProperties,
+                              boolean createTablesOnStartup,
+                              String nodeId, String officeName, MessageStore ms,
+                              PersistenceManager pm,
+                              TransactionRepository tr,
+                              FilterFactory filterFactory,
+                              QueuedExecutorPool pool,                              
+                              String groupName,
+                              String syncChannelConfig,
+                              String asyncChannelConfig,
+                              long stateTimeout, long castTimeout,
+                              MessagePullPolicy redistributionPolicy,                      
+                              ClusterRouterFactory rf,
+                              int pullSize) throws Exception
+   {            
+      this(ds, tm, sqlProperties, createTablesOnStartup, nodeId, officeName, ms,
+           pm, tr, filterFactory, pool, groupName, stateTimeout, castTimeout, redistributionPolicy,
+           rf, pullSize);
+
+      this.syncChannelConfigS = syncChannelConfig;      
+      this.asyncChannelConfigS = asyncChannelConfig;     
+   }
+   
+   private DefaultClusteredPostOffice(DataSource ds, TransactionManager tm, Properties sqlProperties,
+                               boolean createTablesOnStartup,
+                               String nodeId, String officeName, MessageStore ms,
+                               PersistenceManager pm,                               
+                               TransactionRepository tr,
+                               FilterFactory filterFactory,
+                               QueuedExecutorPool pool,
+                               String groupName,
+                               long stateTimeout, long castTimeout,                             
+                               MessagePullPolicy redistributionPolicy,                               
+                               ClusterRouterFactory rf,
+                               int pullSize)
+   {
+      super (ds, tm, sqlProperties, createTablesOnStartup, nodeId, officeName, ms, pm, tr, filterFactory,
+             pool);
+             
+      this.pm = pm;
+      
+      this.groupName = groupName;
+      
+      this.stateTimeout = stateTimeout;
+      
+      this.castTimeout = castTimeout;
+      
+      this.messagePullPolicy = redistributionPolicy;
+      
+      this.routerFactory = rf;
+      
+      this.pullSize = pullSize;
+      
+      routerMap = new HashMap();
+      
+      init();
+   }
+
+   // MessagingComponent overrides
+   // --------------------------------------------------------------
+   
+   public void start() throws Exception
+   {
+      if (syncChannelConfigE != null)
+      {        
+         this.syncChannel = new JChannel(syncChannelConfigE);
+         this.asyncChannel = new JChannel(asyncChannelConfigE); 
+      }
+      else
+      {
+         this.syncChannel = new JChannel(syncChannelConfigS);
+         this.asyncChannel = new JChannel(asyncChannelConfigS); 
+      }
+      
+      //We don't want to receive local messages on any of the channels
+      syncChannel.setOpt(Channel.LOCAL, Boolean.FALSE);
+      
+      asyncChannel.setOpt(Channel.LOCAL, Boolean.FALSE);
+      
+      this.controlMessageListener = new ControlMessageListener();
+      
+      this.requestHandler = new PostOfficeRequestHandler();
+      
+      this.controlMembershipListener = new ControlMembershipListener();
+      
+      this.controlMessageDispatcher = new MessageDispatcher(syncChannel, controlMessageListener,
+                                                            controlMembershipListener, requestHandler, true);      
+      this.dataReceiver = new DataReceiver();
+      
+      asyncChannel.setReceiver(dataReceiver);    
+             
+      syncChannel.connect(groupName);
+      
+      asyncChannel.connect(groupName);
+      
+      super.start();
+      
+      Address currentAddress = syncChannel.getLocalAddress();
+      
+      log.info(this.nodeId + " address is " + currentAddress);
+             
+      handleAddressNodeMapping(currentAddress, nodeId);
+      
+      syncSendRequest(new SendNodeIdRequest(currentAddress, nodeId));           
+   }
+
+   public void stop() throws Exception
+   {
+      super.stop();
+         
+      syncChannel.close();
+      
+      asyncChannel.close();
+   }  
+   
+   // PostOffice implementation ---------------------------------------        
+
+   public Binding bindClusteredQueue(String condition, LocalClusteredQueue queue) throws Exception
+   {           
+      if (!queue.getNodeId().equals(this.nodeId))
+      {
+         throw new IllegalArgumentException("Queue node id does not match office node id");
+      }
+      
+      Binding binding = (Binding)super.bindQueue(condition, queue);
+      
+      BindRequest request =
+         new BindRequest(nodeId, queue.getName(), condition, queue.getFilter() == null ? null : queue.getFilter().getFilterString(),
+                         binding.getQueue().getChannelID(), queue.isRecoverable());
+      
+      syncSendRequest(request);
+        
+      return binding;
+   }
+   
+   public Binding unbindClusteredQueue(String queueName) throws Throwable
+   {
+      Binding binding = (Binding)super.unbindQueue(queueName);
+      
+      UnbindRequest request = new UnbindRequest(nodeId, queueName);
+      
+      syncSendRequest(request);
+      
+      return binding;
+   }
+   
+   /*
+    * This is called by the server peer if it determines that the server crashed last time it was run
+    */
+   public void recover() throws Exception
+   {
+      //We send a "check" message to all nodes of the cluster
+      asyncSendRequest(new CheckRequest(nodeId));
+   }
+   
+   public boolean route(MessageReference ref, String condition, Transaction tx) throws Exception
+   {
+      if (ref == null)
+      {
+         throw new IllegalArgumentException("Message reference is null");
+      }
+      
+      if (condition == null)
+      {
+         throw new IllegalArgumentException("Condition is null");
+      }
+      
+      boolean routed = false;
+      
+      lock.readLock().acquire();
+      
+      try
+      {      
+         ClusteredBindings cb = (ClusteredBindings)conditionMap.get(condition);
+         
+         boolean startInternalTx = false;
+         
+         String lastNodeId = null;
+         
+         if (cb != null)
+         {
+            if (tx == null && ref.isReliable())
+            {                
+               if (!(cb.getDurableCount() == 0 || (cb.getDurableCount() == 1 && cb.getLocalDurableCount() == 1)))
+               {
+                  // When routing a persistent message without a transaction then we may need to start an 
+                  // internal transaction in order to route it.
+                  // This is so we can guarantee the message is delivered to all or none of the subscriptions.
+                  // We need to do this if there is anything other than
+                  // No durable subs or exactly one local durable sub
+                  startInternalTx = true;
+               }
+            }                        
+            
+            if (startInternalTx)
+            {
+               tx = tr.createTransaction();
+            }
+                
+            int numberRemote = 0;
+            
+            Map queueNameNodeIdMap = null;
+            
+            long lastChannelId = -1;
+            
+            Collection routers = cb.getRouters();
+
+            Iterator iter = routers.iterator();
+                     
+            while (iter.hasNext())
+            {
+               ClusterRouter router = (ClusterRouter)iter.next();
+               
+               Delivery del = router.handle(null, ref, tx);
+               
+               if (del != null && del.isSelectorAccepted())
+               {
+                  routed = true;
+               }
+                              
+               ClusteredQueue queue = (ClusteredQueue)del.getObserver();
+               
+               log.info("sent " + ref.getMessageID() + " to " + queue.getName() + " on node " + queue.getNodeId() + " selector accepted " + del.isSelectorAccepted());
+               
+               if (del.isSelectorAccepted() && !queue.isLocal())
+               {
+                  //We need to send the message remotely
+                  numberRemote++;
+                  
+                  lastNodeId = queue.getNodeId();
+                  
+                  if (router.numberOfReceivers() > 1 && queueNameNodeIdMap == null)
+                  {
+                     //If there are more than one queues with the same node on the remote nodes
+                     //We have now chosen which one will receive the message so we need to add this
+                     //information to a map which will get sent when casting - so the the queue
+                     //on the receiving node knows whether to receive the message
+                     queueNameNodeIdMap = new HashMap();
+                     
+                     //We add an entry to the map so that on the receiving node we can work out which
+                     //queue instance will receive the message
+                     queueNameNodeIdMap.put(queue.getName(), lastNodeId);
+                  }
+                  
+                  lastChannelId = queue.getChannelID();
+               }
+            }
+            
+            //Now we've sent the message to any local queues, we might also need
+            //to send the message to the other office instances on the cluster if there are
+            //queues on those nodes that need to receive the message
+            
+            //FIXME - there is a bug here, numberRemote does not take into account that more than one
+            //of the number remote may be on the same node, so we could end up multicasting
+            //when unicast would do
+            if (numberRemote > 0)
+            {
+               log.info("Need to send remotely");
+ 
+               if (tx == null)
+               {
+                  if (numberRemote == 1)
+                  {
+                     log.info("unicast no tx");
+                     //Unicast - only one node is interested in the message
+                     
+                     //FIXME - temporarily commented out until can get unicast to work
+                     //asyncSendRequest(new MessageRequest(condition, ref.getMessage(), null), lastNodeId);
+                     asyncSendRequest(new MessageRequest(condition, ref.getMessage(), null));
+                  }
+                  else
+                  {
+                     log.info("multicast no tx");
+                     //Multicast - more than one node is interested
+                     asyncSendRequest(new MessageRequest(condition, ref.getMessage(), queueNameNodeIdMap));
+                  }                                 
+               }
+               else
+               {
+                  CastMessagesCallback callback = (CastMessagesCallback)tx.getCallback(this);
+                  
+                  if (callback == null)
+                  {
+                     callback = new CastMessagesCallback(nodeId, tx.getId(), DefaultClusteredPostOffice.this);
+                     
+                     //This callback MUST be executed first
+                     
+                     //Execution order is as follows:
+                     //Before commit:
+                     //1. Cast messages across network - get added to holding area (if persistent) on receiving
+                     //nodes
+                     //2. Persist messages in persistent store
+                     //After commit
+                     //1. Cast commit message across network
+                     tx.addFirstCallback(callback, this);
+                  }
+                      
+                  callback.addMessage(condition, ref.getMessage(), queueNameNodeIdMap,
+                                      numberRemote == 1 ? lastNodeId : null,
+                                      lastChannelId);    
+               }
+            }
+                                                
+            if (startInternalTx)
+            {               
+               tx.commit();
+            }
+         }
+      }
+      finally
+      {                  
+         lock.readLock().release();
+      }
+         
+      return routed; 
+   }
+   
+   public boolean isLocal()
+   {
+      return false;
+   }
+   
+   // PostOfficeInternal implementation ------------------------------------------------------------------
+   
+   /*
+    * Called when another node adds a binding
+    */
+   public void addBindingFromCluster(String nodeId, String queueName, String condition,
+                                     String filterString, long channelID, boolean durable)
+      throws Exception
+   {
+      lock.writeLock().acquire();
+      
+      try
+      {                     
+         //Sanity
+
+         if (!nodeIdAddressMap.containsKey(nodeId))
+         {
+            throw new IllegalStateException("Cannot find address for node: " + nodeId);
+         }
+         
+         // We currently only allow one binding per name per node
+         Map nameMap = (Map)nameMaps.get(nodeId);
+         
+         Binding binding = null;
+         
+         if (nameMap != null)
+         {
+            binding = (Binding)nameMap.get(queueName);
+         }
+         
+         if (binding != null)
+         {
+            throw new IllegalArgumentException(this.nodeId + "Binding already exists for node Id " + nodeId + " queue name " + queueName);
+         }
+            
+         binding = this.createBinding(nodeId, condition, queueName, channelID, filterString, durable);
+         
+         addBinding(binding);         
+      }
+      finally
+      {
+         lock.writeLock().release();
+      }
+   }
+   
+   /*
+    * Called when another node removes a binding
+    */
+   public void removeBindingFromCluster(String nodeId, String queueName) throws Exception
+   {
+      lock.writeLock().acquire();
+      
+      try
+      {         
+         // Sanity
+         if (!nodeIdAddressMap.containsKey(nodeId))
+         {
+            throw new IllegalStateException("Cannot find address for node: " + nodeId);
+         }
+         
+         removeBinding(nodeId, queueName);         
+      }
+      finally
+      {
+         lock.writeLock().release();
+      }
+   }
+   
+   public void handleAddressNodeMapping(Address address, String nodeId) throws Exception
+   {
+      lock.writeLock().acquire();
+      
+      try
+      { 
+         nodeIdAddressMap.put(nodeId, address);
+      }
+      finally
+      {
+         lock.writeLock().release();
+      }
+   }
+   
+   public void addToQueue(String queueName, List messages) throws Exception
+   {
+      lock.readLock().acquire();      
+            
+      try
+      {
+         Binding binding = this.getBindingForQueueName(queueName);
+         
+         if (binding == null)
+         {
+            throw new IllegalStateException("Cannot find binding for queue name " + queueName);
+         }
+         
+         LocalClusteredQueue queue = (LocalClusteredQueue)binding.getQueue();
+         
+         Iterator iter = messages.iterator();
+         
+         while (iter.hasNext())
+         {
+            MessageReference ref = null;
+            
+            try
+            {
+               org.jboss.messaging.core.Message msg = (org.jboss.messaging.core.Message)iter.next();
+               
+               ref = ms.reference(msg);
+               
+               queue.handleFromCluster(ref);
+            }
+            finally
+            {
+               if (ref != null)
+               {
+                  ref.releaseMemoryReference();
+               }
+            }
+         }    
+      }
+      finally
+      {
+         
+         lock.readLock().release();
+      }
+   }
+   
+   public void routeFromCluster(org.jboss.messaging.core.Message message, String routingKey,
+                                Map queueNameNodeIdMap) throws Exception
+   {
+      log.info(this.nodeId + " received route from cluster, ref = " + message.getMessageID() + " routing key " +
+               routingKey + " map " + queueNameNodeIdMap);
+      
+      lock.readLock().acquire();  
+      
+      // Need to reference the message
+      MessageReference ref = null;
+      try
+      {
+         ref = ms.reference(message);
+              
+         // We route on the condition
+         DefaultClusteredBindings cb = (DefaultClusteredBindings)conditionMap.get(routingKey);
+         
+         log.info("cb is: " + cb);
+      
+         if (cb != null)
+         {                                
+            Collection bindings = cb.getAllBindings();
+            
+            Iterator iter = bindings.iterator();
+            
+            while (iter.hasNext())
+            {
+               Binding binding = (Binding)iter.next();
+               
+               log.info("got binding: " + binding.getQueue().getName());
+                                                        
+               if (binding.getNodeId().equals(this.nodeId))
+               {  
+                  log.info("node id matches");
+                  boolean handle = true;
+                  
+                  if (queueNameNodeIdMap != null)
+                  {
+                     String desiredNodeId = (String)queueNameNodeIdMap.get(binding.getQueue().getName());
+                     
+                     //When there are more than one queues with the same name across the cluster we only
+                     //want to chose one of them
+                     
+                     if (desiredNodeId != null)
+                     {
+                        handle = desiredNodeId.equals(nodeId);
+                     }
+                  }
+                  
+                  if (handle)
+                  {
+                     //It's a local binding so we pass the message on to the subscription
+                     
+                     LocalClusteredQueue queue = (LocalClusteredQueue)binding.getQueue();
+                     
+                     log.info("sending " + message.getMessageID() + " to queue: " + queue.getName() + " on node " + this.nodeId);
+                  
+                     //TODO instead of adding a new method on the channel
+                     //we should set a header and use the same method
+                     Delivery del = queue.handleFromCluster(ref);
+                     
+                     log.info("sending " + message.getMessageID() + " to queue: " + queue.getName() + " on node " + this.nodeId + " delivery is " + del + " accepted? " + del.isSelectorAccepted());
+                  }
+               }                                              
+            }                          
+         }
+      }
+      finally
+      {
+         if (ref != null)
+         {
+            ref.releaseMemoryReference();
+         }
+         lock.readLock().release();
+      }
+   }
+   
+   /*
+    * Multicast a message to all members of the group
+    */
+   public void asyncSendRequest(ClusterRequest request) throws Exception
+   {            
+      byte[] bytes = writeRequest(request);
+         
+      log.info("async sending " + bytes);
+      
+      asyncChannel.send(new Message(null, null, bytes));
+   }
+   
+   /*
+    * Unicast a message to one members of the group
+    */
+   public void asyncSendRequest(ClusterRequest request, Address address) throws Exception
+   {            
+      log.info("unicasting to address: " + address);
+      
+      byte[] bytes = writeRequest(request);
+            
+      Message m = new Message(address, null, bytes);
+      
+      //TODO - handle serialization more efficiently
+      asyncChannel.send(m);
+   }
+   
+   /*
+    * We put the transaction in the holding area
+    */
+   public void holdTransaction(TransactionId id, ClusterTransaction tx) throws Exception
+   {
+      synchronized (holdingArea)
+      {
+         holdingArea.put(id, tx);
+      } 
+   }
+   
+   public void commitTransaction(TransactionId id) throws Throwable
+   {
+      ClusterTransaction tx = null;
+      
+      synchronized (holdingArea)
+      {
+         tx = (ClusterTransaction)holdingArea.remove(id);
+      }
+      
+      if (tx == null)
+      {
+         throw new IllegalStateException("Cannot find transaction transaction id: " + id);
+      }
+      
+      tx.commit(this);
+   }
+   
+   /**
+    * Check for any transactions that need to be committed or rolled back
+    */
+   public void check(String nodeId) throws Throwable
+   {
+      synchronized (holdingArea)
+      {
+         Iterator iter = holdingArea.entrySet().iterator();
+         
+         List toRemove = new ArrayList();
+         
+         while (iter.hasNext())
+         {
+            Map.Entry entry = (Map.Entry)iter.next();
+            
+            TransactionId id = (TransactionId)entry.getKey();
+            
+            if (id.getNodeId().equals(nodeId))
+            {
+               ClusterTransaction tx = (ClusterTransaction)iter.next();
+               
+               boolean commit = tx.check(this);
+               
+               if (commit)
+               {
+                  tx.commit(this);
+               }
+               else
+               {
+                  tx.rollback(this);
+               }
+               
+               toRemove.add(tx);
+            }
+         }
+         
+         //Remove the transactions from the holding area
+         
+         iter = toRemove.iterator();
+         
+         while (iter.hasNext())
+         {
+            TransactionId id = (TransactionId)iter.next();
+            
+            holdingArea.remove(id);
+         }
+      }
+   }
+   
+   public void sendStats() throws Exception
+   {
+      lock.writeLock().acquire();
+      
+      List statsList = null;      
+      
+      try
+      {         
+         Map nameMap = (Map)nameMaps.get(nodeId);
+         
+         if (nameMap != null)
+         {            
+            Iterator iter = nameMap.values().iterator();
+                     
+            while (iter.hasNext())
+            {
+               Binding bb = (Binding)iter.next();
+               
+               LocalClusteredQueue q = (LocalClusteredQueue)bb.getQueue();
+                             
+               if (q.isActive())
+               {                                                      
+                  QueueStats stats = q.getStats();
+                                     
+                  //We don't bother sending the stats if there's no significant change in the values
+                  
+                  if (q.changedSignificantly())
+                  {
+                     if (statsList == null)
+                     {
+                        statsList = new ArrayList();
+                     }
+
+                     statsList.add(stats);
+                  } 
+               }
+            }
+         }
+      }
+      finally
+      {
+         lock.writeLock().release();
+      }
+      
+      if (statsList != null)
+      {
+         ClusterRequest req = new QueueStatsRequest(nodeId, statsList);
+         
+         asyncSendRequest(req);
+      }
+   }
+   
+   public void updateQueueStats(String nodeId, List statsList) throws Exception
+   {
+      lock.writeLock().acquire();
+      
+      try
+      {      
+         log.info("I have a list of stats: " + statsList.size() + " from nodeId: " + nodeId);
+         
+         if (nodeId.equals(this.nodeId))
+         {
+            //Sanity check
+            throw new IllegalStateException("Cannot update queue stats for current node");
+         }
+         
+         Map nameMap = (Map)nameMaps.get(nodeId);
+         
+         if (nameMap == null)
+         {
+            //This is ok, the node might have left
+            log.info("But I have no bindings for " + nodeId);
+         }
+         else
+         {     
+            log.info("I do have bindings for " + nodeId);
+
+            Iterator iter = statsList.iterator();
+            
+            while (iter.hasNext())
+            {
+               QueueStats st = (QueueStats)iter.next();
+               
+               Binding bb = (Binding)nameMap.get(st.getQueueName());
+               
+               if (bb == null)
+               {
+                  throw new IllegalStateException("Cannot find binding for queue name: " + st.getQueueName());
+               }
+               
+               RemoteQueueStub stub = (RemoteQueueStub)bb.getQueue();
+               
+               stub.setStats(st);
+               
+               ClusterRouter router = (ClusterRouter)routerMap.get(st.getQueueName());
+               
+               LocalClusteredQueue localQueue = router.getLocalQueue();
+               
+               if (localQueue != null)
+               {               
+                  RemoteQueueStub toQueue = messagePullPolicy.chooseQueue(router.getQueues());
+                  
+                  if (toQueue != null)
+                  {
+                     localQueue.setPullQueue(toQueue);
+                     
+                     //We now trigger delivery - this may cause a pull event
+                     localQueue.deliver(false);
+                  }
+               }               
+            }         
+         }         
+      }
+      finally
+      {
+         lock.writeLock().release();      
+      }
+   }      
+   
+   public boolean referenceExistsInStorage(long channelID, long messageID) throws Exception
+   {
+      return pm.referenceExists(channelID, messageID);
+   } 
+   
+   public List getDeliveries(String queueName, int numMessages) throws Exception
+   {
+      Binding binding = getBindingForQueueName(queueName);
+      
+      if (binding == null)
+      {
+         throw new IllegalArgumentException("Cannot find binding for queue " + queueName);
+      }
+      
+      LocalClusteredQueue queue = (LocalClusteredQueue)binding.getQueue();
+      
+      List dels = queue.getDeliveries(numMessages);
+      
+      return dels;
+   }
+         
+   public void pullMessages(ClusteredQueue localQueue, ClusteredQueue remoteQueue) throws Throwable
+   {
+      pullMessages(localQueue, remoteQueue, pullSize);
+   }
+              
+   // Public ------------------------------------------------------------------------------------------
+      
+   // Protected ---------------------------------------------------------------------------------------
+        
+   protected void addToConditionMap(Binding binding)
+   {
+      String condition = binding.getCondition();
+      
+      ClusteredBindings bindings = (ClusteredBindings)conditionMap.get(condition);
+      
+      if (bindings == null)
+      {
+         bindings = new DefaultClusteredBindings(nodeId);
+         
+         conditionMap.put(condition, bindings);
+      }
+      
+      bindings.addBinding(binding);
+      
+      String queueName = binding.getQueue().getName();
+      
+      ClusterRouter router = (ClusterRouter)routerMap.get(queueName);
+      
+      if (router == null)
+      {
+         router = routerFactory.createRouter();
+         
+         routerMap.put(queueName, router);
+         
+         bindings.addRouter(queueName, router);
+      }
+      
+      router.add(binding.getQueue());                  
+   }
+
+   protected void removeFromConditionMap(Binding binding)
+   {
+      ClusteredBindings bindings = (ClusteredBindings)conditionMap.get(binding.getCondition());
+      
+      if (bindings == null)
+      {
+         throw new IllegalStateException("Cannot find condition bindings for " + binding.getCondition());
+      }
+      
+      boolean removed = bindings.removeBinding(binding);
+      
+      if (!removed)
+      {
+         throw new IllegalStateException("Cannot find binding in condition binding list");
+      }           
+      
+      if (bindings.isEmpty())
+      {
+         conditionMap.remove(binding.getCondition());
+      }        
+      
+      String queueName = binding.getQueue().getName();
+      
+      ClusterRouter router = (ClusterRouter)routerMap.get(queueName);
+      
+      if (router == null)
+      {
+         throw new IllegalStateException("Cannot find router with name " + queueName);
+      }
+      
+      removed = router.remove(binding.getQueue());
+      
+      if (!removed)
+      {
+         throw new IllegalStateException("Cannot find router in map");
+      }
+      
+      if (router.getQueues().isEmpty())
+      {
+         routerMap.remove(queueName);
+      }      
+   }
+
+   protected void loadBindings() throws Exception
+   {
+      // TODO I need to know whether this call times out - how do I know this??
+      boolean isState = syncChannel.getState(null, stateTimeout);
+                              
+      if (!isState)
+      {       
+         //Must be first member in group or non clustered- we load the state ourself from the database
+         super.loadBindings();      
+      }
+      else
+      {
+         //The state will be set in due course via the MessageListener - we must wait until this happens
+         
+         synchronized (setStateLock)
+         {
+            //TODO we should implement a timeout on this
+            while (!stateSet)
+            {
+               setStateLock.wait();
+            } 
+         }
+      }
+   }
+   
+   protected Binding createBinding(String nodeId, String condition, String queueName, long channelId, String filterString, boolean durable) throws Exception
+   {            
+      Filter filter = filterFactory.createFilter(filterString);
+      
+      Queue queue;
+      if (nodeId.equals(this.nodeId))
+      {
+         QueuedExecutor executor = (QueuedExecutor)pool.get();
+         
+         queue = new LocalClusteredQueue(this, nodeId, queueName, channelId, ms, pm, true,
+                                         durable, executor, filter);
+      }
+      else
+      {
+         queue = new RemoteQueueStub(nodeId, queueName, channelId, durable, pm, filter);
+      }
+      
+      Binding binding = new DefaultBinding(nodeId, condition, queue);
+      
+      return binding;
+   }
+   
+   
+   
+   // Private ------------------------------------------------------------------------------------------
+           
+   /**
+    * TODO This can probably be moved into LocalClusteredQueue
+    * 
+    * Pull messages from a remote queue to a local queue.
+    * If any of the messages are reliable then this needs to be done reliable (i.e. without loss or redelivery)
+    * Normally this would require 2PC which would make performance suck.
+    * However since we know both queues share the same DB then we can do the persistence locally in the same
+    * tx thus avoiding 2PC and maintaining reliability:)
+    * We do the following:
+    * 
+    * 1. A tx is started locally
+    * 2. Create deliveries for message(s) on the remote node - bring messages back to the local node
+    * We send a message to the remote node to retrieve a set of deliveries from the queue - it gets a max of num
+    * deliveries.
+    * The unreliable ones can be acknowledged immediately, the reliable ones are not acknowledged and a holding transaction
+    * is placed in the holding area on the remote node, which contains knowledge of the deliveries.
+    * The messages corresponding to the deliveries are returned to the local node
+    * 3. The retrieved messages are added to the local queue in the tx
+    * 4. Deliveries corresponding to the messages retrieved are acknowledged LOCALLY for the remote queue.
+    * 5. The local tx is committed.
+    * 6. Send "commit" message to remote node
+    * 7. "Commit" message is received and deliveries in the holding transaction are acknowledged IN MEMORY only.
+    * On failure, commit or rollback will be called on the holding transaction causing the deliveries to be acked or cancelled
+    * depending on whether they exist in the database
+    */
+   private void pullMessages(ClusteredQueue localQueue, ClusteredQueue remoteQueue, int num) throws Throwable
+   { 
+      Address fromAddress = (Address)nodeIdAddressMap.get(remoteQueue.getNodeId());
+      
+      if (fromAddress == null)
+      {
+         //This is ok - the node might have left the group
+         return;
+      }
+   
+      Transaction tx = tr.createTransaction();
+         
+      ClusterRequest req = new PullMessagesRequest(this.nodeId, tx.getId(), remoteQueue.getChannelID(),
+                                                   localQueue.getName(), num);
+      
+      List msgs = (List)syncSendRequest(req, fromAddress);
+      
+      Iterator iter = msgs.iterator();
+      
+      while (iter.hasNext())
+      {
+         org.jboss.messaging.core.Message msg = (org.jboss.messaging.core.Message)iter.next();
+         
+         MessageReference ref = null;
+         
+         try
+         {
+            ref = ms.reference(msg);
+            
+            Delivery delRet = localQueue.handle(null, ref, tx);
+            
+            if (delRet == null || !delRet.isSelectorAccepted())
+            {
+               //This should never happen
+               throw new IllegalStateException("Aaarrgg queue did not accept reference");
+            }
+         }
+         finally
+         {
+            if (ref != null)
+            {
+               ref.releaseMemoryReference();
+            }
+         }
+         
+         Delivery del = new SimpleDelivery(localQueue, ref);
+         
+         del.acknowledge(tx);
+      }
+      
+      tx.commit();
+      
+      //TODO what if commit throws an exception - this means the commit message doesn't hit the 
+      //remote node so the holding transaction stays in the holding area 
+      //Need to catch the exception and throw a check message
+      //What we need to do is catch any exceptions at the top of the call, i.e. just after the interface
+      //and send a checkrequest
+      //This applies to a normal message and messages requests too
+            
+      req = new PullMessagesRequest(this.nodeId, tx.getId());
+      
+      asyncSendRequest(req, fromAddress);
+   }
+   
+   
+   /*
+    * Multicast a sync request
+    */
+   private void syncSendRequest(ClusterRequest request) throws Exception
+   {            
+      byte[] bytes = writeRequest(request);
+            
+      Message message = new Message(null, null, bytes);      
+      
+      controlMessageDispatcher.castMessage(null, message, GroupRequest.GET_ALL, castTimeout);
+   }
+   
+   /*
+    * Unicast a sync request
+    */
+   private Object syncSendRequest(ClusterRequest request, Address address) throws Exception
+   {              
+      byte[] bytes = writeRequest(request);
+            
+      Message message = new Message(address, null, bytes);      
+      
+      Object result = controlMessageDispatcher.sendMessage(message, GroupRequest.GET_FIRST, castTimeout);
+       
+      return result;
+   }
+    
+   private void removeBindingsForAddress(Address address) throws Exception
+   {
+      lock.writeLock().acquire();
+      
+      try
+      { 
+         Iterator iter = nodeIdAddressMap.entrySet().iterator();
+         
+         String nodeId = null;
+         while (iter.hasNext())
+         {
+            Map.Entry entry = (Map.Entry)iter.next();
+            
+            Address adr = (Address)entry.getValue();
+            
+            if (adr.equals(address))
+            {
+               nodeId = (String)entry.getKey();
+            }
+         }
+         
+         if (nodeId == null)
+         {
+            throw new IllegalStateException("Cannot find node id for address: " + address);
+         }
+         
+         Map nameMap = (Map)nameMaps.get(nodeId);
+
+         if (nameMap != null)
+         {
+            List toRemove = new ArrayList();
+            
+            iter = nameMap.values().iterator();
+            
+            while (iter.hasNext())
+            {
+               Binding binding = (Binding)iter.next();
+               
+               if (!binding.getQueue().isRecoverable())
+               {
+                  toRemove.add(binding);
+               }
+            }
+            
+            iter = toRemove.iterator();
+            
+            while (iter.hasNext())
+            {
+               Binding binding = (Binding)iter.next();
+               
+               removeBinding(nodeId, binding.getQueue().getName());
+            }
+         }
+         
+         //Remove the address mapping
+         nodeIdAddressMap.remove(nodeId);
+      }
+      finally
+      {
+         lock.writeLock().release();
+      }
+   }
+      
+   //TODO - Sort out serialization properly
+   
+   private byte[] getStateAsBytes() throws Exception
+   {
+      List bindings = new ArrayList();
+      
+      Iterator iter = nameMaps.values().iterator();
+      
+      while (iter.hasNext())
+      {
+         Map map  = (Map)iter.next();
+         
+         Iterator iter2 = map.values().iterator();
+         
+         while (iter2.hasNext())
+         {
+            Binding binding = (Binding)iter2.next();
+                      
+            Queue queue = binding.getQueue();        
+            
+            BindingInfo info = new BindingInfo(binding.getNodeId(), queue.getName(),
+                                               binding.getCondition(),
+                                               queue.getFilter() == null ? null : queue.getFilter().getFilterString(),
+                                               queue.getChannelID(),
+                                               queue.isRecoverable());    
+            bindings.add(info);
+         }
+      }
+      
+      SharedState state = new SharedState(bindings, nodeIdAddressMap);
+      
+      byte[] bytes = StreamUtils.toBytes(state); 
+           
+      return bytes;
+   }
+   
+   private void processStateBytes(byte[] bytes) throws Exception
+   {
+      SharedState state = new SharedState();
+      
+      StreamUtils.fromBytes(state, bytes);
+      
+      nameMaps.clear();
+      
+      conditionMap.clear();
+                 
+      List bindings = state.getBindings();
+      
+      Iterator iter = bindings.iterator();
+      
+      while (iter.hasNext())
+      {
+         BindingInfo info = (BindingInfo)iter.next();
+         
+         Binding binding = this.createBinding(info.getNodeId(), info.getCondition(), info.getQueueName(), info.getChannelId(), info.getFilterString(), info.isDurable());
+         
+         addBinding(binding);
+      }
+      
+      this.nodeIdAddressMap.clear();
+      
+      this.nodeIdAddressMap.putAll(state.getNodeIdAddressMap());
+   }
+   
+   
+   
+   private byte[] writeRequest(ClusterRequest request) throws Exception
+   {
+      ByteArrayOutputStream baos = new ByteArrayOutputStream(2048);
+      
+      DataOutputStream daos = new DataOutputStream(baos);
+      
+      ClusterRequest.writeToStream(daos, request);
+            
+      daos.flush();
+      
+      return baos.toByteArray();
+   }
+   
+   private ClusterRequest readRequest(byte[] bytes) throws Exception
+   {
+      ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
+      
+      DataInputStream dais = new DataInputStream(bais);
+      
+      ClusterRequest request = ClusterRequest.createFromStream(dais);
+      
+      dais.close();
+      
+      return request;            
+   }
+   
+   // Inner classes -------------------------------------------------------------------
+    
+   /*
+    * This class is used to manage state on the control channel
+    */
+   private class ControlMessageListener implements MessageListener
+   {
+      public byte[] getState()
+      {     
+         try
+         {
+            lock.writeLock().acquire();
+         }
+         catch (InterruptedException e)
+         {
+            log.error("Thread Interrupted", e);
+         }
+         try
+         {
+            return getStateAsBytes();
+         }
+         catch (Exception e)
+         {
+            log.error("Caught Exception in MessageListener", e);
+            IllegalStateException e2 = new IllegalStateException(e.getMessage());
+            e2.setStackTrace(e.getStackTrace());
+            throw e2;
+         }     
+         finally
+         {
+            lock.writeLock().release();
+         }
+      }
+      
+      public void receive(Message message)
+      {         
+         log.info("Received message on control channel: " + message);
+      }
+      
+      public void setState(byte[] bytes)
+      {
+         if (bytes != null)
+         {
+            
+            try
+            {
+               lock.writeLock().acquire();         
+            }
+            catch (InterruptedException e)
+            {
+               log.error("Thread interrupted", e);
+            }
+            try
+            {
+               processStateBytes(bytes);               
+            }
+            catch (Exception e)
+            {
+               log.error("Caught Exception in MessageListener", e);
+               IllegalStateException e2 = new IllegalStateException(e.getMessage());
+               e2.setStackTrace(e.getStackTrace());
+               throw e2;
+            }
+            finally
+            {
+               lock.writeLock().release();
+            }
+         }
+               
+         synchronized (setStateLock)
+         {
+            stateSet = true;
+            setStateLock.notify();
+         }
+      }      
+   }
+   
+   /*
+    * We use this class so we notice when members leave the group
+    */
+   private class ControlMembershipListener implements MembershipListener
+   {
+      public void block()
+      {
+         //NOOP
+      }
+
+      public void suspect(Address address)
+      {
+         //NOOP
+      }
+
+      public void viewAccepted(View view)
+      {
+         if (currentView != null)
+         {
+            Iterator iter = currentView.getMembers().iterator();
+            
+            while (iter.hasNext())
+            {
+               Address address = (Address)iter.next();
+               
+               if (!view.containsMember(address))
+               {
+                  //Member must have left                  
+                  //We don't remove bindings for ourself
+                  
+                  Address currentAddress = syncChannel.getLocalAddress();
+                  
+                  if (!address.equals(currentAddress))
+                  {                  
+                     try
+                     {
+                        removeBindingsForAddress(address);
+                     }               
+                     catch (Exception e)
+                     {
+                        log.error("Caught Exception in MembershipListener", e);
+                        IllegalStateException e2 = new IllegalStateException(e.getMessage());
+                        e2.setStackTrace(e.getStackTrace());
+                        throw e2;
+                     }
+                  }
+               }
+            }
+         }
+         
+         currentView = view;
+      }
+
+      public byte[] getState()
+      {        
+         //NOOP
+         return null;
+      }     
+   }
+   
+   
+   /*
+    * This class is used to listen for messages on the data channel
+    */
+   private class DataReceiver implements Receiver
+   {
+      public void block()
+      {   
+         //NOOP
+      }
+
+      public void suspect(Address address)
+      { 
+         //NOOP
+      }
+
+      public void viewAccepted(View view)
+      { 
+         //NOOP
+      }
+
+      public byte[] getState()
+      {         
+         //NOOP
+         return null;
+      }
+      
+      public void receive(Message message)
+      {
+         try
+         {
+            byte[] bytes = message.getBuffer();
+            
+            ClusterRequest request = readRequest(bytes);
+            
+            request.execute(DefaultClusteredPostOffice.this);            
+         }
+         catch (Throwable e)
+         {
+            log.error("Caught Exception in Receiver", e);
+            IllegalStateException e2 = new IllegalStateException(e.getMessage());
+            e2.setStackTrace(e.getStackTrace());
+            throw e2;
+         }         
+      }
+      
+      public void setState(byte[] bytes)
+      {
+         //NOOP         
+      }      
+   }
+          
+   /*
+    * This class is used to handle synchronous requests
+    */
+   private class PostOfficeRequestHandler implements RequestHandler
+   {
+      public Object handle(Message message)
+      {                
+         try
+         {   
+            byte[] bytes = message.getBuffer();
+            
+            ClusterRequest request = readRequest(bytes);
+            
+            request.execute(DefaultClusteredPostOffice.this);
+         }
+         catch (Throwable e)
+         {
+            log.error("Caught Exception in RequestHandler", e);
+            IllegalStateException e2 = new IllegalStateException(e.getMessage());
+            e2.setStackTrace(e.getStackTrace());
+            throw e2;
+         }
+         return null;
+      }      
+   }
+}
\ No newline at end of file

Added: trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/DefaultMessagePullPolicy.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/DefaultMessagePullPolicy.java	2006-09-21 00:50:21 UTC (rev 1335)
+++ trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/DefaultMessagePullPolicy.java	2006-09-21 09:41:24 UTC (rev 1336)
@@ -0,0 +1,71 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.messaging.core.plugin.postoffice.cluster;
+
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * A DefaultMessagePullPolicy
+ * 
+ * This chooses the remote queue with the most messages
+ *
+ * @author <a href="mailto:tim.fox at jboss.com">Tim Fox</a>
+ * @version <tt>$Revision: 1.1 $</tt>
+ *
+ * $Id$
+ *
+ */
+public class DefaultMessagePullPolicy implements MessagePullPolicy
+{
+
+   public RemoteQueueStub chooseQueue(List queues)
+   {
+      Iterator iter = queues.iterator();
+      
+      RemoteQueueStub chosenQueue = null;
+      
+      int maxMessages = 0;
+      
+      while (iter.hasNext())
+      {
+         ClusteredQueue queue = (ClusteredQueue)iter.next();
+         
+         if (!queue.isLocal())
+         {
+            QueueStats stats = queue.getStats();
+            
+            int cnt = stats.getMessageCount();
+            
+            if (cnt > maxMessages)
+            {
+               maxMessages = cnt;
+               
+               chosenQueue = (RemoteQueueStub)queue;
+            }
+         }
+      }
+      
+      return chosenQueue;
+   }
+
+}

Modified: trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/FavourLocalRouter.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/FavourLocalRouter.java	2006-09-21 00:50:21 UTC (rev 1335)
+++ trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/FavourLocalRouter.java	2006-09-21 09:41:24 UTC (rev 1336)
@@ -57,12 +57,12 @@
  * $Id$
  *
  */
-public class FavourLocalRouter implements Router
+public class FavourLocalRouter implements ClusterRouter
 {
    //MUST be an arraylist for fast index access
    private ArrayList queues;
    
-   private ClusteredQueue localQueue;
+   private LocalClusteredQueue localQueue;
    
    private int target;
    
@@ -75,6 +75,11 @@
    {
       return queues.size();
    }
+   
+   public LocalClusteredQueue getLocalQueue()
+   {
+      return localQueue;
+   }
 
    public boolean add(Receiver receiver)
    {
@@ -86,7 +91,7 @@
          {
             throw new IllegalStateException("Already has local queue");
          }
-         localQueue = queue;
+         localQueue = (LocalClusteredQueue)queue;
       }
       
       queues.add(queue); 

Modified: trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/FavourLocalRouterFactory.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/FavourLocalRouterFactory.java	2006-09-21 00:50:21 UTC (rev 1335)
+++ trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/FavourLocalRouterFactory.java	2006-09-21 09:41:24 UTC (rev 1336)
@@ -35,7 +35,7 @@
  */
 public class FavourLocalRouterFactory implements ClusterRouterFactory
 {
-   public Router createRouter()
+   public ClusterRouter createRouter()
    {
       return new FavourLocalRouter();
    }

Modified: trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/LocalClusteredQueue.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/LocalClusteredQueue.java	2006-09-21 00:50:21 UTC (rev 1335)
+++ trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/LocalClusteredQueue.java	2006-09-21 09:41:24 UTC (rev 1336)
@@ -22,17 +22,17 @@
 package org.jboss.messaging.core.plugin.postoffice.cluster;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 import org.jboss.messaging.core.Delivery;
-import org.jboss.messaging.core.DeliveryObserver;
 import org.jboss.messaging.core.Filter;
 import org.jboss.messaging.core.MessageReference;
 import org.jboss.messaging.core.SimpleDelivery;
 import org.jboss.messaging.core.local.PagingFilteredQueue;
 import org.jboss.messaging.core.plugin.contract.MessageStore;
 import org.jboss.messaging.core.plugin.contract.PersistenceManager;
-import org.jboss.messaging.core.tx.Transaction;
+import org.jboss.messaging.core.plugin.contract.PostOffice;
 import org.jboss.messaging.util.Future;
 
 import EDU.oswego.cs.dl.util.concurrent.QueuedExecutor;
@@ -49,23 +49,18 @@
  */
 public class LocalClusteredQueue extends PagingFilteredQueue implements ClusteredQueue
 {
-   private static final int MIN_PERIOD = 1000;
+   private PostOfficeInternal office;
    
-   private static final int STATS_DIFFERENCE_MARGIN_PERCENT = 10;   
-      
-   private String nodeId;
+   private volatile int lastCount;
    
-   private long lastTime;
+   private volatile boolean changedSignificantly;
    
-   private QueueStats lastStats;
+   private RemoteQueueStub pullQueue;
    
-   private volatile int numberAdded;
-   
-   private volatile int numberConsumed;
-   
-   private volatile boolean changedSignificantly;
+   private String nodeId;
  
-   public LocalClusteredQueue(String nodeId, String name, long id, MessageStore ms, PersistenceManager pm,             
+   //TODO - we shouldn't have to specify office AND nodeId
+   public LocalClusteredQueue(PostOffice office, String nodeId, String name, long id, MessageStore ms, PersistenceManager pm,             
                               boolean acceptReliableMessages, boolean recoverable, QueuedExecutor executor,
                               Filter filter,
                               int fullSize, int pageSize, int downCacheSize)
@@ -74,12 +69,11 @@
      
       this.nodeId = nodeId;
       
-      lastTime = System.currentTimeMillis();      
-      
-      numberAdded = numberConsumed = 0;
+      //FIXME - this cast is a hack
+      this.office = (PostOfficeInternal)office;
    }
    
-   public LocalClusteredQueue(String nodeId, String name, long id, MessageStore ms, PersistenceManager pm,             
+   public LocalClusteredQueue(PostOffice office, String nodeId, String name, long id, MessageStore ms, PersistenceManager pm,             
                               boolean acceptReliableMessages, boolean recoverable, QueuedExecutor executor,
                               Filter filter)
    {
@@ -87,54 +81,27 @@
       
       this.nodeId = nodeId;
       
-      lastTime = System.currentTimeMillis();      
-      
-      numberAdded = numberConsumed = 0;
+      //FIXME - this cast is a hack
+      this.office = (PostOfficeInternal)office;
    }
    
-   public QueueStats getStats()
+   public void setPullQueue(RemoteQueueStub queue)
    {
-      long now = System.currentTimeMillis();
+      this.pullQueue = queue;
+   }
+   
+   public QueueStats getStats()
+   {      
+      int cnt = messageCount();
       
-      long period = now - lastTime;
-      
-      if (period <= MIN_PERIOD)
+      if (cnt != lastCount)
       {
-         //Cache the value to avoid recalculating too often
-         return lastStats;
-      }
-      
-      float addRate = 1000 * numberAdded / ((float)period);
-      
-      float consumeRate = 1000 * numberConsumed / ((float)period);
-      
-      int cnt = messageCount();      
-      
-      if (lastStats != null)
-      {
-         if (checkSignificant(lastStats.getAddRate(), addRate) ||
-             checkSignificant(lastStats.getConsumeRate(), consumeRate) ||
-             checkSignificant(lastStats.getMessageCount(), cnt))
-         {
-            changedSignificantly = true; 
-         }
-         else
-         {
-            changedSignificantly = false;
-         }
-      }
-      else
-      {
          changedSignificantly = true;
+         
+         lastCount = cnt;
       }
-            
-      lastStats = new QueueStats(name, addRate, consumeRate, messageCount());
       
-      lastTime = now;
-      
-      numberAdded = numberConsumed = 0;
-      
-      return lastStats;
+      return new QueueStats(name, cnt);
    }      
    
    //Have the stats changed significantly since the last time we request them?
@@ -153,6 +120,9 @@
       return nodeId;
    }
       
+   /*
+    * Used when pulling messages from a remote queue
+    */
    public List getDeliveries(int number) throws Exception
    {
       List dels = new ArrayList();
@@ -161,17 +131,27 @@
       {
          synchronized (deliveryLock)
          {
-            MessageReference ref;
-            
-            while ((ref = removeFirstInMemory()) != null)
+            //We only get the refs if receiversReady = true since
+            //we don't
+            //TODO is this right?          
+            if (!receiversReady)
             {
-               SimpleDelivery del = new SimpleDelivery(this, ref);
+               MessageReference ref;
                
-               deliveries.add(del);
-               
-               dels.add(del);               
-            }           
-            return dels;
+               while ((ref = removeFirstInMemory()) != null)
+               {
+                  SimpleDelivery del = new SimpleDelivery(this, ref);
+                  
+                  deliveries.add(del);
+                  
+                  dels.add(del);               
+               }           
+               return dels;
+            }
+            else
+            {
+               return Collections.EMPTY_LIST;
+            }
          }
       }          
    }
@@ -181,7 +161,7 @@
     * persist the message even if it is persistent - this is because persistent messages
     * are always persisted on the sending node before sending.
     */
-   public Delivery handleFromCluster(DeliveryObserver sender, MessageReference ref, Transaction tx)
+   public Delivery handleFromCluster(MessageReference ref)
       throws Exception
    {
       if (filter != null && !filter.accept(ref))
@@ -194,61 +174,41 @@
       checkClosed();
       
       Future result = new Future();
+      
+      // Instead of executing directly, we add the handle request to the event queue.
+      // Since remoting doesn't currently handle non blocking IO, we still have to wait for the
+      // result, but when remoting does, we can use a full SEDA approach and get even better
+      // throughput.
+      this.executor.execute(new HandleRunnable(result, null, ref, false));
 
-      if (tx == null)
-      {         
-         // Instead of executing directly, we add the handle request to the event queue.
-         // Since remoting doesn't currently handle non blocking IO, we still have to wait for the
-         // result, but when remoting does, we can use a full SEDA approach and get even better
-         // throughput.
-         this.executor.execute(new HandleRunnable(result, sender, ref, false));
-   
-         return (Delivery)result.getResult();
-      }
-      else
-      {
-         return handleInternal(sender, ref, tx, false);
-      }
+      return (Delivery)result.getResult();
    }
-
-   protected void addReferenceInMemory(MessageReference ref) throws Exception
+   
+   public void acknowledgeFromCluster(Delivery d) throws Throwable
    {
-      super.addReferenceInMemory(ref);
-      
-      //This is ok, since the channel ensures only one thread calls this method at once
-      numberAdded++;
+      acknowledgeInternal(d, false);      
    }
-
-   protected boolean acknowledgeInMemory(Delivery d)
-   {
-      boolean acked = super.acknowledgeInMemory(d);
-      
-      // This is ok, since the channel ensures only one thread calls this method at once
-      numberConsumed--;
-      
-      return acked;
-   }  
    
-   private boolean checkSignificant(float oldValue, float newValue)
+   protected void deliverInternal(boolean handle) throws Throwable
    {
-      boolean significant = false;
+      super.deliverInternal(handle);
       
-      if (oldValue != 0)
-      {         
-         int percentChange = (int)(100 * (oldValue - newValue) / oldValue);
-         
-         if (Math.abs(percentChange) >= STATS_DIFFERENCE_MARGIN_PERCENT)
-         {
-            significant = true;
-         }
-      }
-      else
+      if (!handle)
       {
-         if (newValue != 0)
+         if (receiversReady)
          {
-            significant = true;
+            //Delivery has been prompted (not from handle call)
+            //and has run, and there are consumers that are still interested in receiving more
+            //refs but there are none available in the channel (either the channel is empty
+            //or there are only refs that don't match any selectors)
+            //then we should perhaps pull some messages from a remote queue
+            if (pullQueue != null)
+            {
+               office.pullMessages(this, pullQueue);                        
+            }
          }
       }
-      return significant;
    }
+   
+   
 }

Copied: trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/MessagePullPolicy.java (from rev 1321, trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/RedistributionPolicy.java)
===================================================================
--- trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/RedistributionPolicy.java	2006-09-19 19:17:09 UTC (rev 1321)
+++ trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/MessagePullPolicy.java	2006-09-21 09:41:24 UTC (rev 1336)
@@ -0,0 +1,39 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.messaging.core.plugin.postoffice.cluster;
+
+import java.util.List;
+
+/**
+ * 
+ * A MessagePullPolicy
+ *
+ * @author <a href="mailto:tim.fox at jboss.com">Tim Fox</a>
+ * @version <tt>$Revision: 1.1 $</tt>
+ *
+ * $Id$
+ *
+ */
+public interface MessagePullPolicy
+{
+   RemoteQueueStub chooseQueue(List queues);
+}

Deleted: trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/MessageRedistributor.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/MessageRedistributor.java	2006-09-21 00:50:21 UTC (rev 1335)
+++ trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/MessageRedistributor.java	2006-09-21 09:41:24 UTC (rev 1336)
@@ -1,100 +0,0 @@
-/*
- * JBoss, Home of Professional Open Source
- * Copyright 2005, JBoss Inc., and individual contributors as indicated
- * by the @authors tag. See the copyright.txt in the distribution for a
- * full listing of individual contributors.
- *
- * This is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
- */
-package org.jboss.messaging.core.plugin.postoffice.cluster;
-
-import java.util.Timer;
-import java.util.TimerTask;
-
-import org.jboss.logging.Logger;
-import org.jboss.messaging.core.plugin.contract.MessagingComponent;
-
-/**
- * A MessageMover
- *
- * @author <a href="mailto:tim.fox at jboss.com">Tim Fox</a>
- * @version <tt>$Revision: 1.1 $</tt>
- *
- * $Id$
- *
- */
-
-class MessageRedistributor implements MessagingComponent
-{
-   private static final Logger log = Logger.getLogger(MessageRedistributor.class);
-     
-   private PostOfficeInternal office;
-   
-   private Timer timer;
-   
-   private long period;
-   
-   MessageRedistributor(PostOfficeInternal office, long period)
-   {
-      this.office = office;
-      
-      this.period = period;
-   }
-   
-   
-   // MessagingComponent overrides
-   // ---------------------------------------------------
-   
-   public void start() throws Exception
-   {
-      timer = new Timer(true);
-      
-      //Add a random delay to start, to prevent multiple exchanges all calculating at the same time
-      
-      long delay = (long)(Math.random() * period);
-      
-      timer.schedule(new RedistributeTimerTask(), delay, period);
-   }
-
-   public void stop() throws Exception
-   {
-      timer.cancel();
-   }
-   
-   class RedistributeTimerTask extends TimerTask
-   {
-      public void run()
-      {
-         try
-         {
-            office.calculateRedistribution();
-         }
-         catch (Throwable t)
-         {
-            log.error("Caught Throwable in calculating message redistribution", t);
-         }
-         try
-         {
-            office.sendStats();
-         }
-         catch (Exception e)
-         {
-            log.error("Caught Exception in calculating/sending queue statistics", e);
-         }
-      }
-      
-   }
-}

Modified: trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/MessageRequest.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/MessageRequest.java	2006-09-21 00:50:21 UTC (rev 1335)
+++ trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/MessageRequest.java	2006-09-21 09:41:24 UTC (rev 1336)
@@ -63,12 +63,13 @@
       this.queueNameNodeIdMap = queueNameNodeIdMap;
    }
    
-   public void execute(PostOfficeInternal office) throws Exception
+   Object execute(PostOfficeInternal office) throws Exception
    {
       office.routeFromCluster(message, routingKey, queueNameNodeIdMap);      
+      return null;
    }  
    
-   public byte getType()
+   byte getType()
    {
       return TYPE;
    }

Modified: trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/MessagesRequest.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/MessagesRequest.java	2006-09-21 00:50:21 UTC (rev 1335)
+++ trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/MessagesRequest.java	2006-09-21 09:41:24 UTC (rev 1336)
@@ -54,7 +54,7 @@
       this.messageHolders = messageHolders;
    }
    
-   public void execute(PostOfficeInternal office) throws Exception
+   Object execute(PostOfficeInternal office) throws Exception
    {
       Iterator iter = messageHolders.iterator();
       
@@ -64,9 +64,10 @@
          
          office.routeFromCluster(holder.getMessage(), holder.getRoutingKey(), holder.getQueueNameToNodeIdMap());
       }
+      return null;
    }
    
-   public byte getType()
+   byte getType()
    {
       return TYPE;
    }

Deleted: trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/MoveMessagesCallback.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/MoveMessagesCallback.java	2006-09-21 00:50:21 UTC (rev 1335)
+++ trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/MoveMessagesCallback.java	2006-09-21 09:41:24 UTC (rev 1336)
@@ -1,109 +0,0 @@
-/*
- * JBoss, Home of Professional Open Source
- * Copyright 2005, JBoss Inc., and individual contributors as indicated
- * by the @authors tag. See the copyright.txt in the distribution for a
- * full listing of individual contributors.
- *
- * This is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
- */
-package org.jboss.messaging.core.plugin.postoffice.cluster;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.jboss.messaging.core.Message;
-import org.jboss.messaging.core.tx.TxCallback;
-
-/**
- * A MoveMessagesCallback
- *
- * @author <a href="mailto:tim.fox at jboss.com">Tim Fox</a>
- * @version <tt>$Revision: 1.1 $</tt>
- *
- * $Id$
- *
- */
-class MoveMessagesCallback implements TxCallback
-{
-   private List messages;
-   
-   private String destinationNodeId;
-   
-   private String currentNodeId;
-   
-   private String queueName;
-   
-   private long txId;
-   
-   private PostOfficeInternal office;
-      
-   MoveMessagesCallback(String currentNodeId, String destNodeId, String queueName, long txId, PostOfficeInternal office)
-   {
-      this.currentNodeId = currentNodeId;
-      
-      this.destinationNodeId = destNodeId;
-      
-      this.queueName = queueName;
-      
-      this.txId = txId;
-      
-      this.office = office;
-      
-      messages = new ArrayList();
-   }
-   
-   void addMessage(Message msg)
-   {
-      messages.add(msg);
-   }
-   
-   public void afterCommit(boolean onePhase) throws Exception
-   {
-      ClusterRequest req = new MoveTransactionRequest(currentNodeId, txId);
-      
-      //We unicast the message to the node
-      office.asyncSendRequest(req, destinationNodeId);    
-   }
-
-   public void afterPrepare() throws Exception
-   {
-      //NOOP
-   }
-
-   public void afterRollback(boolean onePhase) throws Exception
-   {
-      //NOOP
-   }
-
-   public void beforeCommit(boolean onePhase) throws Exception
-   {
-      ClusterRequest req = new MoveTransactionRequest(currentNodeId, txId, messages, queueName);
-      
-      //We unicast
-      office.asyncSendRequest(req, destinationNodeId);
-   }
-
-   public void beforePrepare() throws Exception
-   {
-      //NOOP
-   }
-
-   public void beforeRollback(boolean onePhase) throws Exception
-   {
-      //NOOP
-   }
-
-}

Deleted: trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/MoveTransactionRequest.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/MoveTransactionRequest.java	2006-09-21 00:50:21 UTC (rev 1335)
+++ trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/MoveTransactionRequest.java	2006-09-21 09:41:24 UTC (rev 1336)
@@ -1,137 +0,0 @@
-/*
- * JBoss, Home of Professional Open Source
- * Copyright 2005, JBoss Inc., and individual contributors as indicated
- * by the @authors tag. See the copyright.txt in the distribution for a
- * full listing of individual contributors.
- *
- * This is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
- */
-package org.jboss.messaging.core.plugin.postoffice.cluster;
-
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-
-import org.jboss.messaging.core.Message;
-import org.jboss.messaging.core.message.MessageFactory;
-import org.jboss.messaging.util.StreamUtils;
-
-/**
- * 
- * A MoveTransactionRequest
- *
- * @author <a href="mailto:tim.fox at jboss.com">Tim Fox</a>
- * @version <tt>$Revision: 1.1 $</tt>
- *
- * $Id$
- *
- */
-class MoveTransactionRequest extends TransactionRequest
-{
-   static final int TYPE = 5;
-   
-   private String queueName;
-   
-   private List messages;
-   
-   MoveTransactionRequest()
-   {      
-   }
-   
-   MoveTransactionRequest(String nodeId, long txId, List messages, String queueName)
-   {
-      super(nodeId, txId, true);
-      
-      this.messages = messages;
-      
-      this.queueName = queueName;
-   }
-   
-   MoveTransactionRequest(String nodeId, long txId)
-   {
-      super(nodeId, txId, false);
-   }
-   
-   public void commit(PostOfficeInternal office) throws Exception
-   {
-      office.addToQueue(queueName, messages);  
-   }
-   
-   public byte getType()
-   {
-      return TYPE;
-   }
-
-   public void read(DataInputStream in) throws Exception
-   {
-      super.read(in);
-      
-      queueName = in.readUTF();
-      
-      int b = in.readByte();
-      
-      if (b == StreamUtils.NULL)
-      {
-         messages = null;
-      }
-      else
-      {
-         int size = in.readInt();
-         
-         messages = new ArrayList(size);
-         
-         for (int i = 0; i < size; i++)
-         {
-            byte type = in.readByte();
-            Message msg = MessageFactory.createMessage(type);
-            msg.read(in);
-            messages.add(msg);
-         }
-      }
-   }
-
-   public void write(DataOutputStream out) throws Exception
-   {
-      super.write(out);
-      
-      out.writeUTF(queueName);
-      
-      if (messages == null)
-      {
-         out.writeByte(StreamUtils.NULL);
-      }
-      else
-      {
-         out.writeByte(StreamUtils.LIST);
-         
-         out.writeInt(messages.size());
-         
-         Iterator iter = messages.iterator();
-         while (iter.hasNext())
-         {
-            Message message = (Message)iter.next();
-            out.writeByte(message.getType());      
-            message.write(out);
-         }
-      }
-      
-      
-   }
-}
-
-

Copied: trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/NullMessagePullPolicy.java (from rev 1321, trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/StandardRedistributionPolicy.java)
===================================================================
--- trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/StandardRedistributionPolicy.java	2006-09-19 19:17:09 UTC (rev 1321)
+++ trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/NullMessagePullPolicy.java	2006-09-21 09:41:24 UTC (rev 1336)
@@ -0,0 +1,47 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.messaging.core.plugin.postoffice.cluster;
+
+import java.util.List;
+
+/**
+ * 
+ * A NullMessagePullPolicy
+ *
+ * @author <a href="mailto:tim.fox at jboss.com">Tim Fox</a>
+ * @version <tt>$Revision: 1.1 $</tt>
+ *
+ * $Id$
+ *
+ */
+public class NullMessagePullPolicy implements MessagePullPolicy
+{
+   
+   public NullMessagePullPolicy()
+   {
+   }
+
+   public RemoteQueueStub chooseQueue(List queues)
+   {      
+      return null;
+   }
+}

Modified: trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/PostOfficeInternal.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/PostOfficeInternal.java	2006-09-21 00:50:21 UTC (rev 1335)
+++ trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/PostOfficeInternal.java	2006-09-21 09:41:24 UTC (rev 1336)
@@ -59,17 +59,22 @@
    
    void asyncSendRequest(ClusterRequest request) throws Exception;
    
-   void asyncSendRequest(ClusterRequest request, String nodeId) throws Exception;
+   void asyncSendRequest(ClusterRequest request, Address address) throws Exception;
    
-   void holdTransaction(TransactionId id, ClusterTransaction tx) throws Exception;
+   void holdTransaction(TransactionId id, ClusterTransaction tx) throws Throwable;
    
-   void commitTransaction(TransactionId id) throws Exception;
+   void commitTransaction(TransactionId id) throws Throwable;
    
-   void check(String nodeId) throws Exception;
+   void check(String nodeId) throws Throwable;
    
-   void calculateRedistribution() throws Throwable;
-   
    void updateQueueStats(String nodeId, List stats) throws Exception;
    
    void sendStats() throws Exception;
+   
+   boolean referenceExistsInStorage(long channelID, long messageID) throws Exception;
+   
+   List getDeliveries(String queueName, int numMessages) throws Exception;
+   
+   void pullMessages(ClusteredQueue localQueue, ClusteredQueue remoteQueue) throws Throwable;
+
 }

Added: trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/PullMessagesRequest.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/PullMessagesRequest.java	2006-09-21 00:50:21 UTC (rev 1335)
+++ trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/PullMessagesRequest.java	2006-09-21 09:41:24 UTC (rev 1336)
@@ -0,0 +1,200 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.messaging.core.plugin.postoffice.cluster;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.jboss.messaging.core.Delivery;
+
+/**
+ * A PullMessagesRequest
+ *
+ * @author <a href="mailto:tim.fox at jboss.com">Tim Fox</a>
+ * @version <tt>$Revision: 1.1 $</tt>
+ *
+ * $Id$
+ *
+ */
+public class PullMessagesRequest extends TransactionRequest implements ClusterTransaction
+{
+   private String queueName;
+   
+   private int numMessages;
+   
+   private List reliableDels;
+   
+   static final int TYPE = 5;
+         
+   PullMessagesRequest()
+   {      
+   }
+        
+   PullMessagesRequest(String nodeId, long txId, long checkChannelID, String queueName, int numMessages)
+   {
+      super(nodeId, txId, true, checkChannelID);
+      
+      this.queueName = queueName;
+      
+      this.numMessages = numMessages;
+   }
+   
+   PullMessagesRequest(String nodeId, long txId)
+   {
+      super(nodeId, txId, false);
+   }
+
+   Object execute(PostOfficeInternal office) throws Throwable
+   {
+      TransactionId id = new TransactionId(nodeId, txId);
+      
+      if (hold)
+      {         
+         List dels = office.getDeliveries(queueName, numMessages);
+         
+         PullMessagesResponse response = new PullMessagesResponse(dels.size());
+         
+         if (!dels.isEmpty())
+         {
+            Iterator iter = dels.iterator();
+            
+            Delivery del = (Delivery)iter.next();
+            
+            if (del.getReference().isReliable())
+            {
+               //Add it to internal list
+               if (reliableDels == null)
+               {
+                  reliableDels  = new ArrayList();
+                  
+                  reliableDels.add(del);
+               }
+            }
+            else
+            {
+               //We can ack it now
+               del.acknowledge(null);
+            }
+            
+            response.addMessage(del.getReference().getMessage());
+         }
+         
+         if (reliableDels != null)
+         {
+            //Add this to the holding area
+            office.holdTransaction(id, this);
+         }
+         
+         return response;
+      }
+      else
+      {
+         office.commitTransaction(id);
+         
+         return null;
+      }
+   }
+
+   byte getType()
+   {
+      return TYPE;
+   }
+
+   public boolean check(PostOfficeInternal office) throws Exception
+   {
+      // If the messages DON'T exist in the database then we should commit the transaction
+      // Since the acks have already been processed persistently
+      
+      // otherwise we should roll it back
+      
+      Iterator iter = reliableDels.iterator();
+      
+      //We only need to check one of them since they would all have been acked in a tx      
+      
+      Delivery del = (Delivery)iter.next();
+      
+      //We store the channelID of one of the channels that the message was persisted in
+      //it doesn't matter which one since they were all inserted in the same tx
+      
+      if (office.referenceExistsInStorage(checkChannelID, del.getReference().getMessageID()))
+      {
+         //We should rollback
+         return false;
+      }
+      else
+      {
+         //We should commit
+         return true;
+      }
+   }
+
+   public void commit(PostOfficeInternal office) throws Throwable
+   {
+      //We need to ack the deliveries
+      
+      Iterator iter = reliableDels.iterator();
+      
+      while (iter.hasNext())
+      {
+         Delivery del = (Delivery)iter.next();
+         
+         //We need to ack them in memory only
+         //since they would have been acked on the pulling node
+         LocalClusteredQueue queue = (LocalClusteredQueue)del.getObserver();
+         
+         queue.acknowledgeFromCluster(del);
+      }
+   }
+
+   public void rollback(PostOfficeInternal office) throws Throwable
+   {
+      //We need to cancel the deliveries
+      
+      Iterator iter = reliableDels.iterator();
+      
+      while (iter.hasNext())
+      {
+         Delivery del = (Delivery)iter.next();
+         
+         del.cancel();
+      }      
+   }
+   
+   public void read(DataInputStream in) throws Exception
+   {
+      queueName = in.readUTF();
+      
+      numMessages = in.readInt();
+   }
+
+   public void write(DataOutputStream out) throws Exception
+   {
+      out.writeUTF(queueName);
+      
+      out.writeInt(numMessages);
+   }
+
+
+}

Added: trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/PullMessagesResponse.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/PullMessagesResponse.java	2006-09-21 00:50:21 UTC (rev 1335)
+++ trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/PullMessagesResponse.java	2006-09-21 09:41:24 UTC (rev 1336)
@@ -0,0 +1,90 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2005, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.jboss.messaging.core.plugin.postoffice.cluster;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.jboss.messaging.core.Message;
+import org.jboss.messaging.core.message.MessageFactory;
+import org.jboss.messaging.util.Streamable;
+
+/**
+ * A PullMessagesResponse
+ *
+ * @author <a href="mailto:tim.fox at jboss.com">Tim Fox</a>
+ * @version <tt>$Revision: 1.1 $</tt>
+ *
+ * $Id$
+ *
+ */
+public class PullMessagesResponse implements Streamable
+{
+   private List messages;
+   
+   PullMessagesResponse(int size)
+   {
+      messages = new ArrayList(size);
+   }
+   
+   void addMessage(Message msg)
+   {
+      messages.add(msg);
+   }
+
+   public void read(DataInputStream in) throws Exception
+   {
+      int num = in.readInt();
+      
+      messages = new ArrayList(num);
+      
+      for (int i = 0; i < num; i++)
+      {
+         byte type = in.readByte();
+         
+         Message msg = MessageFactory.createMessage(type);
+         
+         msg.read(in);
+         
+         messages.add(msg);
+      }
+   }
+
+   public void write(DataOutputStream out) throws Exception
+   {
+      out.writeInt(messages.size());
+      
+      Iterator iter = messages.iterator();
+      
+      while (iter.hasNext())
+      {
+         Message msg = (Message)iter.next();
+         
+         out.writeByte(msg.getType());
+         
+         msg.write(out);
+      }   
+   }
+}

Modified: trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/QueueStats.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/QueueStats.java	2006-09-21 00:50:21 UTC (rev 1335)
+++ trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/QueueStats.java	2006-09-21 09:41:24 UTC (rev 1336)
@@ -39,9 +39,9 @@
 {
    private String queueName;
    
-   private float addRate;
+  // private float addRate;
    
-   private float consumeRate;
+  // private float consumeRate;
    
    private int messageCount;
 
@@ -49,26 +49,27 @@
    {      
    }
    
-   QueueStats(String queueName, float addRate, float consumeRate, int messageCount)
+   //QueueStats(String queueName, float addRate, float consumeRate, int messageCount)
+   QueueStats(String queueName, int messageCount)
    {
       this.queueName = queueName;
       
-      this.addRate = addRate;
+     // this.addRate = addRate;
       
-      this.consumeRate = consumeRate;
+     // this.consumeRate = consumeRate;
       
       this.messageCount = messageCount;
    }
 
-   float getAddRate()
-   {
-      return addRate;
-   }
-   
-   float getConsumeRate()
-   {
-      return consumeRate;
-   }
+//   float getAddRate()
+//   {
+//      return addRate;
+//   }
+//   
+//   float getConsumeRate()
+//   {
+//      return consumeRate;
+//   }
 
    int getMessageCount()
    {
@@ -84,10 +85,10 @@
    {
       queueName = in.readUTF();
       
-      addRate = in.readFloat();
+//      addRate = in.readFloat();
+//      
+//      consumeRate = in.readFloat();
       
-      consumeRate = in.readFloat();
-      
       messageCount = in.readInt();
    }
 
@@ -95,10 +96,10 @@
    {
       out.writeUTF(queueName);
       
-      out.writeFloat(addRate);
+//      out.writeFloat(addRate);
+//      
+//      out.writeFloat(consumeRate);
       
-      out.writeFloat(consumeRate);
-      
       out.writeInt(messageCount);
    }      
 }

Modified: trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/QueueStatsRequest.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/QueueStatsRequest.java	2006-09-21 00:50:21 UTC (rev 1335)
+++ trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/QueueStatsRequest.java	2006-09-21 09:41:24 UTC (rev 1336)
@@ -56,12 +56,14 @@
       this.queueStats = stats;
    }
 
-   public void execute(PostOfficeInternal office) throws Exception
+   Object execute(PostOfficeInternal office) throws Exception
    {
       office.updateQueueStats(nodeId, queueStats);
+      
+      return null;
    }
    
-   public byte getType()
+   byte getType()
    {
       return TYPE;
    }

Deleted: trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/RedistributionOrder.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/RedistributionOrder.java	2006-09-21 00:50:21 UTC (rev 1335)
+++ trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/RedistributionOrder.java	2006-09-21 09:41:24 UTC (rev 1336)
@@ -1,67 +0,0 @@
-/*
- * JBoss, Home of Professional Open Source
- * Copyright 2005, JBoss Inc., and individual contributors as indicated
- * by the @authors tag. See the copyright.txt in the distribution for a
- * full listing of individual contributors.
- *
- * This is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
- */
-package org.jboss.messaging.core.plugin.postoffice.cluster;
-
-import org.jboss.messaging.core.Queue;
-
-/**
- * 
- * A RedistributionOrder
- *
- * @author <a href="mailto:tim.fox at jboss.com">Tim Fox</a>
- * @version <tt>$Revision: 1.1 $</tt>
- *
- * $Id$
- *
- */
-public class RedistributionOrder
-{
-   private int numberOfMessages;
-   
-   private Queue queue;
-   
-   private String destinationNodeId;
-
-   public RedistributionOrder(int numberOfMessages, Queue queue, String destinationNodeId)
-   {
-      this.numberOfMessages = numberOfMessages;
-      
-      this.queue = queue;
-      
-      this.destinationNodeId = destinationNodeId;
-   }
-
-   public String getDestinationNodeId()
-   {
-      return destinationNodeId;
-   }
-
-   public int getNumberOfMessages()
-   {
-      return numberOfMessages;
-   }
-
-   public Queue getQueue()
-   {
-      return queue;
-   }
-}

Deleted: trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/RedistributionPolicy.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/RedistributionPolicy.java	2006-09-21 00:50:21 UTC (rev 1335)
+++ trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/RedistributionPolicy.java	2006-09-21 09:41:24 UTC (rev 1336)
@@ -1,38 +0,0 @@
-/*
- * JBoss, Home of Professional Open Source
- * Copyright 2005, JBoss Inc., and individual contributors as indicated
- * by the @authors tag. See the copyright.txt in the distribution for a
- * full listing of individual contributors.
- *
- * This is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
- */
-package org.jboss.messaging.core.plugin.postoffice.cluster;
-
-import java.util.List;
-
-/**
- * A RedistributionPolicy
- *
- * @author <a href="mailto:tim.fox at jboss.com">Tim Fox</a>
- * @version <tt>$Revision: 1.1 $</tt>
- *
- * $Id$
- *
- */
-public interface RedistributionPolicy
-{
-   RedistributionOrder calculate(List queues);
-}

Modified: trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/RemoteQueueStub.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/RemoteQueueStub.java	2006-09-21 00:50:21 UTC (rev 1335)
+++ trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/RemoteQueueStub.java	2006-09-21 09:41:24 UTC (rev 1336)
@@ -201,7 +201,10 @@
 
    public void acknowledge(Delivery d, Transaction tx) throws Throwable
    {
-      throw new UnsupportedOperationException();
+      if (recoverable && d.getReference().isReliable())
+      {
+         pm.removeReference(this.id, d.getReference(), tx);
+      }
    }
 
    public void cancel(Delivery d) throws Throwable

Deleted: trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/RouterFactory.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/RouterFactory.java	2006-09-21 00:50:21 UTC (rev 1335)
+++ trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/RouterFactory.java	2006-09-21 09:41:24 UTC (rev 1336)
@@ -1,38 +0,0 @@
-/*
- * JBoss, Home of Professional Open Source
- * Copyright 2005, JBoss Inc., and individual contributors as indicated
- * by the @authors tag. See the copyright.txt in the distribution for a
- * full listing of individual contributors.
- *
- * This is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
- */
-package org.jboss.messaging.core.plugin.postoffice.cluster;
-
-import org.jboss.messaging.core.Router;
-
-/**
- * A RouterFactory
- *
- * @author <a href="mailto:tim.fox at jboss.com">Tim Fox</a>
- * @version <tt>$Revision: 1.1 $</tt>
- *
- * $Id$
- *
- */
-public interface RouterFactory
-{
-   Router createRouter();
-}

Modified: trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/SendNodeIdRequest.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/SendNodeIdRequest.java	2006-09-21 00:50:21 UTC (rev 1335)
+++ trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/SendNodeIdRequest.java	2006-09-21 09:41:24 UTC (rev 1336)
@@ -56,12 +56,14 @@
       this.nodeId = nodeId;      
    }
    
-   public void execute(PostOfficeInternal office) throws Exception
+   Object execute(PostOfficeInternal office) throws Exception
    {
       office.handleAddressNodeMapping(address, nodeId);
+      
+      return null;
    }
    
-   public byte getType()
+   byte getType()
    {
       return TYPE;
    }

Modified: trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/SendTransactionRequest.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/SendTransactionRequest.java	2006-09-21 00:50:21 UTC (rev 1335)
+++ trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/SendTransactionRequest.java	2006-09-21 09:41:24 UTC (rev 1336)
@@ -45,14 +45,14 @@
    static final int TYPE = 8;
    
    private List messageHolders;
-   
+      
    SendTransactionRequest()
    {      
    }
         
-   SendTransactionRequest(String nodeId, long txId, List messageHolders)
+   SendTransactionRequest(String nodeId, long txId, List messageHolders, long channelID)
    {
-      super(nodeId, txId, true);
+      super(nodeId, txId, true, channelID);
       
       this.messageHolders = messageHolders;  
    }
@@ -74,6 +74,37 @@
       }
    }
    
+   public boolean check(PostOfficeInternal office) throws Exception
+   {
+      //If the messages exist in the database then we should commit the transaction
+      //otherwise we should roll it back
+      
+      Iterator iter = messageHolders.iterator();
+      
+      //We only need to check that one of the refs made it to the database - the refs would have
+      //been inserted into the db transactionally, so either they're all there or none are
+      MessageHolder holder = (MessageHolder)iter.next();
+      
+      //We store the channelID of one of the channels that the message was persisted in
+      //it doesn't matter which one since they were all inserted in the same tx
+      
+      if (office.referenceExistsInStorage(checkChannelID, holder.getMessage().getMessageID()))
+      {
+         //We can commit
+         return true;
+      }
+      else
+      {
+         //We should rollback
+         return false;
+      }
+   }
+
+   public void rollback(PostOfficeInternal office) throws Exception
+   {
+      //NOOP
+   }  
+   
    public byte getType()
    {
       return TYPE;
@@ -119,6 +150,6 @@
       {
          out.writeByte(StreamUtils.NULL);
       }      
-   }   
+   } 
 }
 

Deleted: trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/StandardRedistributionPolicy.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/StandardRedistributionPolicy.java	2006-09-21 00:50:21 UTC (rev 1335)
+++ trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/StandardRedistributionPolicy.java	2006-09-21 09:41:24 UTC (rev 1336)
@@ -1,82 +0,0 @@
-/*
- * JBoss, Home of Professional Open Source
- * Copyright 2005, JBoss Inc., and individual contributors as indicated
- * by the @authors tag. See the copyright.txt in the distribution for a
- * full listing of individual contributors.
- *
- * This is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
- */
-package org.jboss.messaging.core.plugin.postoffice.cluster;
-
-import java.util.Iterator;
-import java.util.List;
-
-/**
- * 
- * A StandardRedistributionPolicy
- * 
- * In this simple redistribution policy, we only move messages from a particular local queue if 
- *
- * @author <a href="mailto:tim.fox at jboss.com">Tim Fox</a>
- * @version <tt>$Revision: 1.1 $</tt>
- *
- * $Id$
- *
- */
-public class StandardRedistributionPolicy implements RedistributionPolicy
-{
-   private String localNodeId;
-   
-   public StandardRedistributionPolicy(String localNodeId)
-   {
-      this.localNodeId = localNodeId;
-   }
-
-   public RedistributionOrder calculate(List queues)
-   {
-      Iterator iter = queues.iterator();
-      
-      ClusteredQueue localQueue = null;
-      
-      while (iter.hasNext())
-      {
-         ClusteredQueue queue = (ClusteredQueue)iter.next();
-         
-         if (queue.isLocal())
-         {
-            localQueue = queue;
-            
-            break;
-         }
-      }
-      
-      if (localQueue == null)
-      {
-         return null;
-      }
-      
-      QueueStats stats = localQueue.getStats();
-      
-      if (stats == null)
-      {
-         //We have not given the queue long enough to produce stats - so we can't move anything
-         //now
-         return null;
-      }
-
-      return null;
-   }
-}

Modified: trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/TransactionRequest.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/TransactionRequest.java	2006-09-21 00:50:21 UTC (rev 1335)
+++ trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/TransactionRequest.java	2006-09-21 09:41:24 UTC (rev 1336)
@@ -37,16 +37,29 @@
  */
 abstract class TransactionRequest extends ClusterRequest implements ClusterTransaction
 {
-   private String nodeId;
+   protected String nodeId;
    
-   private long txId;
+   protected long txId;
  
-   private boolean hold;
+   protected boolean hold;
    
+   protected long checkChannelID;
+   
    TransactionRequest()
    {      
    }
       
+   TransactionRequest(String nodeId, long txId, boolean hold, long checkChannelID)
+   {
+      this.nodeId = nodeId;
+      
+      this.txId= txId;
+
+      this.hold = hold;
+      
+      this.checkChannelID = checkChannelID;
+   }
+   
    TransactionRequest(String nodeId, long txId, boolean hold)
    {
       this.nodeId = nodeId;
@@ -56,7 +69,7 @@
       this.hold = hold;
    }
    
-   public void execute(PostOfficeInternal office) throws Exception
+   Object execute(PostOfficeInternal office) throws Throwable
    { 
       TransactionId id = new TransactionId(nodeId, txId);
       
@@ -68,6 +81,7 @@
       {
          office.commitTransaction(id);
       }
+      return null;
    }   
    
    public void read(DataInputStream in) throws Exception

Modified: trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/UnbindRequest.java
===================================================================
--- trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/UnbindRequest.java	2006-09-21 00:50:21 UTC (rev 1335)
+++ trunk/src/main/org/jboss/messaging/core/plugin/postoffice/cluster/UnbindRequest.java	2006-09-21 09:41:24 UTC (rev 1336)
@@ -52,12 +52,14 @@
       this.queueName = queueName;
    }
 
-   public void execute(PostOfficeInternal office) throws Exception
+   Object execute(PostOfficeInternal office) throws Exception
    {
       office.removeBindingFromCluster(nodeId, queueName);
+      
+      return null;
    }
    
-   public byte getType()
+   byte getType()
    {
       return TYPE;
    }

Modified: trunk/tests/src/org/jboss/test/messaging/core/plugin/postoffice/cluster/ClusteredPostOfficeTest.java
===================================================================
--- trunk/tests/src/org/jboss/test/messaging/core/plugin/postoffice/cluster/ClusteredPostOfficeTest.java	2006-09-21 00:50:21 UTC (rev 1335)
+++ trunk/tests/src/org/jboss/test/messaging/core/plugin/postoffice/cluster/ClusteredPostOfficeTest.java	2006-09-21 09:41:24 UTC (rev 1336)
@@ -32,10 +32,11 @@
 import org.jboss.messaging.core.plugin.contract.ClusteredPostOffice;
 import org.jboss.messaging.core.plugin.postoffice.Binding;
 import org.jboss.messaging.core.plugin.postoffice.cluster.ClusterRouterFactory;
-import org.jboss.messaging.core.plugin.postoffice.cluster.ClusteredPostOfficeImpl;
+import org.jboss.messaging.core.plugin.postoffice.cluster.DefaultClusteredPostOffice;
 import org.jboss.messaging.core.plugin.postoffice.cluster.FavourLocalRouterFactory;
 import org.jboss.messaging.core.plugin.postoffice.cluster.LocalClusteredQueue;
-import org.jboss.messaging.core.plugin.postoffice.cluster.RedistributionPolicy;
+import org.jboss.messaging.core.plugin.postoffice.cluster.MessagePullPolicy;
+import org.jboss.messaging.core.plugin.postoffice.cluster.NullMessagePullPolicy;
 import org.jboss.messaging.core.tx.Transaction;
 import org.jboss.test.messaging.core.SimpleFilter;
 import org.jboss.test.messaging.core.SimpleFilterFactory;
@@ -99,11 +100,11 @@
          
          //Add a couple of bindings
          
-         LocalClusteredQueue queue1 = new LocalClusteredQueue("node1", "sub1", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);
+         LocalClusteredQueue queue1 = new LocalClusteredQueue(office1, "node1", "sub1", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);
          
          Binding binding1 =
             office1.bindClusteredQueue("topic1", queue1);
-         LocalClusteredQueue queue2 = new LocalClusteredQueue("node1", "sub2", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);
+         LocalClusteredQueue queue2 = new LocalClusteredQueue(office1, "node1", "sub2", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);
 
          Binding binding2 =
             office1.bindClusteredQueue("topic1", queue2);
@@ -122,7 +123,7 @@
          
          //Add another binding on node 2
          
-         LocalClusteredQueue queue3 = new LocalClusteredQueue("node2", "sub3", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);
+         LocalClusteredQueue queue3 = new LocalClusteredQueue(office2, "node2", "sub3", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);
 
          Binding binding3 =
             office2.bindClusteredQueue("topic1", queue3);
@@ -149,7 +150,7 @@
 
          //Add another binding on node 1
          
-         LocalClusteredQueue queue4 = new LocalClusteredQueue("node2", "sub4", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);
+         LocalClusteredQueue queue4 = new LocalClusteredQueue(office2, "node2", "sub4", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);
          Binding binding4 =
             office2.bindClusteredQueue("topic1", queue4);
          
@@ -213,7 +214,7 @@
          
          //Add another binding on node 3
                   
-         LocalClusteredQueue queue5 = new LocalClusteredQueue("node3", "sub5", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);
+         LocalClusteredQueue queue5 = new LocalClusteredQueue(office3, "node3", "sub5", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);
          
          Binding binding5 =
             office3.bindClusteredQueue("topic1", queue5);
@@ -249,12 +250,12 @@
          
          //Add a durable and a non durable binding on node 1
          
-         LocalClusteredQueue queue6 = new LocalClusteredQueue("node1", "sub6", im.getId(), ms, pm, true, true, (QueuedExecutor)pool.get(), null);         
+         LocalClusteredQueue queue6 = new LocalClusteredQueue(office1, "node1", "sub6", im.getId(), ms, pm, true, true, (QueuedExecutor)pool.get(), null);         
          
          Binding binding6 =
             office1.bindClusteredQueue("topic1", queue6);
          
-         LocalClusteredQueue queue7 = new LocalClusteredQueue("node1", "sub7", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);         
+         LocalClusteredQueue queue7 = new LocalClusteredQueue(office1, "node1", "sub7", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);         
          
          Binding binding7 =
             office1.bindClusteredQueue("topic1", queue7);
@@ -475,15 +476,15 @@
          
          office2 = createClusteredPostOffice("node2", "testgroup");
          
-         LocalClusteredQueue queue1 = new LocalClusteredQueue("node1", "queue1", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);
+         LocalClusteredQueue queue1 = new LocalClusteredQueue(office1, "node1", "queue1", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);
          
          Binding binding1 = office1.bindClusteredQueue("queue1", queue1);
          
-         LocalClusteredQueue queue2 = new LocalClusteredQueue("node2", "queue1", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);
+         LocalClusteredQueue queue2 = new LocalClusteredQueue(office2, "node2", "queue1", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);
          
          Binding binding2 = office2.bindClusteredQueue("queue1", queue2);
                   
-         LocalClusteredQueue queue3 = new LocalClusteredQueue("node1", "queue1", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);
+         LocalClusteredQueue queue3 = new LocalClusteredQueue(office1, "node1", "queue1", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);
          
          try
          {
@@ -494,7 +495,7 @@
          {
             //Ok
          }
-         LocalClusteredQueue queue4 = new LocalClusteredQueue("node2", "queue1", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);
+         LocalClusteredQueue queue4 = new LocalClusteredQueue(office2, "node2", "queue1", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);
          
          try
          {
@@ -510,7 +511,7 @@
          
          office2.unbindClusteredQueue("queue1");
          
-         LocalClusteredQueue queue5 = new LocalClusteredQueue("node1", "queue1", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);
+         LocalClusteredQueue queue5 = new LocalClusteredQueue(office1, "node1", "queue1", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);
          
          Binding binding5 = office1.bindClusteredQueue("queue1", queue5);
          
@@ -589,15 +590,15 @@
          SimpleFilter filter1 = new SimpleFilter(2);
          SimpleFilter filter2 = new SimpleFilter(3);
       
-         LocalClusteredQueue queue1 = new LocalClusteredQueue("node1", "queue1", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), filter1);         
+         LocalClusteredQueue queue1 = new LocalClusteredQueue(office1, "node1", "queue1", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), filter1);         
          Binding binding1 =
             office1.bindClusteredQueue("topic1", queue1);
          
-         LocalClusteredQueue queue2 = new LocalClusteredQueue("node2", "queue2", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), filter2);         
+         LocalClusteredQueue queue2 = new LocalClusteredQueue(office2, "node2", "queue2", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), filter2);         
          Binding binding2 =
             office2.bindClusteredQueue("topic1", queue2);
          
-         LocalClusteredQueue queue3 = new LocalClusteredQueue("node2", "queue3", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);         
+         LocalClusteredQueue queue3 = new LocalClusteredQueue(office2, "node2", "queue3", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);         
          Binding binding3 =
             office2.bindClusteredQueue("topic1", queue3);   
          
@@ -691,52 +692,52 @@
          LocalClusteredQueue[] queues = new LocalClusteredQueue[16];
          Binding[] bindings = new Binding[16];
          
-         queues[0] = new LocalClusteredQueue("node1", "sub1", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);         
+         queues[0] = new LocalClusteredQueue(office1, "node1", "sub1", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);         
          bindings[0] = office1.bindClusteredQueue("topic1", queues[0]);
          
-         queues[1] = new LocalClusteredQueue("node1", "sub2", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);         
+         queues[1] = new LocalClusteredQueue(office1, "node1", "sub2", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);         
          bindings[1] = office1.bindClusteredQueue("topic1", queues[1]);
          
-         queues[2] = new LocalClusteredQueue("node2", "sub3", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);         
+         queues[2] = new LocalClusteredQueue(office2, "node2", "sub3", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);         
          bindings[2] = office2.bindClusteredQueue("topic1", queues[2]);
          
-         queues[3] = new LocalClusteredQueue("node2", "sub4", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);         
+         queues[3] = new LocalClusteredQueue(office2, "node2", "sub4", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);         
          bindings[3] = office2.bindClusteredQueue("topic1", queues[3]);
          
-         queues[4] = new LocalClusteredQueue("node2", "sub5", im.getId(), ms, pm, true, true, (QueuedExecutor)pool.get(), null);         
+         queues[4] = new LocalClusteredQueue(office2, "node2", "sub5", im.getId(), ms, pm, true, true, (QueuedExecutor)pool.get(), null);         
          bindings[4] = office2.bindClusteredQueue("topic1", queues[4]);
          
-         queues[5] = new LocalClusteredQueue("node1", "sub6", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);         
+         queues[5] = new LocalClusteredQueue(office1, "node1", "sub6", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);         
          bindings[5] = office1.bindClusteredQueue("topic1", queues[5]);
          
-         queues[6] = new LocalClusteredQueue("node1", "sub7", im.getId(), ms, pm, true, true, (QueuedExecutor)pool.get(), null);         
+         queues[6] = new LocalClusteredQueue(office1, "node1", "sub7", im.getId(), ms, pm, true, true, (QueuedExecutor)pool.get(), null);         
          bindings[6] = office1.bindClusteredQueue("topic1", queues[6]);
          
-         queues[7] = new LocalClusteredQueue("node1", "sub8", im.getId(), ms, pm, true, true, (QueuedExecutor)pool.get(), null);         
+         queues[7] = new LocalClusteredQueue(office1, "node1", "sub8", im.getId(), ms, pm, true, true, (QueuedExecutor)pool.get(), null);         
          bindings[7] = office1.bindClusteredQueue("topic1", queues[7]);
          
-         queues[8] = new LocalClusteredQueue("node1", "sub9", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);         
+         queues[8] = new LocalClusteredQueue(office1, "node1", "sub9", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);         
          bindings[8] = office1.bindClusteredQueue("topic2", queues[8]);
          
-         queues[9] = new LocalClusteredQueue("node1", "sub10", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);         
+         queues[9] = new LocalClusteredQueue(office1, "node1", "sub10", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);         
          bindings[9] = office1.bindClusteredQueue("topic2", queues[9]);
          
-         queues[10] = new LocalClusteredQueue("node2", "sub11", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);         
+         queues[10] = new LocalClusteredQueue(office2, "node2", "sub11", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);         
          bindings[10] = office2.bindClusteredQueue("topic2", queues[10]);
          
-         queues[11] = new LocalClusteredQueue("node2", "sub12", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);         
+         queues[11] = new LocalClusteredQueue(office2, "node2", "sub12", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);         
          bindings[11] = office2.bindClusteredQueue("topic2", queues[11]);
          
-         queues[12] = new LocalClusteredQueue("node2", "sub13", im.getId(), ms, pm, true, true, (QueuedExecutor)pool.get(), null);         
+         queues[12] = new LocalClusteredQueue(office2, "node2", "sub13", im.getId(), ms, pm, true, true, (QueuedExecutor)pool.get(), null);         
          bindings[12] = office2.bindClusteredQueue("topic2", queues[12]);
          
-         queues[13] = new LocalClusteredQueue("node1", "sub14", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);         
+         queues[13] = new LocalClusteredQueue(office1, "node1", "sub14", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);         
          bindings[13] = office1.bindClusteredQueue("topic2", queues[13]);
          
-         queues[14] = new LocalClusteredQueue("node1", "sub15", im.getId(), ms, pm, true, true, (QueuedExecutor)pool.get(), null);         
+         queues[14] = new LocalClusteredQueue(office1, "node1", "sub15", im.getId(), ms, pm, true, true, (QueuedExecutor)pool.get(), null);         
          bindings[14] = office1.bindClusteredQueue("topic2", queues[14]);
          
-         queues[15] = new LocalClusteredQueue("node1", "sub16", im.getId(), ms, pm, true, true, (QueuedExecutor)pool.get(), null);         
+         queues[15] = new LocalClusteredQueue(office1, "node1", "sub16", im.getId(), ms, pm, true, true, (QueuedExecutor)pool.get(), null);         
          bindings[15] = office1.bindClusteredQueue("topic2", queues[15]);
        
          SimpleReceiver[] receivers = new SimpleReceiver[16];
@@ -860,52 +861,52 @@
          LocalClusteredQueue[] queues = new LocalClusteredQueue[16];
          Binding[] bindings = new Binding[16];
          
-         queues[0] = new LocalClusteredQueue("node1", "sub1", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);         
+         queues[0] = new LocalClusteredQueue(office1, "node1", "sub1", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);         
          bindings[0] = office1.bindClusteredQueue("topic1", queues[0]);
          
-         queues[1] = new LocalClusteredQueue("node1", "sub2", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);         
+         queues[1] = new LocalClusteredQueue(office1, "node1", "sub2", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);         
          bindings[1] = office1.bindClusteredQueue("topic1", queues[1]);
          
-         queues[2] = new LocalClusteredQueue("node2", "sub3", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);         
+         queues[2] = new LocalClusteredQueue(office2, "node2", "sub3", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);         
          bindings[2] = office2.bindClusteredQueue("topic1", queues[2]);
          
-         queues[3] = new LocalClusteredQueue("node2", "sub4", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);         
+         queues[3] = new LocalClusteredQueue(office2, "node2", "sub4", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);         
          bindings[3] = office2.bindClusteredQueue("topic1", queues[3]);
          
-         queues[4] = new LocalClusteredQueue("node2", "sub5", im.getId(), ms, pm, true, true, (QueuedExecutor)pool.get(), null);         
+         queues[4] = new LocalClusteredQueue(office2, "node2", "sub5", im.getId(), ms, pm, true, true, (QueuedExecutor)pool.get(), null);         
          bindings[4] = office2.bindClusteredQueue("topic1", queues[4]);
          
-         queues[5] = new LocalClusteredQueue("node1", "sub6", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);         
+         queues[5] = new LocalClusteredQueue(office1, "node1", "sub6", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);         
          bindings[5] = office1.bindClusteredQueue("topic1", queues[5]);
          
-         queues[6] = new LocalClusteredQueue("node1", "sub7", im.getId(), ms, pm, true, true, (QueuedExecutor)pool.get(), null);         
+         queues[6] = new LocalClusteredQueue(office1, "node1", "sub7", im.getId(), ms, pm, true, true, (QueuedExecutor)pool.get(), null);         
          bindings[6] = office1.bindClusteredQueue("topic1", queues[6]);
          
-         queues[7] = new LocalClusteredQueue("node1", "sub8", im.getId(), ms, pm, true, true, (QueuedExecutor)pool.get(), null);         
+         queues[7] = new LocalClusteredQueue(office1, "node1", "sub8", im.getId(), ms, pm, true, true, (QueuedExecutor)pool.get(), null);         
          bindings[7] = office1.bindClusteredQueue("topic1", queues[7]);
          
-         queues[8] = new LocalClusteredQueue("node1", "sub9", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);         
+         queues[8] = new LocalClusteredQueue(office1, "node1", "sub9", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);         
          bindings[8] = office1.bindClusteredQueue("topic2", queues[8]);
          
-         queues[9] = new LocalClusteredQueue("node1", "sub10", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);         
+         queues[9] = new LocalClusteredQueue(office1, "node1", "sub10", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);         
          bindings[9] = office1.bindClusteredQueue("topic2", queues[9]);
          
-         queues[10] = new LocalClusteredQueue("node2", "sub11", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);         
+         queues[10] = new LocalClusteredQueue(office2, "node2", "sub11", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);         
          bindings[10] = office2.bindClusteredQueue("topic2", queues[10]);
          
-         queues[11] = new LocalClusteredQueue("node2", "sub12", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);         
+         queues[11] = new LocalClusteredQueue(office2, "node2", "sub12", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);         
          bindings[11] = office2.bindClusteredQueue("topic2", queues[11]);
          
-         queues[12] = new LocalClusteredQueue("node2", "sub13", im.getId(), ms, pm, true, true, (QueuedExecutor)pool.get(), null);         
+         queues[12] = new LocalClusteredQueue(office2, "node2", "sub13", im.getId(), ms, pm, true, true, (QueuedExecutor)pool.get(), null);         
          bindings[12] = office2.bindClusteredQueue("topic2", queues[12]);
          
-         queues[13] = new LocalClusteredQueue("node1", "sub14", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);         
+         queues[13] = new LocalClusteredQueue(office1, "node1", "sub14", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);         
          bindings[13] = office1.bindClusteredQueue("topic2", queues[13]);
          
-         queues[14] = new LocalClusteredQueue("node1", "sub15", im.getId(), ms, pm, true, true, (QueuedExecutor)pool.get(), null);         
+         queues[14] = new LocalClusteredQueue(office1, "node1", "sub15", im.getId(), ms, pm, true, true, (QueuedExecutor)pool.get(), null);         
          bindings[14] = office1.bindClusteredQueue("topic2", queues[14]);
          
-         queues[15] = new LocalClusteredQueue("node1", "sub16", im.getId(), ms, pm, true, true, (QueuedExecutor)pool.get(), null);         
+         queues[15] = new LocalClusteredQueue(office1, "node1", "sub16", im.getId(), ms, pm, true, true, (QueuedExecutor)pool.get(), null);         
          bindings[15] = office1.bindClusteredQueue("topic2", queues[15]);
 
          SimpleReceiver[] receivers = new SimpleReceiver[16];
@@ -1445,19 +1446,19 @@
    
    protected ClusteredPostOffice createClusteredPostOffice(String nodeId, String groupName) throws Exception
    {
-      RedistributionPolicy redistPolicy = new NullRedistributionPolicy();
+      MessagePullPolicy redistPolicy = new NullMessagePullPolicy();
       
       FilterFactory ff = new SimpleFilterFactory();
       
       ClusterRouterFactory rf = new FavourLocalRouterFactory();
       
-      ClusteredPostOfficeImpl postOffice = 
-         new ClusteredPostOfficeImpl(sc.getDataSource(), sc.getTransactionManager(),
+      DefaultClusteredPostOffice postOffice = 
+         new DefaultClusteredPostOffice(sc.getDataSource(), sc.getTransactionManager(),
                                  null, true, nodeId, "Clustered", ms, pm, tr, ff, pool,
                                  groupName,
                                  JGroupsUtil.getControlStackProperties(),
                                  JGroupsUtil.getDataStackProperties(),
-                                 5000, 5000, redistPolicy, 1000, rf);
+                                 5000, 5000, redistPolicy, rf, 1);
       
       postOffice.start();      
       

Modified: trunk/tests/src/org/jboss/test/messaging/core/plugin/postoffice/cluster/FavourLocalRouterTest.java
===================================================================
--- trunk/tests/src/org/jboss/test/messaging/core/plugin/postoffice/cluster/FavourLocalRouterTest.java	2006-09-21 00:50:21 UTC (rev 1335)
+++ trunk/tests/src/org/jboss/test/messaging/core/plugin/postoffice/cluster/FavourLocalRouterTest.java	2006-09-21 09:41:24 UTC (rev 1336)
@@ -29,10 +29,11 @@
 import org.jboss.messaging.core.plugin.contract.ClusteredPostOffice;
 import org.jboss.messaging.core.plugin.postoffice.Binding;
 import org.jboss.messaging.core.plugin.postoffice.cluster.ClusterRouterFactory;
-import org.jboss.messaging.core.plugin.postoffice.cluster.ClusteredPostOfficeImpl;
+import org.jboss.messaging.core.plugin.postoffice.cluster.DefaultClusteredPostOffice;
 import org.jboss.messaging.core.plugin.postoffice.cluster.FavourLocalRouterFactory;
 import org.jboss.messaging.core.plugin.postoffice.cluster.LocalClusteredQueue;
-import org.jboss.messaging.core.plugin.postoffice.cluster.RedistributionPolicy;
+import org.jboss.messaging.core.plugin.postoffice.cluster.MessagePullPolicy;
+import org.jboss.messaging.core.plugin.postoffice.cluster.NullMessagePullPolicy;
 import org.jboss.test.messaging.core.SimpleFilterFactory;
 import org.jboss.test.messaging.core.plugin.postoffice.DefaultPostOfficeTest;
 import org.jboss.test.messaging.util.CoreMessageFactory;
@@ -83,11 +84,11 @@
          
          office2 = createClusteredPostOffice("node2", "testgroup");
          
-         LocalClusteredQueue queue1 = new LocalClusteredQueue("node1", "queue1", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);
+         LocalClusteredQueue queue1 = new LocalClusteredQueue(office1, "node1", "queue1", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);
          
          Binding binding1 = office1.bindClusteredQueue("queue1", queue1);
          
-         LocalClusteredQueue queue2 = new LocalClusteredQueue("node2", "queue1", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);
+         LocalClusteredQueue queue2 = new LocalClusteredQueue(office2, "node2", "queue1", im.getId(), ms, pm, true, false, (QueuedExecutor)pool.get(), null);
          
          Binding binding2 = office2.bindClusteredQueue("queue1", queue1);
       
@@ -141,19 +142,19 @@
    
    protected ClusteredPostOffice createClusteredPostOffice(String nodeId, String groupName) throws Exception
    {
-      RedistributionPolicy redistPolicy = new NullRedistributionPolicy();
+      MessagePullPolicy redistPolicy = new NullMessagePullPolicy();
       
       FilterFactory ff = new SimpleFilterFactory();
       
       ClusterRouterFactory rf = new FavourLocalRouterFactory();
       
-      ClusteredPostOfficeImpl postOffice = 
-         new ClusteredPostOfficeImpl(sc.getDataSource(), sc.getTransactionManager(),
+      DefaultClusteredPostOffice postOffice = 
+         new DefaultClusteredPostOffice(sc.getDataSource(), sc.getTransactionManager(),
                                  null, true, nodeId, "Clustered", ms, pm, tr, ff, pool,
                                  groupName,
                                  JGroupsUtil.getControlStackProperties(),
                                  JGroupsUtil.getDataStackProperties(),
-                                 5000, 5000, redistPolicy, 1000, rf);
+                                 5000, 5000, redistPolicy, rf, 1);
       
       postOffice.start();      
       

Deleted: trunk/tests/src/org/jboss/test/messaging/core/plugin/postoffice/cluster/NullRedistributionPolicy.java
===================================================================
--- trunk/tests/src/org/jboss/test/messaging/core/plugin/postoffice/cluster/NullRedistributionPolicy.java	2006-09-21 00:50:21 UTC (rev 1335)
+++ trunk/tests/src/org/jboss/test/messaging/core/plugin/postoffice/cluster/NullRedistributionPolicy.java	2006-09-21 09:41:24 UTC (rev 1336)
@@ -1,46 +0,0 @@
-/*
- * JBoss, Home of Professional Open Source
- * Copyright 2005, JBoss Inc., and individual contributors as indicated
- * by the @authors tag. See the copyright.txt in the distribution for a
- * full listing of individual contributors.
- *
- * This is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
- */
-package org.jboss.test.messaging.core.plugin.postoffice.cluster;
-
-import java.util.List;
-
-import org.jboss.messaging.core.plugin.postoffice.cluster.RedistributionOrder;
-import org.jboss.messaging.core.plugin.postoffice.cluster.RedistributionPolicy;
-
-/**
- * A NullRedistrubtionPolicy
- *
- * @author <a href="mailto:tim.fox at jboss.com">Tim Fox</a>
- * @version <tt>$Revision: 1.1 $</tt>
- *
- * $Id$
- *
- */
-public class NullRedistributionPolicy implements RedistributionPolicy
-{
-
-   public RedistributionOrder calculate(List bindings)
-   {
-      return null;
-   }
-
-}




More information about the jboss-cvs-commits mailing list