summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Godfrey <rgodfrey@apache.org>2008-05-11 15:22:03 +0000
committerRobert Godfrey <rgodfrey@apache.org>2008-05-11 15:22:03 +0000
commit531f130c20f73c08ffc963c1e15c7b98cea05d8f (patch)
treed30c93bde8eb6515c076568102a008702c98567a
parent1a7004fd9dab8306ec31b96d9d5e2d5a44256d98 (diff)
downloadqpid-python-531f130c20f73c08ffc963c1e15c7b98cea05d8f.tar.gz
Updates on the refactoring work
git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/branches/broker-queue-refactor@655323 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java3
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java54
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessageMap.java2
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessageMapImpl.java5
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfiguration.java14
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.java5
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/exchange/Exchange.java2
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java1
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/exchange/TopicExchange.java582
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicMatcherDFAState.java59
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicMatcherResult.java2
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicParser.java171
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicWord.java24
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicWordDictionary.java2
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/filter/ArithmeticExpression.java6
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/filter/BinaryExpression.java16
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/filter/BooleanExpression.java7
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/filter/ComparisonExpression.java100
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/filter/ConstantExpression.java9
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/filter/Expression.java5
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/filter/FilterManager.java10
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/filter/FilterManagerFactory.java14
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/filter/JMSSelectorFilter.java28
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/filter/LogicExpression.java126
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/filter/MessageFilter.java5
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/filter/NoConsumerFilter.java4
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/filter/PropertyExpression.java324
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/filter/SimpleFilterManager.java15
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/filter/UnaryExpression.java322
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/filter/XPathExpression.java7
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/filter/XQueryExpression.java5
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/filter/XalanXPathEvaluator.java3
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/flow/AbstractFlowCreditManager.java5
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/flow/MessageOnlyCreditManager.java2
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/flow/Pre0_10CreditManager.java12
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/handler/BasicGetMethodHandler.java90
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeclareHandler.java4
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/queue/AMQMessage.java2
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/queue/AMQPriorityQueue.java41
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java9
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueFactory.java24
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueImpl.java1003
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/queue/ConcurrentSelectorDeliveryManager.java1098
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/queue/DeliveryAgent.java344
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/queue/Filterable.java33
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/queue/IncomingMessage.java21
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/queue/ManagedQueue.java2
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/queue/PriorityQueueList.java164
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntry.java100
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java203
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryIterator.java30
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryList.java202
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryListFactory.java26
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java389
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/queue/SimpleQueueEntryList.java178
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionManager.java47
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionSet.java278
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java6
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/store/DerbyMessageStore.java9
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/store/MemoryMessageStore.java1
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/store/StoreContext.java7
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/subscription/ClientDeliveryMethod.java29
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/subscription/RecordDeliveryMethod.java28
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription.java28
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionFactory.java12
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionFactoryImpl.java39
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java102
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java56
-rw-r--r--java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/List.java1
-rw-r--r--java/broker/src/test/java/org/apache/qpid/server/exchange/DestWildExchangeTest.java22
-rw-r--r--java/broker/src/test/java/org/apache/qpid/server/exchange/ExchangeMBeanTest.java3
-rw-r--r--java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueAlertTest.java14
-rw-r--r--java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueMBeanTest.java3
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/AMQConnection.java2
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/AMQSession.java31
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/transport/TransportConnection.java10
-rw-r--r--java/client/src/test/java/org/apache/qpid/test/unit/topic/TopicSessionTest.java7
-rw-r--r--java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketIoProcessor.java2
-rw-r--r--java/common/src/main/java/org/apache/qpid/framing/AMQShortString.java2
-rw-r--r--java/common/src/main/java/org/apache/qpid/pool/Job.java11
-rw-r--r--java/common/src/main/java/org/apache/qpid/pool/PoolingFilter.java52
-rw-r--r--java/common/src/main/java/org/apache/qpid/pool/ReadWriteJobQueue.java432
-rw-r--r--java/common/src/main/java/org/apache/qpid/pool/ReferenceCountingExecutorService.java10
-rw-r--r--java/management/eclipse-plugin/src/test/java/org/apache/qpid/management/ui/ManagementConsoleTest.java3
-rw-r--r--java/plugins/src/main/java/org/apache/qpid/extras/exchanges/diagnostic/DiagnosticExchange.java8
-rw-r--r--java/plugins/src/main/java/org/apache/qpid/extras/exchanges/example/TestExchange.java1
-rw-r--r--java/systests/pom.xml1
-rw-r--r--java/systests/src/main/java/org/apache/qpid/server/ack/TxAckTest.java13
-rw-r--r--java/systests/src/main/java/org/apache/qpid/server/exchange/AbstractHeadersExchangeTestBase.java157
-rw-r--r--java/systests/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBeanTest.java2
-rw-r--r--java/systests/src/main/java/org/apache/qpid/server/queue/AckTest.java3
-rw-r--r--java/systests/src/main/java/org/apache/qpid/server/queue/ConcurrencyTestDisabled.java267
-rw-r--r--java/systests/src/main/java/org/apache/qpid/server/queue/PriorityTest.java171
-rw-r--r--java/systests/src/main/java/org/apache/qpid/server/queue/SubscriptionManagerTest.java103
-rw-r--r--java/systests/src/main/java/org/apache/qpid/server/queue/SubscriptionSetTest.java144
-rw-r--r--java/systests/src/main/java/org/apache/qpid/server/queue/SubscriptionTestHelper.java11
-rw-r--r--java/systests/src/main/java/org/apache/qpid/server/store/SkeletonMessageStore.java1
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/client/FlowControlTest.java209
98 files changed, 3603 insertions, 4649 deletions
diff --git a/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java b/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java
index cf1af650b7..03b02707b2 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java
@@ -176,7 +176,8 @@ public class AMQBrokerManagerMBean extends AMQManagedObject implements ManagedBr
ownerShortString = new AMQShortString(owner);
}
- queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString(queueName), durable, ownerShortString, false, getVirtualHost());
+ queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString(queueName), durable, ownerShortString, false, getVirtualHost(),
+ null);
if (queue.isDurable() && !queue.isAutoDelete())
{
_messageStore.createQueue(queue);
diff --git a/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java b/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java
index 0b8913f650..deb6ac8d94 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java
@@ -44,6 +44,8 @@ import org.apache.qpid.server.queue.MessageHandleFactory;
import org.apache.qpid.server.queue.QueueEntry;
import org.apache.qpid.server.subscription.Subscription;
import org.apache.qpid.server.subscription.SubscriptionFactoryImpl;
+import org.apache.qpid.server.subscription.ClientDeliveryMethod;
+import org.apache.qpid.server.subscription.RecordDeliveryMethod;
import org.apache.qpid.server.store.MessageStore;
import org.apache.qpid.server.store.StoreContext;
import org.apache.qpid.server.txn.LocalTransactionalContext;
@@ -435,10 +437,8 @@ public class AMQChannel
}
}
- synchronized (_unacknowledgedMessageMap.getLock())
- {
- _unacknowledgedMessageMap.add(deliveryTag, entry);
- }
+ _unacknowledgedMessageMap.add(deliveryTag, entry);
+
}
private final String id = "(" + System.identityHashCode(this) + ")";
@@ -807,22 +807,7 @@ public class AMQChannel
*/
public void acknowledgeMessage(long deliveryTag, boolean multiple) throws AMQException
{
- synchronized (_unacknowledgedMessageMap.getLock())
- {
- if (_log.isDebugEnabled())
- {
- _log.debug("Unacked (PreAck) Size:" + _unacknowledgedMessageMap.size());
- }
-
- _unacknowledgedMessageMap.acknowledgeMessage(deliveryTag, multiple, _txnContext);
-
- if (_log.isDebugEnabled())
- {
- _log.debug("Unacked (PostAck) Size:" + _unacknowledgedMessageMap.size());
- }
-
- }
-
+ _unacknowledgedMessageMap.acknowledgeMessage(deliveryTag, multiple, _txnContext);
}
/**
@@ -952,4 +937,33 @@ public class AMQChannel
{
return _messageStore;
}
+
+ private final ClientDeliveryMethod _clientDeliveryMethod = new ClientDeliveryMethod()
+ {
+
+ public void deliverToClient(final Subscription sub, final QueueEntry entry, final long deliveryTag)
+ throws AMQException
+ {
+ getProtocolSession().getProtocolOutputConverter().writeDeliver(entry.getMessage(), getChannelId(), deliveryTag, sub.getConsumerTag());
+ }
+ };
+
+ public ClientDeliveryMethod getClientDeliveryMethod()
+ {
+ return _clientDeliveryMethod;
+ }
+
+ private final RecordDeliveryMethod _recordDeliveryMethod = new RecordDeliveryMethod()
+ {
+
+ public void recordMessageDelivery(final Subscription sub, final QueueEntry entry, final long deliveryTag)
+ {
+ addUnacknowledgedMessage(entry, deliveryTag, sub);
+ }
+ };
+
+ public RecordDeliveryMethod getRecordDeliveryMethod()
+ {
+ return _recordDeliveryMethod;
+ }
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessageMap.java b/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessageMap.java
index 431eeb38fc..8e5b631f96 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessageMap.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessageMap.java
@@ -45,8 +45,6 @@ public interface UnacknowledgedMessageMap
void visit(Visitor visitor) throws AMQException;
- Object getLock();
-
void add(long deliveryTag, QueueEntry message);
void collect(long deliveryTag, boolean multiple, Map<Long, QueueEntry> msgs);
diff --git a/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessageMapImpl.java b/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessageMapImpl.java
index fcce6ff44e..79208ab426 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessageMapImpl.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessageMapImpl.java
@@ -113,11 +113,6 @@ public class UnacknowledgedMessageMapImpl implements UnacknowledgedMessageMap
}
}
- public Object getLock()
- {
- return _lock;
- }
-
public void add(long deliveryTag, QueueEntry message)
{
synchronized (_lock)
diff --git a/java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfiguration.java b/java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfiguration.java
index c9598436e0..bd3e5b1f72 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfiguration.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfiguration.java
@@ -30,6 +30,7 @@ import org.apache.commons.configuration.XMLConfiguration;
import org.apache.log4j.Logger;
import org.apache.qpid.AMQException;
import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.FieldTable;
import org.apache.qpid.server.exchange.Exchange;
import org.apache.qpid.server.exchange.ExchangeRegistry;
import org.apache.qpid.server.exchange.ExchangeFactory;
@@ -177,11 +178,22 @@ public class VirtualHostConfiguration
boolean durable = queueConfiguration.getBoolean("durable" ,false);
boolean autodelete = queueConfiguration.getBoolean("autodelete", false);
String owner = queueConfiguration.getString("owner", null);
+ FieldTable arguments = null;
+ Integer priorities = queueConfiguration.getInteger("priorities", null);
+ if(priorities != null && priorities.intValue() > 1)
+ {
+ if(arguments == null)
+ {
+ arguments = new FieldTable();
+ }
+ arguments.put(new AMQShortString("x-qpid-priorities"), priorities);
+ }
+
queue = AMQQueueFactory.createAMQQueueImpl(queueName,
durable,
owner == null ? null : new AMQShortString(owner) /* These queues will have no owner */,
- autodelete /* Therefore autodelete makes no sence */, virtualHost);
+ autodelete /* Therefore autodelete makes no sence */, virtualHost, arguments);
if (queue.isDurable())
{
diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.java
index 72286cb387..8d24626b73 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.java
@@ -38,7 +38,6 @@ import org.apache.qpid.server.management.Managable;
import org.apache.qpid.server.management.ManagedObject;
import org.apache.qpid.server.management.ManagedObjectRegistry;
import org.apache.qpid.server.queue.QueueRegistry;
-import org.apache.qpid.server.queue.AMQQueueImpl;
import org.apache.qpid.server.queue.AMQQueue;
import org.apache.qpid.server.registry.ApplicationRegistry;
import org.apache.qpid.server.virtualhost.VirtualHost;
@@ -192,9 +191,7 @@ public abstract class AbstractExchange implements Exchange, Managable
{
_exchangeMbean.unregister();
}
- }
-
- abstract public Map<AMQShortString, List<AMQQueue>> getBindings();
+ }
public String toString()
{
diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/Exchange.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/Exchange.java
index 31c66694ec..06209c5458 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/exchange/Exchange.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/Exchange.java
@@ -93,6 +93,6 @@ public interface Exchange
*/
boolean hasBindings();
- Map<AMQShortString, List<AMQQueue>> getBindings();
+
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java
index 8d97e9ef25..441f88b9b6 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java
@@ -31,7 +31,6 @@ import org.apache.qpid.framing.ContentHeaderBody;
import org.apache.qpid.framing.FieldTable;
import org.apache.qpid.server.management.MBeanConstructor;
import org.apache.qpid.server.management.MBeanDescription;
-import org.apache.qpid.server.queue.AMQQueueImpl;
import org.apache.qpid.server.queue.IncomingMessage;
import org.apache.qpid.server.queue.AMQQueue;
import org.apache.qpid.server.virtualhost.VirtualHost;
diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/TopicExchange.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/TopicExchange.java
index 3ade1ee7f0..8d3110ef18 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/exchange/TopicExchange.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/TopicExchange.java
@@ -22,6 +22,7 @@ package org.apache.qpid.server.exchange;
import org.apache.log4j.Logger;
import org.apache.qpid.AMQException;
+import org.apache.qpid.common.AMQPFilterTypes;
import org.apache.qpid.protocol.AMQConstant;
import org.apache.qpid.exchange.ExchangeDefaults;
import org.apache.qpid.framing.AMQShortString;
@@ -31,7 +32,12 @@ import org.apache.qpid.server.management.MBeanConstructor;
import org.apache.qpid.server.management.MBeanDescription;
import org.apache.qpid.server.queue.IncomingMessage;
import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.queue.AMQMessage;
import org.apache.qpid.server.virtualhost.VirtualHost;
+import org.apache.qpid.server.exchange.topic.TopicParser;
+import org.apache.qpid.server.exchange.topic.TopicMatcherResult;
+import org.apache.qpid.server.filter.MessageFilter;
+import org.apache.qpid.server.filter.JMSSelectorFilter;
import javax.management.JMException;
import javax.management.MBeanException;
@@ -43,6 +49,9 @@ import javax.management.openmbean.TabularDataSupport;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.lang.ref.WeakReference;
public class TopicExchange extends AbstractExchange
{
@@ -80,22 +89,204 @@ public class TopicExchange extends AbstractExchange
private static final Logger _logger = Logger.getLogger(TopicExchange.class);
+/*
private final ConcurrentHashMap<AMQShortString, List<AMQQueue>> _bindingKey2queues =
new ConcurrentHashMap<AMQShortString, List<AMQQueue>>();
private final ConcurrentHashMap<AMQShortString, List<AMQQueue>> _simpleBindingKey2queues =
new ConcurrentHashMap<AMQShortString, List<AMQQueue>>();
private final ConcurrentHashMap<AMQShortString, List<AMQQueue>> _wildCardBindingKey2queues =
new ConcurrentHashMap<AMQShortString, List<AMQQueue>>();
+*/
// private ConcurrentHashMap<AMQShortString, AMQQueue> _routingKey2queue = new ConcurrentHashMap<AMQShortString, AMQQueue>();
private static final byte TOPIC_SEPARATOR = (byte)'.';
private static final AMQShortString TOPIC_SEPARATOR_AS_SHORTSTRING = new AMQShortString(".");
private static final AMQShortString AMQP_STAR_TOKEN = new AMQShortString("*");
private static final AMQShortString AMQP_HASH_TOKEN = new AMQShortString("#");
- private ConcurrentHashMap<AMQShortString, AMQShortString[]> _bindingKey2Tokenized =
- new ConcurrentHashMap<AMQShortString, AMQShortString[]>();
+
private static final byte HASH_BYTE = (byte)'#';
private static final byte STAR_BYTE = (byte)'*';
+ private final TopicParser _parser = new TopicParser();
+
+ private final Map<AMQShortString, TopicExchangeResult> _topicExchangeResults =
+ new ConcurrentHashMap<AMQShortString, TopicExchangeResult>();
+
+ private final Map<Binding, FieldTable> _bindings = new HashMap<Binding, FieldTable>();
+
+ private final Map<String, WeakReference<JMSSelectorFilter<RuntimeException>>> _selectorCache = new WeakHashMap<String, WeakReference<JMSSelectorFilter<RuntimeException>>>();
+
+ public static class Binding
+ {
+ private final AMQShortString _bindingKey;
+ private final AMQQueue _queue;
+
+ public Binding(AMQShortString bindingKey, AMQQueue queue)
+ {
+ _bindingKey = bindingKey;
+ _queue = queue;
+ }
+
+ public AMQShortString getBindingKey()
+ {
+ return _bindingKey;
+ }
+
+ public AMQQueue getQueue()
+ {
+ return _queue;
+ }
+
+ public int hashCode()
+ {
+ return (_bindingKey == null ? 1 : _bindingKey.hashCode())*31 + _queue.hashCode();
+ }
+
+ public boolean equals(Object o)
+ {
+ if(this == o)
+ {
+ return true;
+ }
+ if(o instanceof Binding)
+ {
+ Binding other = (Binding) o;
+ return (_queue == other._queue)
+ && ((_bindingKey == null) ? other._bindingKey == null : _bindingKey.equals(other._bindingKey));
+ }
+ return false;
+ }
+ }
+
+
+
+ private final class TopicExchangeResult implements TopicMatcherResult
+ {
+ private final Map<AMQQueue, Integer> _unfilteredQueues = new ConcurrentHashMap<AMQQueue, Integer>();
+ private final ConcurrentHashMap<AMQQueue, Map<MessageFilter<RuntimeException>,Integer>> _filteredQueues = new ConcurrentHashMap<AMQQueue, Map<MessageFilter<RuntimeException>, Integer>>();
+
+ public void addUnfilteredQueue(AMQQueue queue)
+ {
+ Integer instances = _unfilteredQueues.get(queue);
+ if(instances == null)
+ {
+ _unfilteredQueues.put(queue, 1);
+ }
+ else
+ {
+ _unfilteredQueues.put(queue, instances + 1);
+ }
+ }
+
+ public void removeUnfilteredQueue(AMQQueue queue)
+ {
+ Integer instances = _unfilteredQueues.get(queue);
+ if(instances == 1)
+ {
+ _unfilteredQueues.remove(queue);
+ }
+ else
+ {
+ _unfilteredQueues.put(queue,instances - 1);
+ }
+
+ }
+
+
+ public void addFilteredQueue(AMQQueue queue, MessageFilter<RuntimeException> filter)
+ {
+ Map<MessageFilter<RuntimeException>,Integer> filters = _filteredQueues.get(queue);
+ if(filters == null)
+ {
+ filters = new ConcurrentHashMap<MessageFilter<RuntimeException>,Integer>();
+ _filteredQueues.put(queue, filters);
+ }
+ Integer instances = filters.get(filter);
+ if(instances == null)
+ {
+ filters.put(filter,1);
+ }
+ else
+ {
+ filters.put(filter, instances + 1);
+ }
+
+ }
+
+ public void removeFilteredQueue(AMQQueue queue, MessageFilter<RuntimeException> filter)
+ {
+ Map<MessageFilter<RuntimeException>,Integer> filters = _filteredQueues.get(queue);
+ if(filters != null)
+ {
+ Integer instances = filters.get(filter);
+ if(instances == 1)
+ {
+ filters.remove(filter);
+ if(filters.isEmpty())
+ {
+ _filteredQueues.remove(queue);
+ }
+ }
+ else if(instances != null)
+ {
+ filters.put(filter, instances - 1);
+ }
+
+ }
+
+ }
+
+ public void replaceQueueFilter(AMQQueue queue,
+ MessageFilter<RuntimeException> oldFilter,
+ MessageFilter<RuntimeException> newFilter)
+ {
+ Map<MessageFilter<RuntimeException>,Integer> filters = _filteredQueues.get(queue);
+ Map<MessageFilter<RuntimeException>,Integer> newFilters = new ConcurrentHashMap<MessageFilter<RuntimeException>,Integer>(filters);
+ Integer oldFilterInstances = filters.get(oldFilter);
+ if(oldFilterInstances == 1)
+ {
+ newFilters.remove(oldFilter);
+ }
+ else
+ {
+ newFilters.put(oldFilter, oldFilterInstances-1);
+ }
+ Integer newFilterInstances = filters.get(newFilter);
+ if(newFilterInstances == null)
+ {
+ newFilters.put(newFilter, 1);
+ }
+ else
+ {
+ newFilters.put(newFilter, newFilterInstances+1);
+ }
+ _filteredQueues.put(queue,newFilters);
+ }
+
+ public Set<AMQQueue> processMessage(IncomingMessage msg, Set<AMQQueue> queues)
+ {
+ queues.addAll(_unfilteredQueues.keySet());
+ if(!_filteredQueues.isEmpty())
+ {
+ for(Map.Entry<AMQQueue, Map<MessageFilter<RuntimeException>, Integer>> entry : _filteredQueues.entrySet())
+ {
+ if(!queues.contains(entry.getKey()))
+ {
+ for(MessageFilter<RuntimeException> filter : entry.getValue().keySet())
+ {
+ if(filter.matches(msg))
+ {
+ queues.add(entry.getKey());
+ }
+ }
+ }
+ }
+ }
+ return queues;
+ }
+
+ }
+
+
/** TopicExchangeMBean class implements the management interface for the Topic exchanges. */
@MBeanDescription("Management Bean for Topic Exchange")
private final class TopicExchangeMBean extends ExchangeMBean
@@ -112,20 +303,24 @@ public class TopicExchange extends AbstractExchange
public TabularData bindings() throws OpenDataException
{
_bindingList = new TabularDataSupport(_bindinglistDataType);
- for (Map.Entry<AMQShortString, List<AMQQueue>> entry : _bindingKey2queues.entrySet())
+ Map<String, List<String>> bindingData = new HashMap<String, List<String>>();
+ for (Binding binding : _bindings.keySet())
{
- AMQShortString key = entry.getKey();
- List<String> queueList = new ArrayList<String>();
-
- List<AMQQueue> queues = getMatchedQueues(key);
- for (AMQQueue q : queues)
+ String key = binding.getBindingKey().toString();
+ List<String> queueNames = bindingData.get(key);
+ if(queueNames == null)
{
- queueList.add(q.getName().toString());
+ queueNames = new ArrayList<String>();
+ bindingData.put(key, queueNames);
}
+ queueNames.add(binding.getQueue().getName().toString());
- Object[] bindingItemValues = {key.toString(), queueList.toArray(new String[queueList.size()])};
- CompositeData bindingData = new CompositeDataSupport(_bindingDataType, _bindingItemNames, bindingItemValues);
- _bindingList.put(bindingData);
+ }
+ for(Map.Entry<String, List<String>> entry : bindingData.entrySet())
+ {
+ Object[] bindingItemValues = {entry.getKey(), entry.getValue().toArray(new String[entry.getValue().size()]) };
+ CompositeData bindingCompositeData = new CompositeDataSupport(_bindingDataType, _bindingItemNames, bindingItemValues);
+ _bindingList.put(bindingCompositeData);
}
return _bindingList;
@@ -163,73 +358,106 @@ public class TopicExchange extends AbstractExchange
_logger.debug("Registering queue " + queue.getName() + " with routing key " + rKey);
- // we need to use putIfAbsent, which is an atomic operation, to avoid a race condition
- List<AMQQueue> queueList = _bindingKey2queues.putIfAbsent(rKey, new CopyOnWriteArrayList<AMQQueue>());
-
-
+ AMQShortString routingKey;
-
-
-
- // if we got null back, no previous value was associated with the specified routing key hence
- // we need to read back the new value just put into the map
- if (queueList == null)
+ if(rKey.contains(HASH_BYTE) || rKey.contains(STAR_BYTE))
{
- queueList = _bindingKey2queues.get(rKey);
+ routingKey = normalize(rKey);
+ }
+ else
+ {
+ routingKey = rKey;
}
+ Binding binding = new Binding(rKey, queue);
-
- if (!queueList.contains(queue))
+ if(_bindings.containsKey(binding))
{
- queueList.add(queue);
+ FieldTable oldArgs = _bindings.get(binding);
+ TopicExchangeResult result = _topicExchangeResults.get(routingKey);
-
- if(rKey.contains(HASH_BYTE) || rKey.contains(STAR_BYTE))
+ if(argumentsContainSelector(args))
{
- AMQShortString routingKey = normalize(rKey);
- List<AMQQueue> queueList2 = _wildCardBindingKey2queues.putIfAbsent(routingKey, new CopyOnWriteArrayList<AMQQueue>());
-
- if(queueList2 == null)
+ if(argumentsContainSelector(oldArgs))
{
- queueList2 = _wildCardBindingKey2queues.get(routingKey);
- AMQShortStringTokenizer keyTok = routingKey.tokenize(TOPIC_SEPARATOR);
-
- ArrayList<AMQShortString> keyTokList = new ArrayList<AMQShortString>(keyTok.countTokens());
-
- while (keyTok.hasMoreTokens())
- {
- keyTokList.add(keyTok.nextToken());
- }
-
- _bindingKey2Tokenized.put(routingKey, keyTokList.toArray(new AMQShortString[keyTokList.size()]));
+ result.replaceQueueFilter(queue,createSelectorFilter(oldArgs), createSelectorFilter(args));
+ }
+ else
+ {
+ result.addFilteredQueue(queue,createSelectorFilter(args));
+ result.removeUnfilteredQueue(queue);
}
- queueList2.add(queue);
-
}
else
{
- List<AMQQueue> queueList2 = _simpleBindingKey2queues.putIfAbsent(rKey, new CopyOnWriteArrayList<AMQQueue>());
- if(queueList2 == null)
+ if(argumentsContainSelector(oldArgs))
+ {
+ result.addUnfilteredQueue(queue);
+ result.removeFilteredQueue(queue, createSelectorFilter(oldArgs));
+ }
+ else
{
- queueList2 = _simpleBindingKey2queues.get(rKey);
+ // TODO - fix control flow
+ return;
}
- queueList2.add(queue);
+ }
+ }
+ else
+ {
+
+ TopicExchangeResult result = _topicExchangeResults.get(routingKey);
+ if(result == null)
+ {
+ result = new TopicExchangeResult();
+ if(argumentsContainSelector(args))
+ {
+ result.addFilteredQueue(queue, createSelectorFilter(args));
+ }
+ else
+ {
+ result.addUnfilteredQueue(queue);
+ }
+ _parser.addBinding(routingKey, result);
+ _topicExchangeResults.put(routingKey,result);
}
+ else
+ {
+ if(argumentsContainSelector(args))
+ {
+ result.addFilteredQueue(queue, createSelectorFilter(args));
+ }
+ else
+ {
+ result.addUnfilteredQueue(queue);
+ }
+ }
+ _bindings.put(binding, args);
+ }
+ }
+ private JMSSelectorFilter<RuntimeException> createSelectorFilter(final FieldTable args)
+ throws AMQException
+ {
- }
- else if (_logger.isDebugEnabled())
+ final String selectorString = args.getString(AMQPFilterTypes.JMS_SELECTOR.getValue());
+ WeakReference<JMSSelectorFilter<RuntimeException>> selectorRef = _selectorCache.get(selectorString);
+ JMSSelectorFilter selector = null;
+
+ if(selectorRef == null || (selector = selectorRef.get())==null)
{
- _logger.debug("Queue " + queue + " is already registered with routing key " + rKey);
+ selector = new JMSSelectorFilter<RuntimeException>(selectorString);
+ _selectorCache.put(selectorString, new WeakReference<JMSSelectorFilter<RuntimeException>>(selector));
}
+ return selector;
+ }
-
-
+ private static boolean argumentsContainSelector(final FieldTable args)
+ {
+ return args != null && args.containsKey(AMQPFilterTypes.JMS_SELECTOR.getValue()) && args.getString(AMQPFilterTypes.JMS_SELECTOR.getValue()).trim().length() != 0;
}
private AMQShortString normalize(AMQShortString routingKey)
@@ -279,16 +507,6 @@ public class TopicExchange extends AbstractExchange
AMQShortString normalizedString = AMQShortString.join(subscriptionList, TOPIC_SEPARATOR_AS_SHORTSTRING);
-/*
- StringBuilder sb = new StringBuilder();
- for (AMQShortString s : subscriptionList)
- {
- sb.append(s);
- sb.append(TOPIC_SEPARATOR);
- }
-
- sb.deleteCharAt(sb.length() - 1);
-*/
return normalizedString;
}
@@ -298,11 +516,11 @@ public class TopicExchange extends AbstractExchange
final AMQShortString routingKey = payload.getRoutingKey();
- List<AMQQueue> queues = getMatchedQueues(routingKey);
+ Collection<AMQQueue> queues = getMatchedQueues(payload, routingKey);
if(queues == null || queues.isEmpty())
{
- _logger.info("Message routing key: " + payload.getRoutingKey() + " No routes - " + _bindingKey2queues);
+ _logger.info("Message routing key: " + payload.getRoutingKey() + " No routes.");
}
payload.enqueue(queues);
@@ -316,23 +534,29 @@ public class TopicExchange extends AbstractExchange
public boolean isBound(AMQShortString routingKey, AMQQueue queue)
{
- List<AMQQueue> queues = _bindingKey2queues.get(normalize(routingKey));
+ Binding binding = new Binding(routingKey, queue);
- return (queues != null) && queues.contains(queue);
+ return _bindings.containsKey(binding);
}
public boolean isBound(AMQShortString routingKey)
{
- List<AMQQueue> queues = _bindingKey2queues.get(normalize(routingKey));
+ for(Binding b : _bindings.keySet())
+ {
+ if(b.getBindingKey().equals(routingKey))
+ {
+ return true;
+ }
+ }
- return (queues != null) && !queues.isEmpty();
+ return false;
}
public boolean isBound(AMQQueue queue)
{
- for (List<AMQQueue> queues : _bindingKey2queues.values())
+ for(Binding b : _bindings.keySet())
{
- if (queues.contains(queue))
+ if(b.getQueue().equals(queue))
{
return true;
}
@@ -343,7 +567,7 @@ public class TopicExchange extends AbstractExchange
public boolean hasBindings()
{
- return !_bindingKey2queues.isEmpty();
+ return !_bindings.isEmpty();
}
public synchronized void deregisterQueue(AMQShortString rKey, AMQQueue queue, FieldTable args) throws AMQException
@@ -351,52 +575,27 @@ public class TopicExchange extends AbstractExchange
assert queue != null;
assert rKey != null;
- List<AMQQueue> queues = _bindingKey2queues.get(rKey);
- if (queues == null)
- {
- throw new AMQException(AMQConstant.NOT_FOUND, "Queue " + queue + " was not registered with exchange " + this.getName()
- + " with routing key " + rKey + ". No queue was registered with that _routing key");
+ Binding binding = new Binding(rKey, queue);
- }
- boolean removedQ = queues.remove(queue);
- if (!removedQ)
+ if (!_bindings.containsKey(binding))
{
- throw new AMQException(AMQConstant.NOT_FOUND, "Queue " + queue + " was not registered with exchange " + this.getName()
- + " with routing key " + rKey);
+ throw new AMQException(AMQConstant.NOT_FOUND, "Queue " + queue.getName() + " was not registered with exchange " + this.getName()
+ + " with routing key " + rKey + ".");
}
-
- if(rKey.contains(HASH_BYTE) || rKey.contains(STAR_BYTE))
+ FieldTable bindingArgs = _bindings.remove(binding);
+ AMQShortString bindingKey = normalize(rKey);
+ TopicExchangeResult result = _topicExchangeResults.get(bindingKey);
+ if(argumentsContainSelector(bindingArgs))
{
- AMQShortString bindingKey = normalize(rKey);
- List<AMQQueue> queues2 = _wildCardBindingKey2queues.get(bindingKey);
- queues2.remove(queue);
- if(queues2.isEmpty())
- {
- _wildCardBindingKey2queues.remove(bindingKey);
- _bindingKey2Tokenized.remove(bindingKey);
- }
-
+ result.removeFilteredQueue(queue, createSelectorFilter(bindingArgs));
}
else
{
- List<AMQQueue> queues2 = _simpleBindingKey2queues.get(rKey);
- queues2.remove(queue);
- if(queues2.isEmpty())
- {
- _simpleBindingKey2queues.remove(rKey);
- }
-
+ result.removeUnfilteredQueue(queue);
}
-
-
-
- if (queues.isEmpty())
- {
- _bindingKey2queues.remove(rKey);
- }
}
protected ExchangeMBean createMBean() throws AMQException
@@ -412,172 +611,25 @@ public class TopicExchange extends AbstractExchange
}
}
- public Map<AMQShortString, List<AMQQueue>> getBindings()
+ private Collection<AMQQueue> getMatchedQueues(IncomingMessage message, AMQShortString routingKey)
{
- return _bindingKey2queues;
- }
-
- private List<AMQQueue> getMatchedQueues(AMQShortString routingKey)
- {
-
- List<AMQQueue> list = null;
- if(!_wildCardBindingKey2queues.isEmpty())
+ Collection<TopicMatcherResult> results = _parser.parse(routingKey);
+ if(results.isEmpty())
{
-
-
- AMQShortStringTokenizer routingTokens = routingKey.tokenize(TOPIC_SEPARATOR);
-
- final int routingTokensCount = routingTokens.countTokens();
-
-
- AMQShortString[] routingkeyTokens = new AMQShortString[routingTokensCount];
-
- if(routingTokensCount == 1)
- {
- routingkeyTokens[0] =routingKey;
- }
- else
- {
-
-
- int token = 0;
- while (routingTokens.hasMoreTokens())
- {
-
- AMQShortString next = routingTokens.nextToken();
- /* if (next.equals(AMQP_HASH) && routingkeyTokens.get(routingkeyTokens.size() - 1).equals(AMQP_HASH))
- {
- continue;
- }
- */
-
- routingkeyTokens[token++] = next;
- }
- }
-
- _logger.info("Routing key tokens: " + Arrays.asList(routingkeyTokens));
-
- for (AMQShortString bindingKey : _wildCardBindingKey2queues.keySet())
- {
-
- AMQShortString[] bindingKeyTokens = _bindingKey2Tokenized.get(bindingKey);
-
-
- boolean matching = true;
- boolean done = false;
-
- int depthPlusRoutingSkip = 0;
- int depthPlusQueueSkip = 0;
-
- final int bindingKeyTokensCount = bindingKeyTokens.length;
-
- while (matching && !done)
- {
-
- if ((bindingKeyTokensCount == depthPlusQueueSkip) || (routingTokensCount == depthPlusRoutingSkip))
- {
- done = true;
-
- // if it was the routing key that ran out of digits
- if (routingTokensCount == depthPlusRoutingSkip)
- {
- if (bindingKeyTokensCount > depthPlusQueueSkip)
- { // a hash and it is the last entry
- matching =
- bindingKeyTokens[depthPlusQueueSkip].equals(AMQP_HASH_TOKEN)
- && (bindingKeyTokensCount == (depthPlusQueueSkip + 1));
- }
- }
- else if (routingTokensCount > depthPlusRoutingSkip)
- {
- // There is still more routing key to check
- matching = false;
- }
-
- continue;
- }
-
- // if the values on the two topics don't match
- if (!bindingKeyTokens[depthPlusQueueSkip].equals(routingkeyTokens[depthPlusRoutingSkip]))
- {
- if (bindingKeyTokens[depthPlusQueueSkip].equals(AMQP_STAR_TOKEN))
- {
- depthPlusQueueSkip++;
- depthPlusRoutingSkip++;
-
- continue;
- }
- else if (bindingKeyTokens[depthPlusQueueSkip].equals(AMQP_HASH_TOKEN))
- {
- // Is this a # at the end
- if (bindingKeyTokensCount == (depthPlusQueueSkip + 1))
- {
- done = true;
-
- continue;
- }
-
- // otherwise # in the middle
- while (routingTokensCount > depthPlusRoutingSkip)
- {
- if (routingkeyTokens[depthPlusRoutingSkip].equals(bindingKeyTokens[depthPlusQueueSkip + 1]))
- {
- depthPlusQueueSkip += 2;
- depthPlusRoutingSkip++;
-
- break;
- }
-
- depthPlusRoutingSkip++;
- }
-
- continue;
- }
-
- matching = false;
- }
-
- depthPlusQueueSkip++;
- depthPlusRoutingSkip++;
- }
-
- if (matching)
- {
- if(list == null)
- {
- list = new ArrayList<AMQQueue>(_wildCardBindingKey2queues.get(bindingKey));
- }
- else
- {
- list.addAll(_wildCardBindingKey2queues.get(bindingKey));
- }
- }
- }
-
+ return Collections.EMPTY_SET;
}
- if(!_simpleBindingKey2queues.isEmpty())
+ else
{
- List<AMQQueue> queues = _simpleBindingKey2queues.get(routingKey);
- if(list == null)
- {
- if(queues == null)
- {
- list = Collections.EMPTY_LIST;
- }
- else
- {
- list = new ArrayList<AMQQueue>(queues);
- }
- }
- else if(queues != null)
+ Set<AMQQueue> queues = new HashSet<AMQQueue>();
+ for(TopicMatcherResult result : results)
{
- list.addAll(queues);
- }
+ ((TopicExchangeResult)result).processMessage(message, queues);
+ }
+ return queues;
}
- return list;
}
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicMatcherDFAState.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicMatcherDFAState.java
index 9da1852f8e..36076cf75b 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicMatcherDFAState.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicMatcherDFAState.java
@@ -4,6 +4,7 @@ import org.apache.qpid.framing.AMQShortString;
import org.apache.qpid.framing.AMQShortStringTokenizer;
import java.util.*;
+import java.util.concurrent.atomic.AtomicInteger;
/*
*
@@ -27,6 +28,10 @@ import java.util.*;
*/
public class TopicMatcherDFAState
{
+ private static final AtomicInteger stateId = new AtomicInteger();
+
+ private final int _id = stateId.incrementAndGet();
+
private final Collection<TopicMatcherResult> _results;
private final Map<TopicWord, TopicMatcherDFAState> _nextStateMap;
private static final byte TOPIC_DELIMITTER = (byte)'.';
@@ -233,4 +238,58 @@ public class TopicMatcherDFAState
}
+ public String toString()
+ {
+ StringBuilder transitions = new StringBuilder();
+ for(Map.Entry<TopicWord, TopicMatcherDFAState> entry : _nextStateMap.entrySet())
+ {
+ transitions.append("[ ");
+ transitions.append(entry.getKey());
+ transitions.append("\t ->\t ");
+ transitions.append(entry.getValue()._id);
+ transitions.append(" ]\n");
+ }
+
+
+ return "[ State " + _id + " ]\n" + transitions + "\n";
+
+ }
+
+ public String reachableStates()
+ {
+ StringBuilder result = new StringBuilder("Start state: " + _id + "\n");
+
+ SortedSet<TopicMatcherDFAState> reachableStates =
+ new TreeSet<TopicMatcherDFAState>(new Comparator<TopicMatcherDFAState>()
+ {
+ public int compare(final TopicMatcherDFAState o1, final TopicMatcherDFAState o2)
+ {
+ return o1._id - o2._id;
+ }
+ });
+ reachableStates.add(this);
+
+ int count;
+
+ do
+ {
+ count = reachableStates.size();
+ Collection<TopicMatcherDFAState> originalStates = new ArrayList<TopicMatcherDFAState>(reachableStates);
+ for(TopicMatcherDFAState state : originalStates)
+ {
+ reachableStates.addAll(state._nextStateMap.values());
+ }
+ }
+ while(reachableStates.size() != count);
+
+
+
+ for(TopicMatcherDFAState state : reachableStates)
+ {
+ result.append(state.toString());
+ }
+
+ return result.toString();
+ }
+
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicMatcherResult.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicMatcherResult.java
index 7e3d1819f4..71d30adfac 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicMatcherResult.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicMatcherResult.java
@@ -20,6 +20,6 @@ package org.apache.qpid.server.exchange.topic;
* under the License.
*
*/
-public class TopicMatcherResult
+public interface TopicMatcherResult
{
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicParser.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicParser.java
index f564bbb565..3e9facf412 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicParser.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicParser.java
@@ -4,6 +4,8 @@ import org.apache.qpid.framing.AMQShortString;
import org.apache.qpid.framing.AMQShortStringTokenizer;
import java.util.*;
+import java.util.concurrent.atomic.AtomicReference;
+import java.io.IOException;
/*
*
@@ -30,6 +32,7 @@ public class TopicParser
private static final byte TOPIC_DELIMITER = (byte)'.';
private final TopicWordDictionary _dictionary = new TopicWordDictionary();
+ private final AtomicReference<TopicMatcherDFAState> _stateMachine = new AtomicReference<TopicMatcherDFAState>();
private static class Position
{
@@ -37,6 +40,7 @@ public class TopicParser
private final boolean _selfTransition;
private final int _position;
private final boolean _endState;
+ private boolean _followedByAnyLoop;
public Position(final int position, final TopicWord word, final boolean selfTransition, final boolean endState)
@@ -59,6 +63,43 @@ public class TopicParser
}
+ public void addBinding(AMQShortString bindingKey, TopicMatcherResult result)
+ {
+
+ TopicMatcherDFAState startingStateMachine;
+ TopicMatcherDFAState newStateMachine;
+
+ do
+ {
+ startingStateMachine = _stateMachine.get();
+ if(startingStateMachine == null)
+ {
+ newStateMachine = createStateMachine(bindingKey, result);
+ }
+ else
+ {
+ newStateMachine = startingStateMachine.mergeStateMachines(createStateMachine(bindingKey, result));
+ }
+
+ }
+ while(!_stateMachine.compareAndSet(startingStateMachine,newStateMachine));
+
+ }
+
+ public Collection<TopicMatcherResult> parse(AMQShortString routingKey)
+ {
+ TopicMatcherDFAState stateMachine = _stateMachine.get();
+ if(stateMachine == null)
+ {
+ return Collections.EMPTY_SET;
+ }
+ else
+ {
+ return stateMachine.parse(_dictionary,routingKey);
+ }
+ }
+
+
TopicMatcherDFAState createStateMachine(AMQShortString bindingKey, TopicMatcherResult result)
{
List<TopicWord> wordList = createTopicWordList(bindingKey);
@@ -108,7 +149,6 @@ public class TopicParser
}
-
int pos = 0;
int wordPos = 0;
@@ -131,6 +171,31 @@ public class TopicParser
}
+
+ for(int p = 0; p<positionCount; p++)
+ {
+ boolean followedByWildcards = true;
+
+ int n = p;
+ while(followedByWildcards && n<(positionCount+1))
+ {
+
+ if(positions[n]._selfTransition)
+ {
+ break;
+ }
+ else if(positions[n]._word!=TopicWord.ANY_WORD)
+ {
+ followedByWildcards = false;
+ }
+ n++;
+ }
+
+
+ positions[p]._followedByAnyLoop = followedByWildcards && (n!= positionCount+1);
+ }
+
+
// from each position you transition to a set of other positions.
// we approach this by examining steps of increasing length - so we
// look how far we can go from the start position in 1 word, 2 words, etc...
@@ -258,6 +323,32 @@ public class TopicParser
{
dest.setValue(Collections.singleton(loopingTerminal));
}
+ else
+ {
+ Position anyLoop = null;
+ for(Position destPos : dest.getValue())
+ {
+ if(destPos._followedByAnyLoop)
+ {
+ if(anyLoop == null || anyLoop._position<destPos._position)
+ {
+ anyLoop = destPos;
+ }
+ }
+ }
+ if(anyLoop != null)
+ {
+ Collection<Position> removals = new ArrayList<Position>();
+ for(Position destPos : dest.getValue())
+ {
+ if(destPos._position < anyLoop._position)
+ {
+ removals.add(destPos);
+ }
+ }
+ dest.getValue().removeAll(removals);
+ }
+ }
SimpleState stateForEntry = stateMap.get(dest.getValue());
if(stateForEntry == null)
@@ -332,8 +423,65 @@ public class TopicParser
public static void main(String[] args)
{
+
+ printMatches("#.b.*.*.*.*.*.h.#.j.*.*.*.*.*.*.q.#.r.*.*.*.*.*.*.*.*","a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z");
+ printMatches(new String[]{
+ "#.a.#",
+ "#.b.#",
+ "#.c.#",
+ "#.d.#",
+ "#.e.#",
+ "#.f.#",
+ "#.g.#",
+ "#.h.#",
+ "#.i.#",
+ "#.j.#",
+ "#.k.#",
+ "#.l.#",
+ "#.m.#",
+ "#.n.#",
+ "#.o.#",
+ "#.p.#",
+ "#.q.#"
+
+ }, "a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z");
+/*
+ printMatches(new String[]{
+ "#.a.#",
+ "#.b.#",
+ "#.c.#",
+ "#.d.#",
+ "#.e.#",
+ "#.f.#",
+ "#.g.#",
+ "#.h.#",
+ "#.i.#",
+ "#.j.#",
+ "#.k.#",
+ "#.l.#",
+ "#.m.#",
+ "#.n.#",
+ "#.o.#",
+ "#.p.#",
+ "#.q.#",
+ "#.r.#",
+ "#.s.#",
+ "#.t.#",
+ "#.u.#",
+ "#.v.#",
+ "#.w.#",
+ "#.x.#",
+ "#.y.#",
+ "#.z.#"
+
+
+ },"a.b");
+
+ printMatches("#.b.*.*.*.*.*.h.#.j.*.*.*.*.*.p.#.r.*.*.*.*.*","a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z");
+ printMatches("#.b.*.*.*.*.*.h.#.j.*.*.*.*.*.p.#.r.*.*.*.*.*.*.*.*","a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z");
printMatches("a.#.b.#","a.b.b.b.b.b.b.b.c");
+*/
printMatches("","");
printMatches("a","a");
@@ -394,12 +542,19 @@ public class TopicParser
TopicParser parser = new TopicParser();
+ long start = System.currentTimeMillis();
for(int i = 0; i < bindingKeys.length; i++)
{
- TopicMatcherResult r = new TopicMatcherResult();
+ System.out.println((System.currentTimeMillis() - start) + ":\t" + bindingKeys[i]);
+ TopicMatcherResult r = new TopicMatcherResult(){};
resultMap.put(r, bindingKeys[i]);
AMQShortString bindingKeyShortString = new AMQShortString(bindingKeys[i]);
+ System.err.println("=====================================================");
+ System.err.println("Adding binding key: " + bindingKeyShortString);
+ System.err.println("-----------------------------------------------------");
+
+
if(i==0)
{
sm = parser.createStateMachine(bindingKeyShortString, r);
@@ -408,6 +563,16 @@ public class TopicParser
{
sm = sm.mergeStateMachines(parser.createStateMachine(bindingKeyShortString, r));
}
+ System.err.println(sm.reachableStates());
+ System.err.println("=====================================================");
+ try
+ {
+ System.in.read();
+ }
+ catch (IOException e)
+ {
+ e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
+ }
}
AMQShortString routingKeyShortString = new AMQShortString(routingKey);
@@ -438,7 +603,7 @@ public class TopicParser
AMQShortString routingKeyShortString = new AMQShortString(routingKey);
TopicParser parser = new TopicParser();
- final TopicMatcherResult result = new TopicMatcherResult();
+ final TopicMatcherResult result = new TopicMatcherResult(){};
TopicMatcherDFAState sm = parser.createStateMachine(bindingKeyShortString, result);
return !sm.parse(parser._dictionary,routingKeyShortString).isEmpty();
diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicWord.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicWord.java
index e86a726b4f..f14d70f8a1 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicWord.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicWord.java
@@ -28,7 +28,27 @@ import java.util.concurrent.ConcurrentHashMap;
*/
public final class TopicWord
{
- public static final TopicWord ANY_WORD = new TopicWord();
- public static final TopicWord WILDCARD_WORD = new TopicWord();
+ public static final TopicWord ANY_WORD = new TopicWord("*");
+ public static final TopicWord WILDCARD_WORD = new TopicWord("#");
+ private String _word;
+ public TopicWord()
+ {
+
+ }
+
+ public TopicWord(String s)
+ {
+ _word = s;
+ }
+
+ public TopicWord(final AMQShortString name)
+ {
+ _word = name.toString();
+ }
+
+ public String toString()
+ {
+ return _word;
+ }
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicWordDictionary.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicWordDictionary.java
index f69616cc85..65a0cd3107 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicWordDictionary.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicWordDictionary.java
@@ -42,7 +42,7 @@ public class TopicWordDictionary
public TopicWord getOrCreateWord(AMQShortString name)
{
- TopicWord word = _dictionary.putIfAbsent(name, new TopicWord());
+ TopicWord word = _dictionary.putIfAbsent(name, new TopicWord(name));
if(word == null)
{
word = _dictionary.get(name);
diff --git a/java/broker/src/main/java/org/apache/qpid/server/filter/ArithmeticExpression.java b/java/broker/src/main/java/org/apache/qpid/server/filter/ArithmeticExpression.java
index fb5220f4da..a964bce306 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/filter/ArithmeticExpression.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/filter/ArithmeticExpression.java
@@ -21,12 +21,12 @@ package org.apache.qpid.server.filter;
//
import org.apache.qpid.AMQException;
-import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.queue.Filterable;
/**
* An expression which performs an operation on two expression values
*/
-public abstract class ArithmeticExpression extends BinaryExpression
+public abstract class ArithmeticExpression<E extends Exception> extends BinaryExpression<E>
{
protected static final int INTEGER = 1;
@@ -248,7 +248,7 @@ public abstract class ArithmeticExpression extends BinaryExpression
}
}
- public Object evaluate(AMQMessage message) throws AMQException
+ public Object evaluate(Filterable<E> message) throws E
{
Object lvalue = left.evaluate(message);
if (lvalue == null)
diff --git a/java/broker/src/main/java/org/apache/qpid/server/filter/BinaryExpression.java b/java/broker/src/main/java/org/apache/qpid/server/filter/BinaryExpression.java
index 024257bea9..7308de80d6 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/filter/BinaryExpression.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/filter/BinaryExpression.java
@@ -23,23 +23,23 @@ package org.apache.qpid.server.filter;
/**
* An expression which performs an operation on two expression values.
*/
-public abstract class BinaryExpression implements Expression
+public abstract class BinaryExpression<E extends Exception> implements Expression<E>
{
- protected Expression left;
- protected Expression right;
+ protected Expression<E> left;
+ protected Expression<E> right;
- public BinaryExpression(Expression left, Expression right)
+ public BinaryExpression(Expression<E> left, Expression<E> right)
{
this.left = left;
this.right = right;
}
- public Expression getLeft()
+ public Expression<E> getLeft()
{
return left;
}
- public Expression getRight()
+ public Expression<E> getRight()
{
return right;
}
@@ -90,7 +90,7 @@ public abstract class BinaryExpression implements Expression
/**
* @param expression
*/
- public void setRight(Expression expression)
+ public void setRight(Expression<E> expression)
{
right = expression;
}
@@ -98,7 +98,7 @@ public abstract class BinaryExpression implements Expression
/**
* @param expression
*/
- public void setLeft(Expression expression)
+ public void setLeft(Expression<E> expression)
{
left = expression;
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/filter/BooleanExpression.java b/java/broker/src/main/java/org/apache/qpid/server/filter/BooleanExpression.java
index e28ff79820..9beb9798d0 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/filter/BooleanExpression.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/filter/BooleanExpression.java
@@ -22,19 +22,20 @@ package org.apache.qpid.server.filter;
import org.apache.qpid.AMQException;
import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.queue.Filterable;
/**
* A BooleanExpression is an expression that always
* produces a Boolean result.
*/
-public interface BooleanExpression extends Expression
+public interface BooleanExpression<E extends Exception> extends Expression<E>
{
/**
* @param message
* @return true if the expression evaluates to Boolean.TRUE.
- * @throws AMQException
+ * @throws E
*/
- public boolean matches(AMQMessage message) throws AMQException;
+ public boolean matches(Filterable<E> message) throws E;
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/filter/ComparisonExpression.java b/java/broker/src/main/java/org/apache/qpid/server/filter/ComparisonExpression.java
index 72a9ef7969..921005c462 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/filter/ComparisonExpression.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/filter/ComparisonExpression.java
@@ -29,19 +29,20 @@ import java.util.regex.Pattern;
import org.apache.qpid.AMQException;
import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.queue.Filterable;
/**
* A filter performing a comparison of two objects
*/
-public abstract class ComparisonExpression extends BinaryExpression implements BooleanExpression
+public abstract class ComparisonExpression<E extends Exception> extends BinaryExpression<E> implements BooleanExpression<E>
{
- public static BooleanExpression createBetween(Expression value, Expression left, Expression right)
+ public static<E extends Exception> BooleanExpression<E> createBetween(Expression<E> value, Expression left, Expression<E> right)
{
return LogicExpression.createAND(createGreaterThanEqual(value, left), createLessThanEqual(value, right));
}
- public static BooleanExpression createNotBetween(Expression value, Expression left, Expression right)
+ public static<E extends Exception> BooleanExpression<E> createNotBetween(Expression<E> value, Expression<E> left, Expression<E> right)
{
return LogicExpression.createOR(createLessThan(value, left), createGreaterThan(value, right));
}
@@ -72,7 +73,7 @@ public abstract class ComparisonExpression extends BinaryExpression implements B
REGEXP_CONTROL_CHARS.add(new Character('!'));
}
- static class LikeExpression extends UnaryExpression implements BooleanExpression
+ static class LikeExpression<E extends Exception> extends UnaryExpression<E> implements BooleanExpression<E>
{
Pattern likePattern;
@@ -80,7 +81,7 @@ public abstract class ComparisonExpression extends BinaryExpression implements B
/**
* @param right
*/
- public LikeExpression(Expression right, String like, int escape)
+ public LikeExpression(Expression<E> right, String like, int escape)
{
super(right);
@@ -137,7 +138,7 @@ public abstract class ComparisonExpression extends BinaryExpression implements B
/**
* org.apache.activemq.filter.Expression#evaluate(MessageEvaluationContext)
*/
- public Object evaluate(AMQMessage message) throws AMQException
+ public Object evaluate(Filterable<E> message) throws E
{
Object rv = this.getRight().evaluate(message);
@@ -157,7 +158,7 @@ public abstract class ComparisonExpression extends BinaryExpression implements B
return likePattern.matcher((String) rv).matches() ? Boolean.TRUE : Boolean.FALSE;
}
- public boolean matches(AMQMessage message) throws AMQException
+ public boolean matches(Filterable<E> message) throws E
{
Object object = evaluate(message);
@@ -235,45 +236,9 @@ public abstract class ComparisonExpression extends BinaryExpression implements B
return doCreateEqual(left, right);
}
- private static BooleanExpression doCreateEqual(Expression left, Expression right)
+ private static<E extends Exception> BooleanExpression<E> doCreateEqual(Expression<E> left, Expression<E> right)
{
- return new ComparisonExpression(left, right)
- {
-
- public Object evaluate(AMQMessage message) throws AMQException
- {
- Object lv = left.evaluate(message);
- Object rv = right.evaluate(message);
-
- // Iff one of the values is null
- if ((lv == null) ^ (rv == null))
- {
- return Boolean.FALSE;
- }
-
- if ((lv == rv) || lv.equals(rv))
- {
- return Boolean.TRUE;
- }
-
- if ((lv instanceof Comparable) && (rv instanceof Comparable))
- {
- return compare((Comparable) lv, (Comparable) rv);
- }
-
- return Boolean.FALSE;
- }
-
- protected boolean asBoolean(int answer)
- {
- return answer == 0;
- }
-
- public String getExpressionSymbol()
- {
- return "=";
- }
- };
+ return new EqualExpression(left, right);
}
public static BooleanExpression createGreaterThan(final Expression left, final Expression right)
@@ -423,7 +388,7 @@ public abstract class ComparisonExpression extends BinaryExpression implements B
super(left, right);
}
- public Object evaluate(AMQMessage message) throws AMQException
+ public Object evaluate(Filterable<E> message) throws E
{
Comparable lv = (Comparable) left.evaluate(message);
if (lv == null)
@@ -585,11 +550,52 @@ public abstract class ComparisonExpression extends BinaryExpression implements B
protected abstract boolean asBoolean(int answer);
- public boolean matches(AMQMessage message) throws AMQException
+ public boolean matches(Filterable<E> message) throws E
{
Object object = evaluate(message);
return (object != null) && (object == Boolean.TRUE);
}
+ private static class EqualExpression<E extends Exception> extends ComparisonExpression<E>
+ {
+ public EqualExpression(final Expression<E> left, final Expression<E> right)
+ {
+ super(left, right);
+ }
+
+ public Object evaluate(Filterable<E> message) throws E
+ {
+ Object lv = left.evaluate(message);
+ Object rv = right.evaluate(message);
+
+ // Iff one of the values is null
+ if ((lv == null) ^ (rv == null))
+ {
+ return Boolean.FALSE;
+ }
+
+ if ((lv == rv) || lv.equals(rv))
+ {
+ return Boolean.TRUE;
+ }
+
+ if ((lv instanceof Comparable) && (rv instanceof Comparable))
+ {
+ return compare((Comparable) lv, (Comparable) rv);
+ }
+
+ return Boolean.FALSE;
+ }
+
+ protected boolean asBoolean(int answer)
+ {
+ return answer == 0;
+ }
+
+ public String getExpressionSymbol()
+ {
+ return "=";
+ }
+ }
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/filter/ConstantExpression.java b/java/broker/src/main/java/org/apache/qpid/server/filter/ConstantExpression.java
index 0e729cc521..3ed2286f2e 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/filter/ConstantExpression.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/filter/ConstantExpression.java
@@ -27,21 +27,22 @@ import java.math.BigDecimal;
import org.apache.qpid.AMQException;
import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.queue.Filterable;
/**
* Represents a constant expression
*/
-public class ConstantExpression implements Expression
+public class ConstantExpression<E extends Exception> implements Expression<E>
{
- static class BooleanConstantExpression extends ConstantExpression implements BooleanExpression
+ static class BooleanConstantExpression<E extends Exception> extends ConstantExpression<E> implements BooleanExpression<E>
{
public BooleanConstantExpression(Object value)
{
super(value);
}
- public boolean matches(AMQMessage message) throws AMQException
+ public boolean matches(Filterable<E> message) throws E
{
Object object = evaluate(message);
@@ -120,7 +121,7 @@ public class ConstantExpression implements Expression
this.value = value;
}
- public Object evaluate(AMQMessage message) throws AMQException
+ public Object evaluate(Filterable<E> message) throws E
{
return value;
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/filter/Expression.java b/java/broker/src/main/java/org/apache/qpid/server/filter/Expression.java
index 5f646c15db..f2ebe41d26 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/filter/Expression.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/filter/Expression.java
@@ -22,16 +22,17 @@ package org.apache.qpid.server.filter;
import org.apache.qpid.AMQException;
import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.queue.Filterable;
/**
* Represents an expression
*/
-public interface Expression
+public interface Expression<E extends Exception>
{
/**
* @return the value of this expression
*/
- public Object evaluate(AMQMessage message) throws AMQException;
+ public Object evaluate(Filterable<E> message) throws E;
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/filter/FilterManager.java b/java/broker/src/main/java/org/apache/qpid/server/filter/FilterManager.java
index c82de9fa15..dd3c126ee5 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/filter/FilterManager.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/filter/FilterManager.java
@@ -24,14 +24,16 @@ package org.apache.qpid.server.filter;
//
import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.queue.Filterable;
+import org.apache.qpid.AMQException;
-public interface FilterManager
+public interface FilterManager<E extends Exception>
{
- void add(MessageFilter filter);
+ void add(MessageFilter<E> filter);
- void remove(MessageFilter filter);
+ void remove(MessageFilter<E> filter);
- boolean allAllow(AMQMessage msg);
+ boolean allAllow(Filterable<E> msg);
boolean hasFilters();
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/filter/FilterManagerFactory.java b/java/broker/src/main/java/org/apache/qpid/server/filter/FilterManagerFactory.java
index 311f0680ec..a7f49d0566 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/filter/FilterManagerFactory.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/filter/FilterManagerFactory.java
@@ -39,7 +39,7 @@ public class FilterManagerFactory
if (filters != null)
{
- manager = new SimpleFilterManager();
+
if(filters.containsKey(AMQPFilterTypes.JMS_SELECTOR.getValue()))
{
@@ -47,23 +47,13 @@ public class FilterManagerFactory
if (selector != null && !selector.equals(""))
{
+ manager = new SimpleFilterManager();
manager.add(new JMSSelectorFilter(selector));
}
}
- if (filters.containsKey(AMQPFilterTypes.NO_CONSUME.getValue()))
- {
- manager.add(new NoConsumerFilter());
- }
-
-
- //If we added no filters don't bear the overhead of having an filter manager
- if (!manager.hasFilters())
- {
- manager = null;
- }
}
else
{
diff --git a/java/broker/src/main/java/org/apache/qpid/server/filter/JMSSelectorFilter.java b/java/broker/src/main/java/org/apache/qpid/server/filter/JMSSelectorFilter.java
index 32f58ed666..96c9353872 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/filter/JMSSelectorFilter.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/filter/JMSSelectorFilter.java
@@ -23,42 +23,30 @@ package org.apache.qpid.server.filter;
import org.apache.log4j.Logger;
import org.apache.qpid.AMQException;
import org.apache.qpid.server.filter.jms.selector.SelectorParser;
-import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.queue.Filterable;
-
-public class JMSSelectorFilter implements MessageFilter
+public class JMSSelectorFilter<E extends Exception> implements MessageFilter<E>
{
private final static Logger _logger = org.apache.log4j.Logger.getLogger(JMSSelectorFilter.class);
private String _selector;
- private BooleanExpression _matcher;
+ private BooleanExpression<E> _matcher;
public JMSSelectorFilter(String selector) throws AMQException
{
_selector = selector;
- _logger.info("Created JMSSelectorFilter with selector:" + _selector);
-
-
_matcher = new SelectorParser().parse(selector);
-
-
}
- public boolean matches(AMQMessage message)
+ public boolean matches(Filterable<E> message) throws E
{
- try
- {
- boolean match = _matcher.matches(message);
- _logger.info(message + " match(" + match + ") selector(" + System.identityHashCode(_selector) + "):" + _selector);
- return match;
- }
- catch (AMQException e)
+ boolean match = _matcher.matches(message);
+ if(_logger.isDebugEnabled())
{
- //fixme this needs to be sorted.. it shouldn't happen
- e.printStackTrace();
+ _logger.debug(message + " match(" + match + ") selector(" + System.identityHashCode(_selector) + "):" + _selector);
}
- return false;
+ return match;
}
public String getSelector()
diff --git a/java/broker/src/main/java/org/apache/qpid/server/filter/LogicExpression.java b/java/broker/src/main/java/org/apache/qpid/server/filter/LogicExpression.java
index c8cbdb2125..094363ed9a 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/filter/LogicExpression.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/filter/LogicExpression.java
@@ -22,71 +22,22 @@ package org.apache.qpid.server.filter;
import org.apache.qpid.AMQException;
import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.queue.Filterable;
/**
* A filter performing a comparison of two objects
*/
-public abstract class LogicExpression extends BinaryExpression implements BooleanExpression
+public abstract class LogicExpression<E extends Exception> extends BinaryExpression<E> implements BooleanExpression<E>
{
- public static BooleanExpression createOR(BooleanExpression lvalue, BooleanExpression rvalue)
+ public static<E extends Exception> BooleanExpression createOR(BooleanExpression<E> lvalue, BooleanExpression<E> rvalue)
{
- return new LogicExpression(lvalue, rvalue)
- {
-
- public Object evaluate(AMQMessage message) throws AMQException
- {
-
- Boolean lv = (Boolean) left.evaluate(message);
- // Can we do an OR shortcut??
- if ((lv != null) && lv.booleanValue())
- {
- return Boolean.TRUE;
- }
-
- Boolean rv = (Boolean) right.evaluate(message);
-
- return (rv == null) ? null : rv;
- }
-
- public String getExpressionSymbol()
- {
- return "OR";
- }
- };
+ return new OrExpression(lvalue, rvalue);
}
- public static BooleanExpression createAND(BooleanExpression lvalue, BooleanExpression rvalue)
+ public static<E extends Exception> BooleanExpression createAND(BooleanExpression<E> lvalue, BooleanExpression<E> rvalue)
{
- return new LogicExpression(lvalue, rvalue)
- {
-
- public Object evaluate(AMQMessage message) throws AMQException
- {
-
- Boolean lv = (Boolean) left.evaluate(message);
-
- // Can we do an AND shortcut??
- if (lv == null)
- {
- return null;
- }
-
- if (!lv.booleanValue())
- {
- return Boolean.FALSE;
- }
-
- Boolean rv = (Boolean) right.evaluate(message);
-
- return (rv == null) ? null : rv;
- }
-
- public String getExpressionSymbol()
- {
- return "AND";
- }
- };
+ return new AndExpression(lvalue, rvalue);
}
/**
@@ -98,13 +49,74 @@ public abstract class LogicExpression extends BinaryExpression implements Boolea
super(left, right);
}
- public abstract Object evaluate(AMQMessage message) throws AMQException;
+ public abstract Object evaluate(Filterable<E> message) throws E;
- public boolean matches(AMQMessage message) throws AMQException
+ public boolean matches(Filterable<E> message) throws E
{
Object object = evaluate(message);
return (object != null) && (object == Boolean.TRUE);
}
+ private static class OrExpression<E extends Exception> extends LogicExpression<E>
+ {
+ public OrExpression(final BooleanExpression<E> lvalue, final BooleanExpression<E> rvalue)
+ {
+ super(lvalue, rvalue);
+ }
+
+ public Object evaluate(Filterable<E> message) throws E
+ {
+
+ Boolean lv = (Boolean) left.evaluate(message);
+ // Can we do an OR shortcut??
+ if ((lv != null) && lv.booleanValue())
+ {
+ return Boolean.TRUE;
+ }
+
+ Boolean rv = (Boolean) right.evaluate(message);
+
+ return (rv == null) ? null : rv;
+ }
+
+ public String getExpressionSymbol()
+ {
+ return "OR";
+ }
+ }
+
+ private static class AndExpression<E extends Exception> extends LogicExpression<E>
+ {
+ public AndExpression(final BooleanExpression<E> lvalue, final BooleanExpression<E> rvalue)
+ {
+ super(lvalue, rvalue);
+ }
+
+ public Object evaluate(Filterable<E> message) throws E
+ {
+
+ Boolean lv = (Boolean) left.evaluate(message);
+
+ // Can we do an AND shortcut??
+ if (lv == null)
+ {
+ return null;
+ }
+
+ if (!lv.booleanValue())
+ {
+ return Boolean.FALSE;
+ }
+
+ Boolean rv = (Boolean) right.evaluate(message);
+
+ return (rv == null) ? null : rv;
+ }
+
+ public String getExpressionSymbol()
+ {
+ return "AND";
+ }
+ }
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/filter/MessageFilter.java b/java/broker/src/main/java/org/apache/qpid/server/filter/MessageFilter.java
index e6bfe974d5..58fc55f8e6 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/filter/MessageFilter.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/filter/MessageFilter.java
@@ -22,8 +22,9 @@ package org.apache.qpid.server.filter;
import org.apache.qpid.AMQException;
import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.queue.Filterable;
-public interface MessageFilter
+public interface MessageFilter<E extends Exception>
{
- boolean matches(AMQMessage message) throws AMQException;
+ boolean matches(Filterable<E> message) throws E;
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/filter/NoConsumerFilter.java b/java/broker/src/main/java/org/apache/qpid/server/filter/NoConsumerFilter.java
index 47ca930d12..f1b3b2511d 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/filter/NoConsumerFilter.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/filter/NoConsumerFilter.java
@@ -22,7 +22,7 @@ package org.apache.qpid.server.filter;
import org.apache.log4j.Logger;
import org.apache.qpid.AMQException;
-import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.queue.Filterable;
public class NoConsumerFilter implements MessageFilter
{
@@ -34,7 +34,7 @@ public class NoConsumerFilter implements MessageFilter
_logger.info("Created NoConsumerFilter");
}
- public boolean matches(AMQMessage message)
+ public boolean matches(Filterable message)
{
return true;
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/filter/PropertyExpression.java b/java/broker/src/main/java/org/apache/qpid/server/filter/PropertyExpression.java
index 5ab360ca19..b30c70dac3 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/filter/PropertyExpression.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/filter/PropertyExpression.java
@@ -30,12 +30,12 @@ import org.apache.log4j.Logger;
import org.apache.qpid.AMQException;
import org.apache.qpid.framing.AMQShortString;
import org.apache.qpid.framing.CommonContentHeaderProperties;
-import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.queue.Filterable;
/**
* Represents a property expression
*/
-public class PropertyExpression implements Expression
+public class PropertyExpression<E extends Exception> implements Expression<E>
{
// Constants - defined the same as JMS
private static final int NON_PERSISTENT = 1;
@@ -44,223 +44,60 @@ public class PropertyExpression implements Expression
private static final Logger _logger = org.apache.log4j.Logger.getLogger(PropertyExpression.class);
- private static final HashMap<String, Expression> JMS_PROPERTY_EXPRESSIONS = new HashMap<String, Expression>();
+ private static final HashMap<String, Expression<? extends Exception>> JMS_PROPERTY_EXPRESSIONS = new HashMap<String, Expression<? extends Exception>>();
- static
{
- JMS_PROPERTY_EXPRESSIONS.put("JMSDestination", new Expression()
+ JMS_PROPERTY_EXPRESSIONS.put("JMSDestination", new Expression<E>()
{
- public Object evaluate(AMQMessage message)
+ public Object evaluate(Filterable<E> message)
{
//TODO
return null;
}
});
- JMS_PROPERTY_EXPRESSIONS.put("JMSReplyTo", new Expression()
- {
- public Object evaluate(AMQMessage message)
- {
- try
- {
- CommonContentHeaderProperties _properties =
- (CommonContentHeaderProperties)
- message.getContentHeaderBody().properties;
- AMQShortString replyTo = _properties.getReplyTo();
-
- return (replyTo == null) ? null : replyTo.toString();
- }
- catch (AMQException e)
- {
- _logger.warn(e);
-
- return null;
- }
-
- }
+ JMS_PROPERTY_EXPRESSIONS.put("JMSReplyTo", new ReplyToExpression());
- });
-
- JMS_PROPERTY_EXPRESSIONS.put("JMSType", new Expression()
- {
- public Object evaluate(AMQMessage message)
- {
- try
- {
- CommonContentHeaderProperties _properties =
- (CommonContentHeaderProperties)
- message.getContentHeaderBody().properties;
- AMQShortString type = _properties.getType();
-
- return (type == null) ? null : type.toString();
- }
- catch (AMQException e)
- {
- _logger.warn(e);
-
- return null;
- }
-
- }
- });
-
- JMS_PROPERTY_EXPRESSIONS.put("JMSDeliveryMode", new Expression()
- {
- public Object evaluate(AMQMessage message)
- {
- try
- {
- int mode = message.isPersistent() ? PERSISTENT : NON_PERSISTENT;
- if (_logger.isDebugEnabled())
- {
- _logger.debug("JMSDeliveryMode is :" + mode);
- }
-
- return mode;
- }
- catch (AMQException e)
- {
- _logger.warn(e);
- }
-
- return NON_PERSISTENT;
- }
- });
+ JMS_PROPERTY_EXPRESSIONS.put("JMSType", new TypeExpression());
- JMS_PROPERTY_EXPRESSIONS.put("JMSPriority", new Expression()
- {
- public Object evaluate(AMQMessage message)
- {
- try
- {
- CommonContentHeaderProperties _properties =
- (CommonContentHeaderProperties)
- message.getContentHeaderBody().properties;
-
- return (int) _properties.getPriority();
- }
- catch (AMQException e)
- {
- _logger.warn(e);
- }
-
- return DEFAULT_PRIORITY;
- }
- });
+ JMS_PROPERTY_EXPRESSIONS.put("JMSDeliveryMode", new DeliveryModeExpression());
- JMS_PROPERTY_EXPRESSIONS.put("AMQMessageID", new Expression()
- {
- public Object evaluate(AMQMessage message)
- {
+ JMS_PROPERTY_EXPRESSIONS.put("JMSPriority", new PriorityExpression());
- try
- {
- CommonContentHeaderProperties _properties =
- (CommonContentHeaderProperties)
- message.getContentHeaderBody().properties;
- AMQShortString messageId = _properties.getMessageId();
+ JMS_PROPERTY_EXPRESSIONS.put("AMQMessageID", new MessageIDExpression());
- return (messageId == null) ? null : messageId;
- }
- catch (AMQException e)
- {
- _logger.warn(e);
+ JMS_PROPERTY_EXPRESSIONS.put("JMSTimestamp", new TimestampExpression());
- return null;
- }
+ JMS_PROPERTY_EXPRESSIONS.put("JMSCorrelationID", new CorrelationIdExpression());
- }
- });
+ JMS_PROPERTY_EXPRESSIONS.put("JMSExpiration", new ExpirationExpression());
- JMS_PROPERTY_EXPRESSIONS.put("JMSTimestamp", new Expression()
+ JMS_PROPERTY_EXPRESSIONS.put("JMSRedelivered", new Expression<E>()
{
- public Object evaluate(AMQMessage message)
- {
-
- try
- {
- CommonContentHeaderProperties _properties =
- (CommonContentHeaderProperties)
- message.getContentHeaderBody().properties;
-
- return _properties.getTimestamp();
- }
- catch (AMQException e)
- {
- _logger.warn(e);
-
- return null;
- }
-
- }
- });
-
- JMS_PROPERTY_EXPRESSIONS.put("JMSCorrelationID", new Expression()
- {
- public Object evaluate(AMQMessage message)
- {
-
- try
- {
- CommonContentHeaderProperties _properties =
- (CommonContentHeaderProperties)
- message.getContentHeaderBody().properties;
- AMQShortString correlationId = _properties.getCorrelationId();
-
- return (correlationId == null) ? null : correlationId.toString();
- }
- catch (AMQException e)
- {
- _logger.warn(e);
-
- return null;
- }
-
- }
- });
-
- JMS_PROPERTY_EXPRESSIONS.put("JMSExpiration", new Expression()
- {
- public Object evaluate(AMQMessage message)
- {
-
- try
- {
- CommonContentHeaderProperties _properties =
- (CommonContentHeaderProperties)
- message.getContentHeaderBody().properties;
-
- return _properties.getExpiration();
- }
- catch (AMQException e)
- {
- _logger.warn(e);
-
- return null;
- }
-
- }
- });
-
- JMS_PROPERTY_EXPRESSIONS.put("JMSRedelivered", new Expression()
- {
- public Object evaluate(AMQMessage message)
+ public Object evaluate(Filterable message) throws E
{
return message.isRedelivered();
}
});
-
}
private final String name;
- private final Expression jmsPropertyExpression;
+ private final Expression<E> jmsPropertyExpression;
+
+ public boolean outerTest()
+ {
+ return false;
+ }
public PropertyExpression(String name)
{
this.name = name;
- jmsPropertyExpression = JMS_PROPERTY_EXPRESSIONS.get(name);
+
+
+
+ jmsPropertyExpression = (Expression<E>) JMS_PROPERTY_EXPRESSIONS.get(name);
}
- public Object evaluate(AMQMessage message) throws AMQException
+ public Object evaluate(Filterable<E> message) throws E
{
if (jmsPropertyExpression != null)
@@ -319,4 +156,113 @@ public class PropertyExpression implements Expression
}
+ private static class ReplyToExpression<E extends Exception> implements Expression<E>
+ {
+ public Object evaluate(Filterable<E> message) throws E
+ {
+
+ CommonContentHeaderProperties _properties =
+ (CommonContentHeaderProperties)
+ message.getContentHeaderBody().properties;
+ AMQShortString replyTo = _properties.getReplyTo();
+
+ return (replyTo == null) ? null : replyTo.toString();
+
+ }
+
+ }
+
+ private static class TypeExpression<E extends Exception> implements Expression<E>
+ {
+ public Object evaluate(Filterable<E> message) throws E
+ {
+ CommonContentHeaderProperties _properties =
+ (CommonContentHeaderProperties)
+ message.getContentHeaderBody().properties;
+ AMQShortString type = _properties.getType();
+
+ return (type == null) ? null : type.toString();
+
+ }
+ }
+
+ private static class DeliveryModeExpression<E extends Exception> implements Expression<E>
+ {
+ public Object evaluate(Filterable<E> message) throws E
+ {
+ int mode = message.isPersistent() ? PERSISTENT : NON_PERSISTENT;
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("JMSDeliveryMode is :" + mode);
+ }
+
+ return mode;
+ }
+ }
+
+ private static class PriorityExpression<E extends Exception> implements Expression<E>
+ {
+ public Object evaluate(Filterable<E> message) throws E
+ {
+ CommonContentHeaderProperties _properties =
+ (CommonContentHeaderProperties)
+ message.getContentHeaderBody().properties;
+
+ return (int) _properties.getPriority();
+ }
+ }
+
+ private static class MessageIDExpression<E extends Exception> implements Expression<E>
+ {
+ public Object evaluate(Filterable<E> message) throws E
+ {
+
+ CommonContentHeaderProperties _properties =
+ (CommonContentHeaderProperties)
+ message.getContentHeaderBody().properties;
+ AMQShortString messageId = _properties.getMessageId();
+
+ return (messageId == null) ? null : messageId;
+
+ }
+ }
+
+ private static class TimestampExpression<E extends Exception> implements Expression<E>
+ {
+ public Object evaluate(Filterable<E> message) throws E
+ {
+ CommonContentHeaderProperties _properties =
+ (CommonContentHeaderProperties)
+ message.getContentHeaderBody().properties;
+
+ return _properties.getTimestamp();
+ }
+ }
+
+ private static class CorrelationIdExpression<E extends Exception> implements Expression<E>
+ {
+ public Object evaluate(Filterable<E> message) throws E
+ {
+ CommonContentHeaderProperties _properties =
+ (CommonContentHeaderProperties)
+ message.getContentHeaderBody().properties;
+ AMQShortString correlationId = _properties.getCorrelationId();
+
+ return (correlationId == null) ? null : correlationId.toString();
+ }
+ }
+
+ private static class ExpirationExpression<E extends Exception> implements Expression<E>
+ {
+ public Object evaluate(Filterable<E> message) throws E
+ {
+
+ CommonContentHeaderProperties _properties =
+ (CommonContentHeaderProperties)
+ message.getContentHeaderBody().properties;
+
+ return _properties.getExpiration();
+
+ }
+ }
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/filter/SimpleFilterManager.java b/java/broker/src/main/java/org/apache/qpid/server/filter/SimpleFilterManager.java
index 62a45f5420..cb738e1489 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/filter/SimpleFilterManager.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/filter/SimpleFilterManager.java
@@ -25,32 +25,33 @@ import java.util.concurrent.ConcurrentLinkedQueue;
import org.apache.log4j.Logger;
import org.apache.qpid.AMQException;
import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.queue.Filterable;
-public class SimpleFilterManager implements FilterManager
+public class SimpleFilterManager implements FilterManager<AMQException>
{
private final Logger _logger = Logger.getLogger(SimpleFilterManager.class);
- private final ConcurrentLinkedQueue<MessageFilter> _filters;
+ private final ConcurrentLinkedQueue<MessageFilter<AMQException>> _filters;
public SimpleFilterManager()
{
_logger.debug("Creating SimpleFilterManager");
- _filters = new ConcurrentLinkedQueue<MessageFilter>();
+ _filters = new ConcurrentLinkedQueue<MessageFilter<AMQException>>();
}
- public void add(MessageFilter filter)
+ public void add(MessageFilter<AMQException> filter)
{
_filters.add(filter);
}
- public void remove(MessageFilter filter)
+ public void remove(MessageFilter<AMQException> filter)
{
_filters.remove(filter);
}
- public boolean allAllow(AMQMessage msg)
+ public boolean allAllow(Filterable<AMQException> msg)
{
- for (MessageFilter filter : _filters)
+ for (MessageFilter<AMQException> filter : _filters)
{
try
{
diff --git a/java/broker/src/main/java/org/apache/qpid/server/filter/UnaryExpression.java b/java/broker/src/main/java/org/apache/qpid/server/filter/UnaryExpression.java
index 83b4ed5358..799a38af5a 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/filter/UnaryExpression.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/filter/UnaryExpression.java
@@ -30,45 +30,23 @@ import java.util.Iterator;
import java.util.List;
import org.apache.qpid.AMQException;
-import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.queue.Filterable;
/**
* An expression which performs an operation on two expression values
*/
-public abstract class UnaryExpression implements Expression
+public abstract class UnaryExpression<E extends Exception> implements Expression<E>
{
private static final BigDecimal BD_LONG_MIN_VALUE = BigDecimal.valueOf(Long.MIN_VALUE);
- protected Expression right;
+ protected Expression<E> right;
- public static Expression createNegate(Expression left)
+ public static<E extends Exception> Expression<E> createNegate(Expression<E> left)
{
- return new UnaryExpression(left)
- {
- public Object evaluate(AMQMessage message) throws AMQException
- {
- Object rvalue = right.evaluate(message);
- if (rvalue == null)
- {
- return null;
- }
-
- if (rvalue instanceof Number)
- {
- return negate((Number) rvalue);
- }
-
- return null;
- }
-
- public String getExpressionSymbol()
- {
- return "-";
- }
- };
+ return new NegativeExpression(left);
}
- public static BooleanExpression createInExpression(PropertyExpression right, List elements, final boolean not)
+ public static<E extends Exception> BooleanExpression createInExpression(PropertyExpression<E> right, List elements, final boolean not)
{
// Use a HashSet if there are many elements.
@@ -88,81 +66,17 @@ public abstract class UnaryExpression implements Expression
final Collection inList = t;
- return new BooleanUnaryExpression(right)
- {
- public Object evaluate(AMQMessage message) throws AMQException
- {
-
- Object rvalue = right.evaluate(message);
- if (rvalue == null)
- {
- return null;
- }
-
- if (rvalue.getClass() != String.class)
- {
- return null;
- }
-
- if (((inList != null) && inList.contains(rvalue)) ^ not)
- {
- return Boolean.TRUE;
- }
- else
- {
- return Boolean.FALSE;
- }
-
- }
-
- public String toString()
- {
- StringBuffer answer = new StringBuffer();
- answer.append(right);
- answer.append(" ");
- answer.append(getExpressionSymbol());
- answer.append(" ( ");
-
- int count = 0;
- for (Iterator i = inList.iterator(); i.hasNext();)
- {
- Object o = (Object) i.next();
- if (count != 0)
- {
- answer.append(", ");
- }
-
- answer.append(o);
- count++;
- }
-
- answer.append(" )");
-
- return answer.toString();
- }
-
- public String getExpressionSymbol()
- {
- if (not)
- {
- return "NOT IN";
- }
- else
- {
- return "IN";
- }
- }
- };
+ return new InExpression(right, inList, not);
}
- abstract static class BooleanUnaryExpression extends UnaryExpression implements BooleanExpression
+ abstract static class BooleanUnaryExpression<E extends Exception> extends UnaryExpression<E> implements BooleanExpression<E>
{
- public BooleanUnaryExpression(Expression left)
+ public BooleanUnaryExpression(Expression<E> left)
{
super(left);
}
- public boolean matches(AMQMessage message) throws AMQException
+ public boolean matches(Filterable<E> message) throws E
{
Object object = evaluate(message);
@@ -171,26 +85,9 @@ public abstract class UnaryExpression implements Expression
}
;
- public static BooleanExpression createNOT(BooleanExpression left)
+ public static<E extends Exception> BooleanExpression<E> createNOT(BooleanExpression<E> left)
{
- return new BooleanUnaryExpression(left)
- {
- public Object evaluate(AMQMessage message) throws AMQException
- {
- Boolean lvalue = (Boolean) right.evaluate(message);
- if (lvalue == null)
- {
- return null;
- }
-
- return lvalue.booleanValue() ? Boolean.FALSE : Boolean.TRUE;
- }
-
- public String getExpressionSymbol()
- {
- return "NOT";
- }
- };
+ return new NotExpression(left);
}
public static BooleanExpression createXPath(final String xpath)
@@ -203,36 +100,9 @@ public abstract class UnaryExpression implements Expression
return new XQueryExpression(xpath);
}
- public static BooleanExpression createBooleanCast(Expression left)
+ public static<E extends Exception> BooleanExpression createBooleanCast(Expression<E> left)
{
- return new BooleanUnaryExpression(left)
- {
- public Object evaluate(AMQMessage message) throws AMQException
- {
- Object rvalue = right.evaluate(message);
- if (rvalue == null)
- {
- return null;
- }
-
- if (!rvalue.getClass().equals(Boolean.class))
- {
- return Boolean.FALSE;
- }
-
- return ((Boolean) rvalue).booleanValue() ? Boolean.TRUE : Boolean.FALSE;
- }
-
- public String toString()
- {
- return right.toString();
- }
-
- public String getExpressionSymbol()
- {
- return "";
- }
- };
+ return new BooleanCastExpression(left);
}
private static Number negate(Number left)
@@ -281,7 +151,7 @@ public abstract class UnaryExpression implements Expression
this.right = left;
}
- public Expression getRight()
+ public Expression<E> getRight()
{
return right;
}
@@ -334,4 +204,166 @@ public abstract class UnaryExpression implements Expression
*/
public abstract String getExpressionSymbol();
+ private static class NegativeExpression<E extends Exception> extends UnaryExpression<E>
+ {
+ public NegativeExpression(final Expression<E> left)
+ {
+ super(left);
+ }
+
+ public Object evaluate(Filterable<E> message) throws E
+ {
+ Object rvalue = right.evaluate(message);
+ if (rvalue == null)
+ {
+ return null;
+ }
+
+ if (rvalue instanceof Number)
+ {
+ return negate((Number) rvalue);
+ }
+
+ return null;
+ }
+
+ public String getExpressionSymbol()
+ {
+ return "-";
+ }
+ }
+
+ private static class InExpression<E extends Exception> extends BooleanUnaryExpression<E>
+ {
+ private final Collection _inList;
+ private final boolean _not;
+
+ public InExpression(final PropertyExpression<E> right, final Collection inList, final boolean not)
+ {
+ super(right);
+ _inList = inList;
+ _not = not;
+ }
+
+ public Object evaluate(Filterable<E> message) throws E
+ {
+
+ Object rvalue = right.evaluate(message);
+ if (rvalue == null)
+ {
+ return null;
+ }
+
+ if (rvalue.getClass() != String.class)
+ {
+ return null;
+ }
+
+ if (((_inList != null) && _inList.contains(rvalue)) ^ _not)
+ {
+ return Boolean.TRUE;
+ }
+ else
+ {
+ return Boolean.FALSE;
+ }
+
+ }
+
+ public String toString()
+ {
+ StringBuffer answer = new StringBuffer();
+ answer.append(right);
+ answer.append(" ");
+ answer.append(getExpressionSymbol());
+ answer.append(" ( ");
+
+ int count = 0;
+ for (Iterator i = _inList.iterator(); i.hasNext();)
+ {
+ Object o = (Object) i.next();
+ if (count != 0)
+ {
+ answer.append(", ");
+ }
+
+ answer.append(o);
+ count++;
+ }
+
+ answer.append(" )");
+
+ return answer.toString();
+ }
+
+ public String getExpressionSymbol()
+ {
+ if (_not)
+ {
+ return "NOT IN";
+ }
+ else
+ {
+ return "IN";
+ }
+ }
+ }
+
+ private static class NotExpression<E extends Exception> extends BooleanUnaryExpression<E>
+ {
+ public NotExpression(final BooleanExpression<E> left)
+ {
+ super(left);
+ }
+
+ public Object evaluate(Filterable<E> message) throws E
+ {
+ Boolean lvalue = (Boolean) right.evaluate(message);
+ if (lvalue == null)
+ {
+ return null;
+ }
+
+ return lvalue.booleanValue() ? Boolean.FALSE : Boolean.TRUE;
+ }
+
+ public String getExpressionSymbol()
+ {
+ return "NOT";
+ }
+ }
+
+ private static class BooleanCastExpression<E extends Exception> extends BooleanUnaryExpression<E>
+ {
+ public BooleanCastExpression(final Expression<E> left)
+ {
+ super(left);
+ }
+
+ public Object evaluate(Filterable<E> message) throws E
+ {
+ Object rvalue = right.evaluate(message);
+ if (rvalue == null)
+ {
+ return null;
+ }
+
+ if (!rvalue.getClass().equals(Boolean.class))
+ {
+ return Boolean.FALSE;
+ }
+
+ return ((Boolean) rvalue).booleanValue() ? Boolean.TRUE : Boolean.FALSE;
+ }
+
+ public String toString()
+ {
+ return right.toString();
+ }
+
+ public String getExpressionSymbol()
+ {
+ return "";
+ }
+ }
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/filter/XPathExpression.java b/java/broker/src/main/java/org/apache/qpid/server/filter/XPathExpression.java
index f5454afae5..1311178fb1 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/filter/XPathExpression.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/filter/XPathExpression.java
@@ -23,6 +23,7 @@ package org.apache.qpid.server.filter;
import org.apache.log4j.Logger;
import org.apache.qpid.AMQException;
import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.queue.Filterable;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
@@ -70,7 +71,7 @@ public final class XPathExpression implements BooleanExpression {
private final XPathEvaluator evaluator;
static public interface XPathEvaluator {
- public boolean evaluate(AMQMessage message) throws AMQException;
+ public boolean evaluate(Filterable message) throws AMQException;
}
XPathExpression(String xpath) {
@@ -92,7 +93,7 @@ public final class XPathExpression implements BooleanExpression {
}
}
- public Object evaluate(AMQMessage message) throws AMQException {
+ public Object evaluate(Filterable message) throws AMQException {
// try {
//FIXME this is flow to disk work
// if( message.isDropped() )
@@ -117,7 +118,7 @@ public final class XPathExpression implements BooleanExpression {
* @return true if the expression evaluates to Boolean.TRUE.
* @throws AMQException
*/
- public boolean matches(AMQMessage message) throws AMQException
+ public boolean matches(Filterable message) throws AMQException
{
Object object = evaluate(message);
return object!=null && object==Boolean.TRUE;
diff --git a/java/broker/src/main/java/org/apache/qpid/server/filter/XQueryExpression.java b/java/broker/src/main/java/org/apache/qpid/server/filter/XQueryExpression.java
index f5debb607a..c13f81cd08 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/filter/XQueryExpression.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/filter/XQueryExpression.java
@@ -19,6 +19,7 @@ package org.apache.qpid.server.filter;
import org.apache.qpid.AMQException;
import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.queue.Filterable;
//
// Based on like named file from r450141 of the Apache ActiveMQ project <http://www.activemq.org/site/home.html>
@@ -35,7 +36,7 @@ public final class XQueryExpression implements BooleanExpression {
this.xpath = xpath;
}
- public Object evaluate(AMQMessage message) throws AMQException {
+ public Object evaluate(Filterable message) throws AMQException {
return Boolean.FALSE;
}
@@ -48,7 +49,7 @@ public final class XQueryExpression implements BooleanExpression {
* @return true if the expression evaluates to Boolean.TRUE.
* @throws AMQException
*/
- public boolean matches(AMQMessage message) throws AMQException
+ public boolean matches(Filterable message) throws AMQException
{
Object object = evaluate(message);
return object!=null && object==Boolean.TRUE;
diff --git a/java/broker/src/main/java/org/apache/qpid/server/filter/XalanXPathEvaluator.java b/java/broker/src/main/java/org/apache/qpid/server/filter/XalanXPathEvaluator.java
index 35d770fd5d..cc67776682 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/filter/XalanXPathEvaluator.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/filter/XalanXPathEvaluator.java
@@ -29,6 +29,7 @@ import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.qpid.AMQException;
import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.queue.Filterable;
import org.apache.xpath.CachedXPathAPI;
import org.w3c.dom.Document;
import org.w3c.dom.traversal.NodeIterator;
@@ -42,7 +43,7 @@ public class XalanXPathEvaluator implements XPathExpression.XPathEvaluator {
this.xpath = xpath;
}
- public boolean evaluate(AMQMessage m) throws AMQException
+ public boolean evaluate(Filterable m) throws AMQException
{
// TODO - we would have to check the content type and then evaluate the content
// here... is this really a feature we wish to implement? - RobG
diff --git a/java/broker/src/main/java/org/apache/qpid/server/flow/AbstractFlowCreditManager.java b/java/broker/src/main/java/org/apache/qpid/server/flow/AbstractFlowCreditManager.java
index af49e113f6..895db7b15b 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/flow/AbstractFlowCreditManager.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/flow/AbstractFlowCreditManager.java
@@ -54,4 +54,9 @@ public abstract class AbstractFlowCreditManager implements FlowCreditManager
notifyListeners(suspended);
}
}
+
+ protected final void notifyIncreaseBytesCredit()
+ {
+ notifyListeners(false);
+ }
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/flow/MessageOnlyCreditManager.java b/java/broker/src/main/java/org/apache/qpid/server/flow/MessageOnlyCreditManager.java
index 6cff2d8488..c1b3a09006 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/flow/MessageOnlyCreditManager.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/flow/MessageOnlyCreditManager.java
@@ -28,7 +28,7 @@ public class MessageOnlyCreditManager extends AbstractFlowCreditManager implemen
{
private final AtomicLong _messageCredit;
- MessageOnlyCreditManager(final long initialCredit)
+ public MessageOnlyCreditManager(final long initialCredit)
{
_messageCredit = new AtomicLong(initialCredit);
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/flow/Pre0_10CreditManager.java b/java/broker/src/main/java/org/apache/qpid/server/flow/Pre0_10CreditManager.java
index d9752b1098..be0300f2c1 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/flow/Pre0_10CreditManager.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/flow/Pre0_10CreditManager.java
@@ -84,8 +84,10 @@ public class Pre0_10CreditManager extends AbstractFlowCreditManager implements F
public synchronized void addCredit(final long messageCredit, final long bytesCredit)
{
final long messageCreditLimit = _messageCreditLimit;
+ boolean notifyIncrease = true;
if(messageCreditLimit != 0L)
{
+ notifyIncrease = (_messageCredit != 0);
long newCredit = _messageCredit + messageCredit;
_messageCredit = newCredit > messageCreditLimit ? messageCreditLimit : newCredit;
}
@@ -96,8 +98,14 @@ public class Pre0_10CreditManager extends AbstractFlowCreditManager implements F
{
long newCredit = _bytesCredit + bytesCredit;
_bytesCredit = newCredit > bytesCreditLimit ? bytesCreditLimit : newCredit;
+ if(notifyIncrease && bytesCredit>0)
+ {
+ notifyIncreaseBytesCredit();
+ }
}
+
+
setSuspended(!hasCredit());
}
@@ -138,7 +146,7 @@ public class Pre0_10CreditManager extends AbstractFlowCreditManager implements F
}
else
{
- setSuspended(true);
+ //setSuspended(true);
return false;
}
}
@@ -166,7 +174,7 @@ public class Pre0_10CreditManager extends AbstractFlowCreditManager implements F
}
else
{
- setSuspended(true);
+ //setSuspended(true);
return false;
}
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/BasicGetMethodHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/BasicGetMethodHandler.java
index b97831c3ce..be1135dd91 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/handler/BasicGetMethodHandler.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/handler/BasicGetMethodHandler.java
@@ -26,11 +26,21 @@ import org.apache.qpid.AMQException;
import org.apache.qpid.framing.BasicGetBody;
import org.apache.qpid.framing.BasicGetEmptyBody;
import org.apache.qpid.framing.MethodRegistry;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.FieldTable;
import org.apache.qpid.protocol.AMQConstant;
import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.flow.FlowCreditManager;
+import org.apache.qpid.server.flow.MessageOnlyCreditManager;
+import org.apache.qpid.server.subscription.SubscriptionImpl;
+import org.apache.qpid.server.subscription.ClientDeliveryMethod;
+import org.apache.qpid.server.subscription.RecordDeliveryMethod;
+import org.apache.qpid.server.subscription.Subscription;
+import org.apache.qpid.server.subscription.SubscriptionFactoryImpl;
import org.apache.qpid.server.protocol.AMQProtocolSession;
-import org.apache.qpid.server.queue.AMQQueueImpl;
import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.queue.QueueEntry;
+import org.apache.qpid.server.queue.SimpleAMQQueue;
import org.apache.qpid.server.security.access.Permission;
import org.apache.qpid.server.state.AMQStateManager;
import org.apache.qpid.server.state.StateAwareMethodListener;
@@ -86,7 +96,7 @@ public class BasicGetMethodHandler implements StateAwareMethodListener<BasicGetB
//Perform ACLs
vHost.getAccessManager().authorise(session, Permission.CONSUME, body, queue);
- if (!queue.performGet(session, channel, !body.getNoAck()))
+ if (!performGet(queue,session, channel, !body.getNoAck()))
{
MethodRegistry methodRegistry = session.getMethodRegistry();
// TODO - set clusterId
@@ -98,4 +108,80 @@ public class BasicGetMethodHandler implements StateAwareMethodListener<BasicGetB
}
}
}
+
+ public static boolean performGet(final AMQQueue queue,
+ final AMQProtocolSession session,
+ final AMQChannel channel,
+ final boolean acks)
+ throws AMQException
+ {
+
+ final FlowCreditManager singleMessageCredit = new MessageOnlyCreditManager(1L);
+
+ final ClientDeliveryMethod getDeliveryMethod = new ClientDeliveryMethod()
+ {
+
+ int _msg;
+
+ public void deliverToClient(final Subscription sub, final QueueEntry entry, final long deliveryTag)
+ throws AMQException
+ {
+ singleMessageCredit.useCreditForMessage(entry.getMessage());
+ session.getProtocolOutputConverter().writeGetOk(entry.getMessage(), channel.getChannelId(),
+ deliveryTag, queue.getMessageCount());
+
+ }
+ };
+ final RecordDeliveryMethod getRecordMethod = new RecordDeliveryMethod()
+ {
+
+ public void recordMessageDelivery(final Subscription sub, final QueueEntry entry, final long deliveryTag)
+ {
+ channel.addUnacknowledgedMessage(entry, deliveryTag, null);
+ }
+ };
+
+ Subscription sub;
+ if(acks)
+ {
+ sub = SubscriptionFactoryImpl.INSTANCE.createSubscription(channel, session, null, acks, null, false, singleMessageCredit, getDeliveryMethod, getRecordMethod);
+ }
+ else
+ {
+ sub = new GetNoAckSubscription(channel,
+ session,
+ null,
+ null,
+ false,
+ singleMessageCredit,
+ getDeliveryMethod,
+ getRecordMethod);
+ }
+
+ queue.registerSubscription(sub,false);
+ queue.flushSubscription(sub);
+ queue.unregisterSubscription(sub);
+ return(!singleMessageCredit.hasCredit());
+
+
+ }
+
+ public static final class GetNoAckSubscription extends SubscriptionImpl.NoAckSubscription
+ {
+ public GetNoAckSubscription(AMQChannel channel, AMQProtocolSession protocolSession,
+ AMQShortString consumerTag, FieldTable filters,
+ boolean noLocal, FlowCreditManager creditManager,
+ ClientDeliveryMethod deliveryMethod,
+ RecordDeliveryMethod recordMethod)
+ throws AMQException
+ {
+ super(channel, protocolSession, consumerTag, filters, noLocal, creditManager, deliveryMethod, recordMethod);
+ }
+
+ public boolean wouldSuspend(QueueEntry msg)
+ {
+ return !getCreditManager().useCreditForMessage(msg.getMessage());
+ }
+
+ }
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeclareHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeclareHandler.java
index 2241281be1..379ec7a7d6 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeclareHandler.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeclareHandler.java
@@ -174,7 +174,9 @@ public class QueueDeclareHandler implements StateAwareMethodListener<QueueDeclar
{
final QueueRegistry registry = virtualHost.getQueueRegistry();
AMQShortString owner = body.getExclusive() ? session.getContextKey() : null;
- final AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(queueName, body.getDurable(), owner, body.getAutoDelete(), virtualHost);
+
+ final AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(queueName, body.getDurable(), owner, body.getAutoDelete(), virtualHost,
+ body.getArguments());
if (body.getExclusive() && !body.getDurable())
diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQMessage.java b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQMessage.java
index cf3d07160f..e790493a82 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQMessage.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQMessage.java
@@ -42,7 +42,7 @@ import java.util.concurrent.atomic.AtomicInteger;
/**
* A deliverable message.
*/
-public class AMQMessage
+public class AMQMessage implements Filterable<AMQException>
{
/** Used for debugging purposes. */
private static final Logger _log = Logger.getLogger(AMQMessage.class);
diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQPriorityQueue.java b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQPriorityQueue.java
new file mode 100644
index 0000000000..8ef28fbcd2
--- /dev/null
+++ b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQPriorityQueue.java
@@ -0,0 +1,41 @@
+/*
+*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*
+*/
+package org.apache.qpid.server.queue;
+
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+import org.apache.qpid.AMQException;
+
+public class AMQPriorityQueue extends SimpleAMQQueue
+{
+ protected AMQPriorityQueue(final AMQShortString name,
+ final boolean durable,
+ final AMQShortString owner,
+ final boolean autoDelete,
+ final VirtualHost virtualHost,
+ int priorities)
+ throws AMQException
+ {
+ super(name, durable, owner, autoDelete, virtualHost, new PriorityQueueList.Factory(priorities));
+ }
+
+
+}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java
index fcfe26c4bd..98583c03d4 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java
@@ -34,7 +34,7 @@ import org.apache.qpid.AMQException;
import java.util.List;
import java.util.Set;
-public interface AMQQueue extends Managable, Comparable
+public interface AMQQueue extends Managable, Comparable<AMQQueue>
{
AMQShortString getName();
@@ -147,11 +147,6 @@ public interface AMQQueue extends Managable, Comparable
void deliverAsync();
-
- boolean performGet(final AMQProtocolSession session, final AMQChannel channel, final boolean b) throws AMQException;
-
-
-
void addQueueDeleteTask(final Task task);
boolean resend(final QueueEntry entry, final Subscription subscription) throws AMQException;
@@ -160,6 +155,8 @@ public interface AMQQueue extends Managable, Comparable
Set<NotificationCheck> getNotificationChecks();
+ void flushSubscription(final Subscription sub) throws AMQException;
+
/**
* ExistingExclusiveSubscription signals a failure to create a subscription, because an exclusive subscription
diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueFactory.java b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueFactory.java
index 99da9c20fa..e751212272 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueFactory.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueFactory.java
@@ -21,20 +21,32 @@
package org.apache.qpid.server.queue;
import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.FieldTable;
import org.apache.qpid.server.virtualhost.VirtualHost;
import org.apache.qpid.AMQException;
public class AMQQueueFactory
{
+ private static final AMQShortString X_QPID_PRIORITIES = new AMQShortString("x-qpid-priorities");
+
public static AMQQueue createAMQQueueImpl(AMQShortString name,
- boolean durable,
- AMQShortString owner,
- boolean autoDelete,
- VirtualHost virtualHost)
+ boolean durable,
+ AMQShortString owner,
+ boolean autoDelete,
+ VirtualHost virtualHost, final FieldTable arguments)
throws AMQException
{
- //return new AMQQueueImpl(name, durable, owner, autoDelete, virtualHost);
- return new SimpleAMQQueue(name, durable, owner, autoDelete, virtualHost);
+
+ final int priorities = arguments.containsKey(X_QPID_PRIORITIES) ? arguments.getInteger(X_QPID_PRIORITIES) : 1;
+
+ if(priorities > 1)
+ {
+ return new AMQPriorityQueue(name, durable, owner, autoDelete, virtualHost, priorities);
+ }
+ else
+ {
+ return new SimpleAMQQueue(name, durable, owner, autoDelete, virtualHost);
+ }
}
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueImpl.java b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueImpl.java
deleted file mode 100644
index d8d0e599ed..0000000000
--- a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueImpl.java
+++ /dev/null
@@ -1,1003 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.queue;
-
-import org.apache.log4j.Logger;
-import org.apache.qpid.AMQException;
-import org.apache.qpid.configuration.Configured;
-import org.apache.qpid.framing.AMQShortString;
-import org.apache.qpid.framing.FieldTable;
-import org.apache.qpid.server.AMQChannel;
-import org.apache.qpid.server.subscription.Subscription;
-import org.apache.qpid.server.exchange.Exchange;
-import org.apache.qpid.server.management.ManagedObject;
-import org.apache.qpid.server.protocol.AMQProtocolSession;
-import org.apache.qpid.server.store.MessageStore;
-import org.apache.qpid.server.store.StoreContext;
-import org.apache.qpid.server.virtualhost.VirtualHost;
-
-import javax.management.JMException;
-import java.text.MessageFormat;
-import java.util.*;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.Executor;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicLong;
-
-/**
- * This is an AMQ Queue, and should not be confused with a JMS queue or any other abstraction like that. It is described
- * fully in RFC 006.
- */
-public class AMQQueueImpl implements AMQQueue, Subscription.StateListener
-{
-
- private static final Logger _logger = Logger.getLogger(AMQQueueImpl.class);
-
- private final AMQShortString _name;
-
- /** null means shared */
- private final AMQShortString _owner;
-
- private final boolean _durable;
-
- /** If true, this queue is deleted when the last subscriber is removed */
- private final boolean _autoDelete;
-
- private final AtomicInteger _subscriberCount = new AtomicInteger();
-
- private final AtomicBoolean _isExclusive = new AtomicBoolean();
-
- private final AtomicBoolean _deleted = new AtomicBoolean(false);
-
- private List<Task> _deleteTaskList = new CopyOnWriteArrayList<Task>();
-
- /** Manages message delivery. */
- private final ConcurrentSelectorDeliveryManager _deliveryMgr;
-
- /** Used to track bindings to exchanges so that on deletion they can easily be cancelled. */
- private final ExchangeBindings _bindings = new ExchangeBindings(this);
-
- /** Executor on which asynchronous delivery will be carriedout where required */
- private final Executor _asyncDelivery;
-
- private final AMQQueueMBean _managedObject;
-
- private final VirtualHost _virtualHost;
-
- /** max allowed size(KB) of a single message */
- @Configured(path = "maximumMessageSize", defaultValue = "0")
- public long _maximumMessageSize;
-
- /** max allowed number of messages on a queue. */
- @Configured(path = "maximumMessageCount", defaultValue = "0")
- public long _maximumMessageCount;
-
- /** max queue depth for the queue */
- @Configured(path = "maximumQueueDepth", defaultValue = "0")
- public long _maximumQueueDepth;
-
- /** maximum message age before alerts occur */
- @Configured(path = "maximumMessageAge", defaultValue = "0")
- public long _maximumMessageAge;
-
- /** the minimum interval between sending out consequetive alerts of the same type */
- @Configured(path = "minimumAlertRepeatGap", defaultValue = "0")
- public long _minimumAlertRepeatGap;
-
- /** total messages received by the queue since startup. */
- public AtomicLong _totalMessagesReceived = new AtomicLong();
-
-
- private final Set<NotificationCheck> _notificationChecks = EnumSet.noneOf(NotificationCheck.class);
- private final AtomicLong _queueEntryId = new AtomicLong(Long.MIN_VALUE);
-
-
- protected AMQQueueImpl(AMQShortString name, boolean durable, AMQShortString owner, boolean autoDelete, VirtualHost virtualHost)
- throws AMQException
- {
-
- if (name == null)
- {
- throw new IllegalArgumentException("Queue name must not be null");
- }
-
- if (virtualHost == null)
- {
- throw new IllegalArgumentException("Virtual Host must not be null");
- }
-
- _name = name;
- _durable = durable;
- _owner = owner;
- _autoDelete = autoDelete;
- _virtualHost = virtualHost;
- _asyncDelivery = AsyncDeliveryConfig.getAsyncDeliveryExecutor();
-
- _managedObject = createMBean();
- _managedObject.register();
-
- _deliveryMgr = new ConcurrentSelectorDeliveryManager(this);
-
- // This ensure that the notification checks for the configured alerts are created.
- setMaximumMessageAge(_maximumMessageAge);
- setMaximumMessageCount(_maximumMessageCount);
- setMaximumMessageSize(_maximumMessageSize);
- setMaximumQueueDepth(_maximumQueueDepth);
-
- }
-
- private AMQQueueMBean createMBean() throws AMQException
- {
- try
- {
- return new AMQQueueMBean(this);
- }
- catch (JMException ex)
- {
- throw new AMQException("AMQQueue MBean creation has failed ", ex);
- }
- }
-
- public final AMQShortString getName()
- {
- return _name;
- }
-
- public boolean isDurable()
- {
- return _durable;
- }
-
- public AMQShortString getOwner()
- {
- return _owner;
- }
-
- public boolean isAutoDelete()
- {
- return _autoDelete;
- }
-
- public boolean isDeleted()
- {
- return _deleted.get();
- }
-
- /** @return no of messages(undelivered) on the queue. */
- public int getMessageCount()
- {
- return _deliveryMgr.getQueueMessageCount();
- }
-
- public int getUndeliveredMessageCount()
- {
- return getMessageCount();
- }
-
- /** @return List of messages(undelivered) on the queue. */
- public List<QueueEntry> getMessagesOnTheQueue()
- {
- return _deliveryMgr.getMessages();
- }
-
- /**
- * Returns messages within the given range of message Ids.
- *
- * @param fromMessageId
- * @param toMessageId
- *
- * @return List of messages
- */
- public List<QueueEntry> getMessagesOnTheQueue(long fromMessageId, long toMessageId)
- {
- return _deliveryMgr.getMessages(fromMessageId, toMessageId);
- }
-
- public long getQueueDepth()
- {
- return _deliveryMgr.getTotalMessageSize();
- }
-
- /**
- * @param messageId
- *
- * @return QueueEntry with give id if exists. null if QueueEntry with given id doesn't exist.
- */
- public QueueEntry getMessageOnTheQueue(long messageId)
- {
- List<QueueEntry> list = getMessagesOnTheQueue(messageId, messageId);
- if ((list == null) || (list.size() == 0))
- {
- return null;
- }
-
- return list.get(0);
- }
-
- /**
- * Moves messages from this queue to another queue, and also commits the move on the message store. Delivery activity
- * on the queues being moved between is suspended during the move.
- *
- * @param fromMessageId The first message id to move.
- * @param toMessageId The last message id to move.
- * @param queueName The queue to move the messages to.
- * @param storeContext The context of the message store under which to perform the move. This is associated with
- * the stores transactional context.
- */
- public synchronized void moveMessagesToAnotherQueue(long fromMessageId, long toMessageId, String queueName,
- StoreContext storeContext)
- {
- AMQQueue toQueue = getVirtualHost().getQueueRegistry().getQueue(new AMQShortString(queueName));
-
- MessageStore fromStore = getVirtualHost().getMessageStore();
- MessageStore toStore = toQueue.getVirtualHost().getMessageStore();
-
- if (toStore != fromStore)
- {
- throw new RuntimeException("Can only move messages between queues on the same message store.");
- }
-
- try
- {
- // Obtain locks to prevent activity on the queues being moved between.
- quiesce();
- toQueue.quiesce();
-
- // Get the list of messages to move.
- List<QueueEntry> foundMessagesList = getMessagesOnTheQueue(fromMessageId, toMessageId);
-
- try
- {
- fromStore.beginTran(storeContext);
-
- // Move the messages in on the message store.
- for (QueueEntry entry : foundMessagesList)
- {
- AMQMessage message = entry.getMessage();
- fromStore.dequeueMessage(storeContext, _name, message.getMessageId());
- toStore.enqueueMessage(storeContext, toQueue.getName(), message.getMessageId());
- }
-
- // Commit and flush the move transcations.
- try
- {
- fromStore.commitTran(storeContext);
- }
- catch (AMQException e)
- {
- throw new RuntimeException("Failed to commit transaction whilst moving messages on message store.", e);
- }
-
- // Move the messages on the in-memory queues.
- toQueue.enqueueMovedMessages(storeContext, foundMessagesList);
- _deliveryMgr.removeMovedMessages(foundMessagesList);
- }
- // Abort the move transactions on move failures.
- catch (AMQException e)
- {
- try
- {
- fromStore.abortTran(storeContext);
- }
- catch (AMQException ae)
- {
- throw new RuntimeException("Failed to abort transaction whilst moving messages on message store.", ae);
- }
- }
- }
- // Release locks to allow activity on the queues being moved between to continue.
- finally
- {
- toQueue.start();
- start();
- }
- }
-
- /**
- * Copies messages on this queue to another queue, and also commits the move on the message store. Delivery activity
- * on the queues being moved between is suspended during the move.
- *
- * @param fromMessageId The first message id to move.
- * @param toMessageId The last message id to move.
- * @param queueName The queue to move the messages to.
- * @param storeContext The context of the message store under which to perform the move. This is associated with
- * the stores transactional context.
- */
- public synchronized void copyMessagesToAnotherQueue(long fromMessageId, long toMessageId, String queueName,
- StoreContext storeContext)
- {
- AMQQueue toQueue = getVirtualHost().getQueueRegistry().getQueue(new AMQShortString(queueName));
-
- MessageStore fromStore = getVirtualHost().getMessageStore();
- MessageStore toStore = toQueue.getVirtualHost().getMessageStore();
-
- if (toStore != fromStore)
- {
- throw new RuntimeException("Can only move messages between queues on the same message store.");
- }
-
- try
- {
- // Obtain locks to prevent activity on the queues being moved between.
- quiesce();
- toQueue.quiesce();
-
- // Get the list of messages to move.
- List<QueueEntry> foundMessagesList = getMessagesOnTheQueue(fromMessageId, toMessageId);
-
- try
- {
- fromStore.beginTran(storeContext);
-
- // Move the messages in on the message store.
- for (QueueEntry entry : foundMessagesList)
- {
- AMQMessage message = entry.getMessage();
- toStore.enqueueMessage(storeContext, toQueue.getName(), message.getMessageId());
- message.takeReference();
- }
-
- // Commit and flush the move transcations.
- try
- {
- fromStore.commitTran(storeContext);
- }
- catch (AMQException e)
- {
- throw new RuntimeException("Failed to commit transaction whilst moving messages on message store.", e);
- }
-
- // Move the messages on the in-memory queues.
- toQueue.enqueueMovedMessages(storeContext, foundMessagesList);
- }
- // Abort the move transactions on move failures.
- catch (AMQException e)
- {
- try
- {
- fromStore.abortTran(storeContext);
- }
- catch (AMQException ae)
- {
- throw new RuntimeException("Failed to abort transaction whilst moving messages on message store.", ae);
- }
- }
- }
- // Release locks to allow activity on the queues being moved between to continue.
- finally
- {
- toQueue.start();
- start();
- }
- }
-
- /**
- * Removes messages from this queue, and also commits the remove on the message store. Delivery activity
- * on the queues being moved between is suspended during the remove.
- *
- * @param fromMessageId The first message id to move.
- * @param toMessageId The last message id to move.
- * @param storeContext The context of the message store under which to perform the move. This is associated with
- * the stores transactional context.
- */
- public synchronized void removeMessagesFromQueue(long fromMessageId, long toMessageId, StoreContext storeContext)
- {
- MessageStore fromStore = getVirtualHost().getMessageStore();
-
- try
- {
- // Obtain locks to prevent activity on the queues being moved between.
- quiesce();
-
- // Get the list of messages to move.
- List<QueueEntry> foundMessagesList = getMessagesOnTheQueue(fromMessageId, toMessageId);
-
- try
- {
- fromStore.beginTran(storeContext);
-
- // remove the messages in on the message store.
- for (QueueEntry entry : foundMessagesList)
- {
- AMQMessage message = entry.getMessage();
- fromStore.dequeueMessage(storeContext, _name, message.getMessageId());
- }
-
- // Commit and flush the move transcations.
- try
- {
- fromStore.commitTran(storeContext);
- }
- catch (AMQException e)
- {
- throw new RuntimeException("Failed to commit transaction whilst moving messages on message store.", e);
- }
-
- // remove the messages on the in-memory queues.
- _deliveryMgr.removeMovedMessages(foundMessagesList);
- }
- // Abort the move transactions on move failures.
- catch (AMQException e)
- {
- try
- {
- fromStore.abortTran(storeContext);
- }
- catch (AMQException ae)
- {
- throw new RuntimeException("Failed to abort transaction whilst moving messages on message store.", ae);
- }
- }
- }
- // Release locks to allow activity on the queues being moved between to continue.
- finally
- {
- start();
- }
- }
-
- public void quiesce()
- {
- _deliveryMgr.startMovingMessages();
- }
-
- public void enqueueMovedMessages(StoreContext storeContext, List<QueueEntry> messageList)
- {
- _deliveryMgr.enqueueMovedMessages(storeContext, messageList);
- _totalMessagesReceived.addAndGet(messageList.size());
- }
-
- public void start()
- {
- _deliveryMgr.stopMovingMessages();
- _deliveryMgr.processAsync(_asyncDelivery);
- }
-
- /** @return MBean object associated with this Queue */
- public ManagedObject getManagedObject()
- {
- return _managedObject;
- }
-
- public long getMaximumMessageSize()
- {
- return _maximumMessageSize;
- }
-
- public void setMaximumMessageSize(final long maximumMessageSize)
- {
- _maximumMessageSize = maximumMessageSize;
- if(maximumMessageSize == 0L)
- {
- _notificationChecks.remove(NotificationCheck.MESSAGE_SIZE_ALERT);
- }
- else
- {
- _notificationChecks.add(NotificationCheck.MESSAGE_SIZE_ALERT);
- }
- }
-
- public int getConsumerCount()
- {
- return getSubscribers().getConsumerCount();
- }
-
- public int getActiveConsumerCount()
- {
- return getSubscribers().getActiveConsumerCount();
- }
-
- public long getReceivedMessageCount()
- {
- return _totalMessagesReceived.get();
- }
-
- public long getMaximumMessageCount()
- {
- return _maximumMessageCount;
- }
-
- public void setMaximumMessageCount(final long maximumMessageCount)
- {
- _maximumMessageCount = maximumMessageCount;
- if(maximumMessageCount == 0L)
- {
- _notificationChecks.remove(NotificationCheck.MESSAGE_COUNT_ALERT);
- }
- else
- {
- _notificationChecks.add(NotificationCheck.MESSAGE_COUNT_ALERT);
- }
-
-
-
- }
-
- public long getMaximumQueueDepth()
- {
- return _maximumQueueDepth;
- }
-
- // Sets the queue depth, the max queue size
- public void setMaximumQueueDepth(final long maximumQueueDepth)
- {
- _maximumQueueDepth = maximumQueueDepth;
- if(maximumQueueDepth == 0L)
- {
- _notificationChecks.remove(NotificationCheck.QUEUE_DEPTH_ALERT);
- }
- else
- {
- _notificationChecks.add(NotificationCheck.QUEUE_DEPTH_ALERT);
- }
-
- }
-
- public long getOldestMessageArrivalTime()
- {
- return _deliveryMgr.getOldestMessageArrival();
-
- }
-
- /** Removes the QueueEntry from the top of the queue. */
- public synchronized void deleteMessageFromTop(StoreContext storeContext) throws AMQException
- {
- _deliveryMgr.removeAMessageFromTop(storeContext, this);
- }
-
- /** removes all the messages from the queue. */
- public synchronized long clearQueue(StoreContext storeContext) throws AMQException
- {
- return _deliveryMgr.clearAllMessages(storeContext);
- }
-
- public void bind(Exchange exchange, AMQShortString routingKey, FieldTable arguments) throws AMQException
- {
- exchange.registerQueue(routingKey, this, arguments);
- if (isDurable() && exchange.isDurable())
- {
- _virtualHost.getMessageStore().bindQueue(exchange, routingKey, this, arguments);
- }
-
- _bindings.addBinding(routingKey, arguments, exchange);
- }
-
- public void unBind(Exchange exchange, AMQShortString routingKey, FieldTable arguments) throws AMQException
- {
- exchange.deregisterQueue(routingKey, this, arguments);
- if (isDurable() && exchange.isDurable())
- {
- _virtualHost.getMessageStore().unbindQueue(exchange, routingKey, this, arguments);
- }
-
- boolean removed = _bindings.remove(routingKey, arguments, exchange);
- if(!removed)
- {
- _logger.error("Mismatch between queue bindings and exchange record of bindings");
- }
- }
-
- public void registerSubscription(final Subscription subscription, final boolean exclusive) throws AMQException
- {
-
- if (incrementSubscriberCount() > 1)
- {
- if (isExclusive())
- {
- decrementSubscriberCount();
- throw new ExistingExclusiveSubscription();
- }
- else if (exclusive)
- {
- decrementSubscriberCount();
- throw new ExistingSubscriptionPreventsExclusive();
- }
-
- }
- else if (exclusive)
- {
- setExclusive(true);
- }
-
- if (_logger.isDebugEnabled())
- {
- _logger.debug(MessageFormat.format("Registering protocol subscription {0} with queue {1}."
- , subscription, this));
- }
-
-
- subscription.setQueue(this);
-
- if (subscription.filtersMessages())
- {
- if (_deliveryMgr.hasQueuedMessages())
- {
- _deliveryMgr.populatePreDeliveryQueue(subscription);
- }
- }
-
- subscription.setStateListener(this);
- getSubscribers().addSubscriber(subscription);
- if(exclusive)
- {
- getSubscribers().setExclusive(true);
- }
- _deliveryMgr.start(subscription);
- deliverAsync();
- }
-
- private boolean isExclusive()
- {
- return _isExclusive.get();
- }
-
- private void setExclusive(boolean exclusive)
- {
- _isExclusive.set(exclusive);
- }
-
- private int incrementSubscriberCount()
- {
- return _subscriberCount.incrementAndGet();
- }
-
- private int decrementSubscriberCount()
- {
- return _subscriberCount.decrementAndGet();
- }
-
- public void unregisterSubscription(final Subscription subscription) throws AMQException
- {
- if (_logger.isDebugEnabled())
- {
- _logger.debug(MessageFormat.format(
- "Unregistering subscription {0} from {1}",
- subscription, this));
- }
-
-
- getSubscribers().setExclusive(false);
- _deliveryMgr.closeSubscription(subscription);
-
- if ((getSubscribers().removeSubscriber(subscription)) == null)
- {
- throw new AMQException("Subscription " + subscription + " not registered with queue " + this);
- }
-
-
- setExclusive(false);
- decrementSubscriberCount();
-
- // if we are eligible for auto deletion, unregister from the queue registry
- if (_autoDelete && getSubscribers().isEmpty())
- {
- if (_logger.isInfoEnabled())
- {
- _logger.info("Auto-deleteing queue:" + this);
- }
-
- autodelete();
- // we need to manually fire the event to the removed subscription (which was the last one left for this
- // queue. This is because the delete method uses the subscription set which has just been cleared
- subscription.queueDeleted(this);
- }
- }
-
- public boolean isUnused()
- {
- return getSubscribers().isEmpty();
- }
-
- public boolean isEmpty()
- {
- return !_deliveryMgr.hasQueuedMessages();
- }
-
- public int delete() throws AMQException
- {
- if (!_deleted.getAndSet(true))
- {
- getSubscribers().queueDeleted(this);
- _bindings.deregister();
- _virtualHost.getQueueRegistry().unregisterQueue(_name);
- _managedObject.unregister();
- for (Task task : _deleteTaskList)
- {
- task.doTask(this);
- }
-
- _deleteTaskList.clear();
- }
- return _deliveryMgr.getQueueMessageCount();
- }
-
- protected void autodelete() throws AMQException
- {
- if (_logger.isDebugEnabled())
- {
- _logger.debug(MessageFormat.format("autodeleting {0}", this));
- }
-
- delete();
- }
-
- /*public void processGet(StoreContext storeContext, AMQMessage msg, boolean deliverFirst) throws AMQException
- {
- // fixme not sure what this is doing. should we be passing deliverFirst through here?
- // This code is not used so when it is perhaps it should
- _deliveryMgr.deliver(storeContext, getName(), msg, deliverFirst);
- try
- {
- msg.immediateAndNotDelivered();
- updateReceivedMessageCount(msg);
- }
- catch (NoConsumersException e)
- {
- // as this message will be returned, it should be removed
- // from the queue:
- dequeue(storeContext, msg);
- }
- }*/
-
- public void requeue(StoreContext storeContext, QueueEntry entry) throws AMQException
- {
- process(storeContext,entry,true);
- }
-
-
- public QueueEntry enqueue(StoreContext storeContext, AMQMessage message) throws AMQException
- {
- QueueEntryImpl entry = createEntry(message);
- process(storeContext, entry, false);
- return entry;
- }
-
- // public DeliveryManager getDeliveryManager()
- // {
- // return _deliveryMgr;
- // }
-
- public void process(StoreContext storeContext, QueueEntry entry, boolean deliverFirst) throws AMQException
- {
- AMQMessage msg = entry.getMessage();
- _deliveryMgr.deliver(storeContext, _name, entry, deliverFirst);
- if(msg.immediateAndNotDelivered())
- {
- dequeue(storeContext, entry);
- }
- else
- {
- updateReceivedMessageCount(entry);
- }
- }
-
- public void dequeue(StoreContext storeContext, QueueEntry entry) throws FailedDequeueException
- {
- try
- {
- AMQMessage msg = entry.getMessage();
- if(isDurable() && msg.isPersistent())
- {
- _virtualHost.getMessageStore().dequeueMessage(storeContext, getName(), msg.getMessageId());
- }
-
- }
- catch (MessageCleanupException e)
- {
- // Message was dequeued, but could not then be deleted
- // though it is no longer referenced. This should be very
- // rare and can be detected and cleaned up on recovery or
- // done through some form of manual intervention.
- _logger.error(e, e);
- }
- catch (AMQException e)
- {
- throw new FailedDequeueException(_name.toString(), e);
- }
- }
-
- public void deliverAsync()
- {
- _deliveryMgr.processAsync(_asyncDelivery);
- }
-
- public SubscriptionManager getSubscribers()
- {
- return _deliveryMgr.getSubscribers();
- }
-
- protected void updateReceivedMessageCount(QueueEntry entry) throws AMQException
- {
- AMQMessage msg = entry.getMessage();
-
- if (!msg.isRedelivered())
- {
- _totalMessagesReceived.incrementAndGet();
- }
-
- try
- {
- _managedObject.checkForNotification(msg);
- }
- catch (JMException e)
- {
- throw new AMQException("Unable to get notification from manage queue: " + e, e);
- }
- }
-
- public boolean equals(Object o)
- {
- if (this == o)
- {
- return true;
- }
-
- if ((o == null) || (getClass() != o.getClass()))
- {
- return false;
- }
-
- final AMQQueue amqQueue = (AMQQueue) o;
-
- return (_name.equals(amqQueue.getName()));
- }
-
- public int hashCode()
- {
- return _name.hashCode();
- }
-
- public String toString()
- {
- return _name.toString();
- }
-
- public boolean performGet(AMQProtocolSession session, AMQChannel channel, boolean acks) throws AMQException
- {
- return _deliveryMgr.performGet(session, channel, acks);
- }
-
- public QueueRegistry getQueueRegistry()
- {
- return _virtualHost.getQueueRegistry();
- }
-
- public VirtualHost getVirtualHost()
- {
- return _virtualHost;
- }
-
- public void addQueueDeleteTask(Task task)
- {
- _deleteTaskList.add(task);
- }
-
- public boolean resend(final QueueEntry entry, final Subscription sub)
- {
-
-
- // Get the lock so we can tell if the sub scription has closed.
- // will stop delivery to this subscription until the lock is released.
- // note: this approach would allow the use of a single queue if the
- // PreDeliveryQueue would allow head additions.
- // In the Java Qpid client we are suspended whilst doing this so it is all rather Mute..
- // needs guidance from AMQP WG Model SIG
- synchronized (sub.getSendLock())
- {
- if (sub.isClosed())
- {
- if (_logger.isDebugEnabled())
- {
- _logger.debug("Subscription(" + System.identityHashCode(sub)
- + ") closed during resend so requeuing message");
- }
- // move this message to requeue
- return false;
-
- }
- else
- {
- if (_logger.isDebugEnabled())
- {
- _logger.debug("Requeuing " + entry.debugIdentity() + " for resend via sub:"
- + System.identityHashCode(sub));
- }
-
- entry.release();
- _deliveryMgr.resend(entry, sub);
- return true;
- }
- } // sync(sub.getSendLock)
-
-
-
-
- }
-
- public long getMinimumAlertRepeatGap()
- {
- return _minimumAlertRepeatGap;
- }
-
- public void setMinimumAlertRepeatGap(long minimumAlertRepeatGap)
- {
- _minimumAlertRepeatGap = minimumAlertRepeatGap;
- }
-
- public long getMaximumMessageAge()
- {
- return _maximumMessageAge;
- }
-
- public void setMaximumMessageAge(long maximumMessageAge)
- {
- _maximumMessageAge = maximumMessageAge;
- if(maximumMessageAge == 0L)
- {
- _notificationChecks.remove(NotificationCheck.MESSAGE_AGE_ALERT);
- }
- else
- {
- _notificationChecks.add(NotificationCheck.MESSAGE_AGE_ALERT);
- }
- }
-
- public void subscriberHasPendingResend(boolean hasContent, Subscription subscription, QueueEntry entry)
- {
- _deliveryMgr.subscriberHasPendingResend(hasContent, subscription, entry);
- }
-
- public QueueEntryImpl createEntry(AMQMessage amqMessage)
- {
- return new QueueEntryImpl(this, amqMessage, _queueEntryId.getAndIncrement());
- }
-
- public int compareTo(Object o)
- {
- return _name.compareTo(((AMQQueue) o).getName());
- }
-
-
- public void removeExpiredIfNoSubscribers() throws AMQException
- {
- synchronized(getSubscribers().getChangeLock())
- {
- if(getSubscribers().isEmpty())
- {
- _deliveryMgr.removeExpired();
- }
- }
- }
-
- public final Set<NotificationCheck> getNotificationChecks()
- {
- return _notificationChecks;
- }
-
- public void stateChange(Subscription sub, Subscription.State oldState, Subscription.State newState)
- {
- if(newState == Subscription.State.ACTIVE)
- {
- deliverAsync();
- }
- }
-}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/ConcurrentSelectorDeliveryManager.java b/java/broker/src/main/java/org/apache/qpid/server/queue/ConcurrentSelectorDeliveryManager.java
deleted file mode 100644
index becd57752e..0000000000
--- a/java/broker/src/main/java/org/apache/qpid/server/queue/ConcurrentSelectorDeliveryManager.java
+++ /dev/null
@@ -1,1098 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.queue;
-
-import org.apache.log4j.Logger;
-import org.apache.qpid.AMQException;
-import org.apache.qpid.configuration.Configured;
-import org.apache.qpid.framing.AMQShortString;
-import org.apache.qpid.framing.abstraction.ContentChunk;
-import org.apache.qpid.server.AMQChannel;
-import org.apache.qpid.server.subscription.Subscription;
-import org.apache.qpid.server.configuration.Configurator;
-import org.apache.qpid.server.protocol.AMQProtocolSession;
-import org.apache.qpid.server.store.StoreContext;
-import org.apache.qpid.util.ConcurrentLinkedMessageQueueAtomicSize;
-import org.apache.qpid.util.MessageQueue;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Queue;
-import java.util.Set;
-import java.util.concurrent.Executor;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicLong;
-import java.util.concurrent.locks.ReentrantLock;
-
-
-/** Manages delivery of messages on behalf of a queue */
-public class ConcurrentSelectorDeliveryManager
-{
- private static final Logger _log = Logger.getLogger(ConcurrentSelectorDeliveryManager.class);
-
- @Configured(path = "advanced.compressBufferOnQueue",
- defaultValue = "false")
- public boolean compressBufferOnQueue;
- /** Holds any queued messages */
- private final MessageQueue<QueueEntry> _messages = new ConcurrentLinkedMessageQueueAtomicSize<QueueEntry>();
-
- /** Ensures that only one asynchronous task is running for this manager at any time. */
- private final AtomicBoolean _processing = new AtomicBoolean();
- /** The subscriptions on the queue to whom messages are delivered */
- private final SubscriptionSet _subscriptions = new SubscriptionSet();
-
- /**
- * A reference to the queue we are delivering messages for. We need this to be able to pass the code that handles
- * acknowledgements a handle on the queue.
- */
- private final AMQQueueImpl _queue;
-
- /**
- * Flag used while moving messages from this queue to another. For moving messages the async delivery should also
- * stop. This flat should be set to true to stop async delivery and set to false to enable async delivery again.
- */
- private AtomicBoolean _movingMessages = new AtomicBoolean();
-
- /**
- * Lock used to ensure that an channel that becomes unsuspended during the start of the queueing process is forced
- * to wait till the first message is added to the queue. This will ensure that the _queue has messages to be
- * delivered via the async thread. <p/> Lock is used to control access to hasQueuedMessages() and over the addition
- * of messages to the queue.
- */
- private ReentrantLock _lock = new ReentrantLock();
- private AtomicLong _totalMessageSize = new AtomicLong();
- private AtomicInteger _extraMessages = new AtomicInteger();
- private Set<DeliveryAgent> _hasContent = Collections.synchronizedSet(new HashSet<DeliveryAgent>());
- private final Object _queueHeadLock = new Object();
- private String _processingThreadName = "";
-
-
- /** Used by any reaping thread to purge messages */
- private StoreContext _reapingStoreContext = new StoreContext();
-
- ConcurrentSelectorDeliveryManager(AMQQueueImpl queue)
- {
-
- //Set values from configuration
- Configurator.configure(this);
-
- if (compressBufferOnQueue)
- {
- _log.warn("Compressing Buffers on queue.");
- }
-
- _queue = queue;
- }
-
-
- public SubscriptionSet getSubscribers()
- {
- return _subscriptions;
- }
-
-
- private boolean addMessageToQueue(QueueEntry entry, boolean deliverFirst)
- {
- AMQMessage msg = entry.getMessage();
- // Shrink the ContentBodies to their actual size to save memory.
- if (compressBufferOnQueue)
- {
- Iterator<ContentChunk> it = msg.getContentBodyIterator();
- while (it.hasNext())
- {
- ContentChunk cb = it.next();
- cb.reduceToFit();
- }
- }
-
- if (deliverFirst)
- {
- synchronized (_queueHeadLock)
- {
- _messages.pushHead(entry);
- }
- }
- else
- {
- _messages.offer(entry);
- }
-
- _totalMessageSize.addAndGet(msg.getSize());
-
- return true;
- }
-
-
- public boolean hasQueuedMessages()
- {
- _lock.lock();
- try
- {
- return !(_messages.isEmpty() && _hasContent.isEmpty());
- }
- finally
- {
- _lock.unlock();
- }
- }
-
- public int getQueueMessageCount()
- {
- return getMessageCount();
- }
-
- /**
- * This is an EXPENSIVE opperation to perform with a ConcurrentLinkedQueue as it must run the queue to determine
- * size. The ConcurrentLinkedQueueAtomicSize uses an AtomicInteger to record the number of elements on the queue.
- *
- * @return int the number of messages in the delivery queue.
- */
- private int getMessageCount()
- {
- return _messages.size() + _extraMessages.get();
- }
-
-
- public long getTotalMessageSize()
- {
- return _totalMessageSize.get();
- }
-
- public long getOldestMessageArrival()
- {
- QueueEntry entry = _messages.peek();
- return entry == null ? Long.MAX_VALUE : entry.getMessage().getArrivalTime();
- }
-
- public void subscriberHasPendingResend(boolean hasContent, Subscription subscription, QueueEntry entry)
- {
- subscriberHasPendingResend(hasContent,_subscriptions.getDeliveryAgent(subscription),entry);
- }
- private void subscriberHasPendingResend(boolean hasContent, DeliveryAgent deliveryAgent, QueueEntry entry)
- {
- _lock.lock();
- try
- {
- if (hasContent)
- {
- _log.debug("Queue has adding subscriber content");
- _hasContent.add(deliveryAgent);
- _totalMessageSize.addAndGet(entry.getSize());
- _extraMessages.addAndGet(1);
- }
- else
- {
- _log.debug("Queue has removing subscriber content");
- if (entry == null)
- {
- _hasContent.remove(deliveryAgent);
- }
- else
- {
- _totalMessageSize.addAndGet(-entry.getSize());
- _extraMessages.addAndGet(-1);
- }
- }
- }
- finally
- {
- _lock.unlock();
- }
- }
-
- /**
- * NOTE : This method should only be called when there are no active subscribers
- */
- public void removeExpired() throws AMQException
- {
- _lock.lock();
-
-
- for(Iterator<QueueEntry> iter = _messages.iterator(); iter.hasNext();)
- {
- QueueEntry entry = iter.next();
- if(entry.expired())
- {
- // fixme: Currently we have to update the total byte size here for the data in the queue
- _totalMessageSize.addAndGet(-entry.getSize());
- _queue.dequeue(_reapingStoreContext,entry);
- iter.remove();
- }
- }
-
-
- _lock.unlock();
- }
-
- /** @return the state of the async processor. */
- public boolean isProcessingAsync()
- {
- return _processing.get();
- }
-
- /**
- * Returns all the messages in the Queue
- *
- * @return List of messages
- */
- public List<QueueEntry> getMessages()
- {
- _lock.lock();
- List<QueueEntry> list = new ArrayList<QueueEntry>();
-
- for (QueueEntry entry : _messages)
- {
- list.add(entry);
- }
- _lock.unlock();
-
- return list;
- }
-
- /**
- * Returns messages within the range of given messageIds
- *
- * @param fromMessageId
- * @param toMessageId
- *
- * @return
- */
- public List<QueueEntry> getMessages(long fromMessageId, long toMessageId)
- {
- if (fromMessageId <= 0 || toMessageId <= 0)
- {
- return null;
- }
-
- long maxMessageCount = toMessageId - fromMessageId + 1;
-
- _lock.lock();
-
- List<QueueEntry> foundMessagesList = new ArrayList<QueueEntry>();
-
- for (QueueEntry entry : _messages)
- {
- long msgId = entry.getMessage().getMessageId();
- if (msgId >= fromMessageId && msgId <= toMessageId)
- {
- foundMessagesList.add(entry);
- }
- // break if the no of messages are found
- if (foundMessagesList.size() == maxMessageCount)
- {
- break;
- }
- }
- _lock.unlock();
-
- return foundMessagesList;
- }
-
- public void populatePreDeliveryQueue(Subscription subscription)
- {
- populatePreDeliveryQueue(_subscriptions.getDeliveryAgent(subscription));
- }
-
- private void populatePreDeliveryQueue(DeliveryAgent deliveryAgent)
- {
-
- Iterator<QueueEntry> currentQueue = _messages.iterator();
-
- while (currentQueue.hasNext())
- {
- QueueEntry entry = currentQueue.next();
-
- if (deliveryAgent != null && deliveryAgent.hasInterest(entry))
- {
- deliveryAgent.enqueueForPreDelivery(entry, false);
- }
-
- }
- }
-
- public boolean performGet(AMQProtocolSession protocolSession, AMQChannel channel, boolean acks) throws AMQException
- {
- QueueEntry entry = getNextMessage();
- if (entry == null)
- {
- return false;
- }
- else
- {
-
- try
- {
- // if we do not need to wait for client acknowledgements
- // we can decrement the reference count immediately.
-
- // By doing this _before_ the send we ensure that it
- // doesn't get sent if it can't be dequeued, preventing
- // duplicate delivery on recovery.
-
- // The send may of course still fail, in which case, as
- // the message is unacked, it will be lost.
- if (!acks)
- {
- if (_log.isDebugEnabled())
- {
- _log.debug("No ack mode so dequeuing message immediately: " + entry.getMessage().getMessageId());
- }
- entry.dequeue(channel.getStoreContext());
- }
- synchronized (channel)
- {
- long deliveryTag = channel.getNextDeliveryTag();
-
- if (acks)
- {
- channel.addUnacknowledgedMessage(entry, deliveryTag, null);
- }
-
- protocolSession.getProtocolOutputConverter().writeGetOk(entry.getMessage(), channel.getChannelId(),
- deliveryTag, _queue.getMessageCount());
-
- }
- _totalMessageSize.addAndGet(-entry.getSize());
-
- if (!acks)
- {
- entry.dispose(channel.getStoreContext());
- }
- }
- finally
- {
- entry.setDeliveredToSubscription();
- }
- return true;
-
- }
- }
-
- /**
- * For feature of moving messages, this method is used. It sets the lock and sets the movingMessages flag, so that
- * the asyn delivery is also stopped.
- */
- public void startMovingMessages()
- {
- _movingMessages.set(true);
- }
-
- /**
- * Once moving messages to another queue is done or aborted, remove lock and unset the movingMessages flag, so that
- * the async delivery can start again.
- */
- public void stopMovingMessages()
- {
- _movingMessages.set(false);
- if (_lock.isHeldByCurrentThread())
- {
- _lock.unlock();
- }
- }
-
- /**
- * Messages will be removed from this queue and all preDeliveryQueues
- *
- * @param messageList
- */
- public void removeMovedMessages(List<QueueEntry> messageList)
- {
- // Remove from the
- boolean hasSubscribers = _subscriptions.hasActiveSubscribers();
- if (hasSubscribers)
- {
- for (Subscription sub : _subscriptions.getSubscriptions())
- {
- DeliveryAgent deliveryAgent = _subscriptions.getDeliveryAgent(sub);
- if (deliveryAgent != null && !sub.isSuspended() && sub.filtersMessages())
- {
- Queue<QueueEntry> preDeliveryQueue = deliveryAgent.getPreDeliveryQueue();
- for (QueueEntry entry : messageList)
- {
- preDeliveryQueue.remove(entry);
- }
- }
- }
- }
-
- for (QueueEntry entry : messageList)
- {
- if (_messages.remove(entry))
- {
- _totalMessageSize.getAndAdd(-entry.getSize());
- }
- }
- }
-
- /**
- * Now with implementation of predelivery queues, this method will mark the message on the top as taken.
- *
- * @param storeContext
- *
- * @throws AMQException
- */
- public void removeAMessageFromTop(StoreContext storeContext, AMQQueue queue) throws AMQException
- {
- _lock.lock();
-
- QueueEntry entry = _messages.poll();
-
- if (entry != null)
- {
- entry.dequeue(storeContext);
-
- _totalMessageSize.addAndGet(-entry.getSize());
-
- //If this causes ref count to hit zero then data will be purged so message.getSize() will NPE.
- entry.dispose(storeContext);
-
- }
-
- _lock.unlock();
- }
-
- public long clearAllMessages(StoreContext storeContext) throws AMQException
- {
- long count = 0;
- _lock.lock();
-
- synchronized (_queueHeadLock)
- {
- QueueEntry entry = getNextMessage();
- while (entry != null)
- {
- //and remove it
- _messages.poll();
-
- entry.dequeue(storeContext);
-
- entry.dispose(storeContext);
-
- entry = getNextMessage();
- count++;
- }
- _totalMessageSize.set(0L);
- }
- _lock.unlock();
- return count;
- }
-
- /**
- * This can only be used to clear the _messages queue. Any subscriber resend queue will not be purged.
- *
- * @return the next message or null
- *
- * @throws org.apache.qpid.AMQException
- */
- private QueueEntry getNextMessage() throws AMQException
- {
- return getNextMessage(_messages, null, false);
- }
-
- private QueueEntry getNextMessage(Queue<QueueEntry> messages, Subscription sub, boolean purgeOnly) throws AMQException
- {
- QueueEntry entry = messages.peek();
-
- //while (we have a message) && ((The subscriber is not a browser or message is taken ) or we are clearing) && (Check message is taken.)
- while (purgeMessage(entry, sub, purgeOnly))
- {
-
- //remove the already taken message or expired
- QueueEntry removed = messages.poll();
-
- assert removed == entry;
-
- // if the message expired then the _totalMessageSize needs adjusting
- if (entry.expired() && !entry.acquire(sub))
- {
- _totalMessageSize.addAndGet(-entry.getSize());
-
- // Use the reapingStoreContext as any sub(if we have one) may be in a tx.
- entry.dequeue(_reapingStoreContext);
- entry.dispose(_reapingStoreContext);
-
- if (_log.isInfoEnabled())
- {
- _log.info(debugIdentity() + " Doing clean up of the main _message queue.");
- }
- }
-
- //else the clean up is not required as the message has already been taken for this queue therefore
- // it was the responsibility of the code that took the message to ensure the _totalMessageSize was updated.
-
- if (_log.isDebugEnabled())
- {
- _log.debug("Removed taken message:" + entry.debugIdentity());
- }
-
- // try the next message
- entry = messages.peek();
- }
-
- return entry;
- }
-
- /**
- * This method will return true if the message is to be purged from the queue.
- * \
- * SIDE-EFFECT: The msg will be taken by the Subscription(sub) for the current Queue(_queue) when purgeOnly is false
- *
- * @param message
- * @param sub
- * @param purgeOnly When set to false the message will be taken by the given Subscription.
- *
- * @return if the msg should be purged
- *
- * @throws AMQException
- */
- private boolean purgeMessage(QueueEntry message, Subscription sub, boolean purgeOnly) throws AMQException
- {
- //Original.. complicated while loop control
-// (message != null
-// && (
-// ((sub != null && !sub.isBrowser()) || message.isTaken(_queue))
-// || sub == null)
-// && message.taken(_queue, sub));
-
- boolean purge = false;
-
- // if the message is null then don't purge as we have no messagse.
- if (message != null)
- {
- // Check that the message hasn't expired.
- if (message.expired())
- {
- return true;
- }
-
- // if we have a subscriber perform message checks
- if (sub != null)
- {
- // if we have a queue browser(we don't purge) so check mark the message as taken
- purge = ((!sub.isBrowser() || message.isAcquired()));
- }
- else
- {
- // if there is no subscription we are doing
- // a get or purging so mark message as taken.
- message.isAcquired();
- // and then ensure that it gets purged
- purge = true;
- }
- }
-
- if (purgeOnly)
- {
- // If we are simply purging the queue don't take the message
- // just purge up to the next non-taken msg.
- return purge && message.isAcquired();
- }
- else
- {
- // if we are purging then ensure we mark this message taken for the current subscriber
- // the current subscriber may be null in the case of a get or a purge but this is ok.
- return purge && message.acquire(sub);
- }
- }
-
- public void sendNextMessage(DeliveryAgent sub)
- {
-
- Queue<QueueEntry> messageQueue = sub.getNextQueue(_messages);
-
- if (_log.isDebugEnabled())
- {
- _log.debug(debugIdentity() + "Async sendNextMessage for sub (" + System.identityHashCode(sub) +
- ") from queue (" + System.identityHashCode(messageQueue) +
- ") AMQQueue (" + System.identityHashCode(_queue) + ")");
- }
-
- if (messageQueue == null)
- {
- // There is no queue with messages currently. This is ok... just means the queue has no msgs matching selector
- if (_log.isInfoEnabled())
- {
- _log.info(debugIdentity() + sub + ": asked to send messages but has none on given queue:" + _queue);
- }
- return;
- }
-
- QueueEntry entry = null;
- QueueEntry removed;
- try
- {
- synchronized (_queueHeadLock)
- {
- entry = getNextMessage(messageQueue, sub.getSubscription(), false);
-
- // message will be null if we have no messages in the messageQueue.
- if (entry == null)
- {
- if (_log.isDebugEnabled())
- {
- _log.debug(debugIdentity() + "No messages for Subscriber(" + System.identityHashCode(sub) + ") from queue; (" + System.identityHashCode(messageQueue) + ")");
- }
- return;
- }
- if (_log.isDebugEnabled())
- {
- _log.debug(debugIdentity() + "Async Delivery Message :" + entry + "(" + System.identityHashCode(entry) +
- ") by :" + System.identityHashCode(this) +
- ") to :" + System.identityHashCode(sub));
- }
-
-
- if (messageQueue == _messages)
- {
- _totalMessageSize.addAndGet(-entry.getSize());
- }
-
- sub.send(entry);
-
- //remove sent message from our queue.
- removed = messageQueue.poll();
- //If we don't remove the message from _messages
- // Otherwise the Async send will never end
- }
-
- if (removed != entry)
- {
- _log.error("Just send message:" + entry.getMessage().debugIdentity() + " BUT removed this from queue:" + removed);
- }
-
- if (_log.isDebugEnabled())
- {
- _log.debug(debugIdentity() + "Async Delivered Message r:" + removed.getMessage().debugIdentity() + "d:" + entry +
- ") by :" + System.identityHashCode(this) +
- ") to :" + System.identityHashCode(sub));
- }
-
-
- if (messageQueue == sub.getResendQueue())
- {
- if (_log.isDebugEnabled())
- {
- _log.debug(debugIdentity() + "All messages sent from resendQueue for " + sub);
- }
- if (messageQueue.isEmpty())
- {
- subscriberHasPendingResend(false, sub, null);
- //better to use the above method as this keeps all the tracking in one location.
- // _hasContent.remove(sub);
- }
-
- _extraMessages.decrementAndGet();
- }
- else if (messageQueue == sub.getPreDeliveryQueue() && !sub.isBrowser())
- {
- if (_log.isInfoEnabled())
- {
- //fixme - we should do the clean up as the message remains on the _message queue
- // this is resulting in the next consumer receiving the message and then attempting to purge it
- //
- cleanMainQueue(sub);
- }
- }
-
- }
- catch (AMQException e)
- {
- if (entry != null)
- {
- entry.release();
- }
- else
- {
- _log.error(debugIdentity() + "Unable to release message as it is null. " + e, e);
- }
- _log.error(debugIdentity() + "Unable to deliver message as dequeue failed: " + e, e);
- }
- }
-
- private void cleanMainQueue(DeliveryAgent sub)
- {
- try
- {
- getNextMessage(_messages, sub.getSubscription(), true);
- }
- catch (AMQException e)
- {
- _log.warn("Problem during main queue purge:" + e.getMessage());
- }
- }
-
- /**
- * enqueues the messages in the list on the queue and all required predelivery queues
- *
- * @param storeContext
- * @param movedMessageList
- */
- public void enqueueMovedMessages(StoreContext storeContext, List<QueueEntry> movedMessageList)
- {
- _lock.lock();
- for (QueueEntry entry : movedMessageList)
- {
- addMessageToQueue(entry, false);
- }
-
- // enqueue on the pre delivery queues
- for (Subscription sub : _subscriptions.getSubscriptions())
- {
- for (QueueEntry entry : movedMessageList)
- {
- // Only give the message to those that want them.
- if (sub.hasInterest(entry))
- {
- DeliveryAgent deliveryAgent = _subscriptions.getDeliveryAgent(sub);
- if(deliveryAgent != null)
- {
- deliveryAgent.enqueueForPreDelivery(entry, true);
- }
- }
- }
- }
- _lock.unlock();
- }
-
- /**
- * Only one thread should ever execute this method concurrently, but it can do so while other threads invoke
- * deliver().
- */
- private void processQueue()
- {
- //record thread name
- if (_log.isDebugEnabled())
- {
- _processingThreadName = Thread.currentThread().getName();
- }
-
- if (_log.isDebugEnabled())
- {
- _log.debug(debugIdentity() + "Running process Queue." + currentStatus());
- }
-
- // Continue to process delivery while we haveSubscribers and messages
- boolean hasSubscribers = _subscriptions.hasActiveSubscribers();
-
- while (hasSubscribers && hasQueuedMessages() && !_movingMessages.get())
- {
- hasSubscribers = false;
-
- for (Subscription sub : _subscriptions.getSubscriptions())
- {
- DeliveryAgent da = _subscriptions.getDeliveryAgent(sub);
- if(da != null)
- {
- synchronized (da.getSendLock())
- {
- if (!sub.isSuspended())
- {
- sendNextMessage(da);
-
- hasSubscribers = true;
- }
- }
- }
- }
- }
-
- if (_log.isDebugEnabled())
- {
- _log.debug(debugIdentity() + "Done process Queue." + currentStatus());
- }
-
- }
-
- public void deliver(StoreContext context, AMQShortString name, QueueEntry entry, boolean deliverFirst) throws AMQException
- {
-
- final boolean debugEnabled = _log.isDebugEnabled();
- if (debugEnabled)
- {
- _log.debug(debugIdentity() + "deliver :first(" + deliverFirst + ") :" + entry);
- }
-
- //Check if we have someone to deliver the message to.
- _lock.lock();
- try
- {
- Subscription s = _subscriptions.nextSubscriber(entry);
- DeliveryAgent da = (s==null) ? null : _subscriptions.getDeliveryAgent(s);
-
-
- if (s == null || (!s.filtersMessages() && hasQueuedMessages())) //no-one can take the message right now or we're queueing
- {
- if (debugEnabled)
- {
- _log.debug(debugIdentity() + "Testing Message(" + entry + ") for Queued Delivery:" + currentStatus());
- }
- if (!entry.getMessage().getMessagePublishInfo().isImmediate())
- {
- addMessageToQueue(entry, deliverFirst);
-
- //release lock now message is on queue.
- _lock.unlock();
-
- //Pre Deliver to all subscriptions
- if (debugEnabled)
- {
- _log.debug(debugIdentity() + "We have " + _subscriptions.getSubscriptions().size() +
- " subscribers to give the message to:" + currentStatus());
- }
- for (Subscription sub : _subscriptions.getSubscriptions())
- {
- DeliveryAgent deliveryAgent = (s==null) ? null : _subscriptions.getDeliveryAgent(sub);
- // Only give the message to those that want them.
- if (deliveryAgent != null && sub.hasInterest(entry))
- {
- if (debugEnabled)
- {
- _log.debug(debugIdentity() + "Queuing message(" + System.identityHashCode(entry) +
- ") for PreDelivery for subscriber(" + System.identityHashCode(sub) + ")");
- }
- deliveryAgent.enqueueForPreDelivery(entry, deliverFirst);
- }
- }
-
- //if we have a non-filtering subscriber but queued messages && we're not Async && we have other Active subs then something is wrong!
- if ((s != null && hasQueuedMessages()) && !isProcessingAsync() && _subscriptions.hasActiveSubscribers())
- {
- _queue.deliverAsync();
- }
-
- }
- }
- else
- {
-
- if (s.filtersMessages())
- {
- if (da.getPreDeliveryQueue().size() > 0)
- {
- _log.error("Direct delivery from PDQ with queued msgs:" + da.getPreDeliveryQueue().size());
- }
- }
- else if (_messages.size() > 0)
- {
- _log.error("Direct delivery from MainQueue queued msgs:" + _messages.size());
- }
-
- //release lock now
- _lock.unlock();
- Object sendLock = (da == null) ? new Object() : da.getSendLock();
- synchronized (sendLock)
- {
- if (!s.isSuspended())
- {
- if (debugEnabled)
- {
- _log.debug(debugIdentity() + "Delivering Message:" + entry.getMessage().debugIdentity() + " to(" +
- System.identityHashCode(s) + ") :" + s);
- }
-
- if (entry.acquire(s))
- {
- //Message has been delivered so don't redeliver.
- // This can currently occur because of the recursive call below
- // During unit tests the send can occur
- // client then rejects
- // this reject then releases the message by the time the
- // if(!msg.isTaken()) call is made below
- // the message has been released so that thread loops to send the message again
- // of course by the time it gets back to here. the thread that released the
- // message is now ready to send it. Here is a sample trace for reference
-//1192627162613:Thread[pool-917-thread-4,5,main]:CSDM:delivery:(true)message:Message[(HC:5529738 ID:145 Ref:1)]: 145; ref count: 1; taken for queues: {Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=false} by Subs:{Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=null}:sub:[channel=Channel: id 1, transaction mode: true, prefetch marks: 2500/5000, consumerTag=41, session=anonymous(5050419), resendQueue=false]
-//1192627162613:Thread[pool-917-thread-4,5,main]:Msg:taken:Q:Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326:sub:[channel=Channel: id 1, transaction mode: true, prefetch marks: 2500/5000, consumerTag=41, session=anonymous(5050419), resendQueue=false]:this:Message[(HC:5529738 ID:145 Ref:1)]: 145; ref count: 1; taken for queues: {Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=false} by Subs:{Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=null}
-//1192627162613:Thread[pool-917-thread-4,5,main]:28398657 Sent :dt:214 msg:(HC:5529738 ID:145 Ref:1)
-//1192627162613:Thread[pool-917-thread-2,5,main]:Reject message by:[channel=Channel: id 1, transaction mode: true, prefetch marks: 2500/5000, consumerTag=41, session=anonymous(5050419), resendQueue=false]
-//1192627162613:Thread[pool-917-thread-2,5,main]:Releasing Message:(HC:5529738 ID:145 Ref:1)
-//1192627162613:Thread[pool-917-thread-2,5,main]:Msg:Release:Q:Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326:This:Message[(HC:5529738 ID:145 Ref:1)]: 145; ref count: 1; taken for queues: {Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=false} by Subs:{Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=[channel=Channel: id 1, transaction mode: true, prefetch marks: 2500/5000, consumerTag=41, session=anonymous(5050419), resendQueue=false]}
-//1192627162613:Thread[pool-917-thread-2,5,main]:CSDM:delivery:(true)message:Message[(HC:5529738 ID:145 Ref:1)]: 145; ref count: 1; taken for queues: {Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=false} by Subs:{Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=null}:sub:[channel=Channel: id 1, transaction mode: true, prefetch marks: 2500/5000, consumerTag=33, session=anonymous(26960027), resendQueue=false]
-//1192627162629:Thread[pool-917-thread-4,5,main]:CSDM:suspended: Message((HC:5529738 ID:145 Ref:1)) has not been taken so recursing!: Subscriber:28398657
-//1192627162629:Thread[pool-917-thread-4,5,main]:CSDM:delivery:(true)message:Message[(HC:5529738 ID:145 Ref:1)]: 145; ref count: 1; taken for queues: {Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=false} by Subs:{Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=null}:sub:[channel=Channel: id 1, transaction mode: true, prefetch marks: 2500/5000, consumerTag=33, session=anonymous(26960027), resendQueue=false]
-//1192627162629:Thread[pool-917-thread-2,5,main]:Msg:taken:Q:Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326:sub:[channel=Channel: id 1, transaction mode: true, prefetch marks: 2500/5000, consumerTag=33, session=anonymous(26960027), resendQueue=false]:this:Message[(HC:5529738 ID:145 Ref:1)]: 145; ref count: 1; taken for queues: {Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=false} by Subs:{Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=null}
-//1192627162629:Thread[pool-917-thread-2,5,main]:25386607 Sent :dt:172 msg:(HC:5529738 ID:145 Ref:1)
-//1192627162629:Thread[pool-917-thread-4,5,main]:Msg:taken:Q:Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326:sub:[channel=Channel: id 1, transaction mode: true, prefetch marks: 2500/5000, consumerTag=33, session=anonymous(26960027), resendQueue=false]:this:Message[(HC:5529738 ID:145 Ref:1)]: 145; ref count: 1; taken for queues: {Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=true} by Subs:{Queue(queue-596fb10e-2968-4e51-a751-1e6643bf9dd6)@16017326=[channel=Channel: id 1, transaction mode: true, prefetch marks: 2500/5000, consumerTag=33, session=anonymous(26960027), resendQueue=false]}
- // Note: In the last request to take the message from thread 4,5 the message has been
- // taken by the previous call done by thread 2,5
-
-
- return;
- }
- //Deliver the message
- da.send(entry);
- }
- else
- {
- if (debugEnabled)
- {
- _log.debug(debugIdentity() + " Subscription(" + System.identityHashCode(s) + ") became " +
- "suspended between nextSubscriber and send for message:" + entry.getMessage().debugIdentity());
- }
- }
- }
-
- //
- // Why do we do this? What was the reasoning? We should have a better approach
- // than recursion and rejecting if someone else sends it before we do.
- //
- if (!entry.isAcquired())
- {
- if (debugEnabled)
- {
- _log.debug(debugIdentity() + " Message(" + entry.getMessage().debugIdentity() + ") has not been taken so recursing!:" +
- " Subscriber:" + System.identityHashCode(s));
- }
-
- deliver(context, name, entry, deliverFirst);
- }
- else
- {
- if (debugEnabled)
- {
- _log.debug(debugIdentity() + " Message(" + entry.toString() +
- ") has been taken so disregarding deliver request to Subscriber:" +
- System.identityHashCode(s));
- }
- }
- }
-
- }
- finally
- {
- //ensure lock is released
- if (_lock.isHeldByCurrentThread())
- {
- _lock.unlock();
- }
- }
- }
-
- private final String id = "(" + String.valueOf(System.identityHashCode(this)) + ")";
-
- private String debugIdentity()
- {
- return id;
- }
-
- final Runner _asyncDelivery = new Runner();
-
- public void debug()
- {
- System.err.println(_extraMessages);
- System.err.println(_messages);
-
- }
-
- public void start(final Subscription subscription)
- {
- DeliveryAgent da = _subscriptions.getDeliveryAgent(subscription);
- if(da != null)
- {
- da.start();
- }
- }
-
-
- private class Runner implements Runnable
- {
- public void run()
- {
- String startName = Thread.currentThread().getName();
- Thread.currentThread().setName("CSDM-AsyncDelivery:" + startName);
- boolean running = true;
- while (running && !_movingMessages.get())
- {
- processQueue();
-
- //Check that messages have not been added since we did our last peek();
- // Synchronize with the thread that adds to the queue.
- // If the queue is still empty then we can exit
- synchronized (_asyncDelivery)
- {
- if (!(hasQueuedMessages() && _subscriptions.hasActiveSubscribers()))
- {
- running = false;
- _processing.set(false);
- }
- }
- }
- Thread.currentThread().setName(startName);
- }
- }
-
- public void processAsync(Executor executor)
- {
- if (_log.isDebugEnabled())
- {
- _log.debug(debugIdentity() + "Processing Async." + currentStatus());
- }
-
- synchronized (_asyncDelivery)
- {
- if (hasQueuedMessages() && _subscriptions.hasActiveSubscribers())
- {
- //are we already running? if so, don't re-run
- if (_processing.compareAndSet(false, true))
- {
- if (_log.isDebugEnabled())
- {
- _log.debug(debugIdentity() + "Executing Async process.");
- }
- executor.execute(_asyncDelivery);
- }
- }
- }
- }
-
- public void closeSubscription(final Subscription subscription)
- {
- _subscriptions.getDeliveryAgent(subscription).close();
- subscription.close();
- }
-
-
- public void resend(final QueueEntry entry, final Subscription subscription)
- {
- final DeliveryAgent deliveryAgent = _subscriptions.getDeliveryAgent(subscription);
- deliveryAgent.addToResendQueue(entry);
-
- }
-
-
- private String currentStatus()
- {
- return " Queued:" + (_messages.isEmpty() ? "Empty " : "Contains(H:M)") +
- "(" + ((ConcurrentLinkedMessageQueueAtomicSize) _messages).headSize() +
- ":" + (_messages.size() - ((ConcurrentLinkedMessageQueueAtomicSize) _messages).headSize()) + ") " +
- " Extra: " + (_hasContent.isEmpty() ? "Empty " : "Contains") +
- "(" + _hasContent.size() + ":" + _extraMessages.get() + ") " +
- " Active:" + _subscriptions.hasActiveSubscribers() +
- " Processing:" + (_processing.get() ? " true : Processing Thread: " + _processingThreadName : " false");
- }
-
-
-
-}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/DeliveryAgent.java b/java/broker/src/main/java/org/apache/qpid/server/queue/DeliveryAgent.java
deleted file mode 100644
index a43e552cef..0000000000
--- a/java/broker/src/main/java/org/apache/qpid/server/queue/DeliveryAgent.java
+++ /dev/null
@@ -1,344 +0,0 @@
-package org.apache.qpid.server.queue;
-
-import org.apache.qpid.server.AMQChannel;
-import org.apache.qpid.server.subscription.Subscription;
-import org.apache.qpid.server.output.ProtocolOutputConverter;
-import org.apache.qpid.AMQException;
-import org.apache.qpid.framing.AMQShortString;
-import org.apache.qpid.util.MessageQueue;
-import org.apache.qpid.util.ConcurrentLinkedMessageQueueAtomicSize;
-import org.apache.qpid.util.ConcurrentLinkedQueueAtomicSize;
-import org.apache.log4j.Logger;
-
-import java.util.Queue;
-
-/*
-*
-* Licensed to the Apache Software Foundation (ASF) under one
-* or more contributor license agreements. See the NOTICE file
-* distributed with this work for additional information
-* regarding copyright ownership. The ASF licenses this file
-* to you under the Apache License, Version 2.0 (the
-* "License"); you may not use this file except in compliance
-* with the License. You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing,
-* software distributed under the License is distributed on an
-* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-* KIND, either express or implied. See the License for the
-* specific language governing permissions and limitations
-* under the License.
-*
-*/
-public class DeliveryAgent
-{
-
- private static final Logger _logger = Logger.getLogger(DeliveryAgent.class);
-
- private final Subscription _subscription;
-
-
-
- private MessageQueue<QueueEntry> _messages;
-
- private Queue<QueueEntry> _resendQueue;
-
- private boolean _sentClose = false;
-
- public DeliveryAgent(final Subscription subscription)
- {
- _subscription = subscription;
-
- if (filtersMessages())
- {
- _messages = new ConcurrentLinkedMessageQueueAtomicSize<QueueEntry>();
- }
- else
- {
- // Reference the DeliveryManager
- _messages = null;
- }
- }
-
-
- public Subscription getSubscription()
- {
- return _subscription;
- }
-
-
- public AMQQueue getQueue()
- {
- return _subscription.getQueue();
- }
-
- public void setQueue(final AMQQueue queue)
- {
- _subscription.setQueue(queue);
- }
-
- public AMQChannel getChannel()
- {
- return _subscription.getChannel();
- }
-
- public boolean isSuspended()
- {
- return _subscription.isSuspended();
- }
-
- public boolean hasInterest(final QueueEntry msg)
- {
- return _subscription.hasInterest(msg);
- }
-
- public boolean isAutoClose()
- {
- return _subscription.isAutoClose();
- }
-
- public boolean isClosed()
- {
- return _subscription.isClosed();
- }
-
- public boolean isBrowser()
- {
- return _subscription.isBrowser();
- }
-
- public void close()
- {
- _subscription.close();
-
- if (_resendQueue != null && !_resendQueue.isEmpty())
- {
- if (_logger.isInfoEnabled())
- {
- _logger.info("Requeuing closing subscription " + this);
- }
- requeue();
- }
-
- //remove references in PDQ
- if (_messages != null)
- {
- if (_logger.isInfoEnabled())
- {
- _logger.info("Clearing PDQ " + this);
- }
-
- _messages.clear();
- }
- }
-
- public boolean filtersMessages()
- {
- return _subscription.filtersMessages();
- }
-
- public void send(final QueueEntry msg)
- throws AMQException
- {
- _subscription.send(msg);
- }
-
- public void queueDeleted(final AMQQueue queue)
- {
- _subscription.queueDeleted(queue);
- }
-
- public Queue<QueueEntry> getPreDeliveryQueue()
- {
- return _messages;
- }
-
- public Queue<QueueEntry> getResendQueue()
- {
- if (_resendQueue == null)
- {
- _resendQueue = new ConcurrentLinkedQueueAtomicSize<QueueEntry>();
- }
- return _resendQueue;
- }
-
- public Queue<QueueEntry> getNextQueue(final Queue<QueueEntry> messages)
- {
- if (_resendQueue != null && !_resendQueue.isEmpty())
- {
- return _resendQueue;
- }
-
- if (filtersMessages())
- {
- if (isAutoClose())
- {
- if (_messages.isEmpty())
- {
- autoclose();
- return null;
- }
- }
- return _messages;
- }
- else // we want the DM queue
- {
- return messages;
- }
- }
-
-
- private void autoclose()
- {
- close();
-
- if (isAutoClose() && !_sentClose)
- {
- _logger.info("Closing autoclose subscription" + this);
-
- ProtocolOutputConverter converter = getChannel().getProtocolSession().getProtocolOutputConverter();
- converter.confirmConsumerAutoClose(getChannel().getChannelId(), getConsumerTag());
- _sentClose = true;
-
- //fixme JIRA do this better
- try
- {
- getChannel().unsubscribeConsumer(getConsumerTag());
- }
- catch (AMQException e)
- {
- // Occurs if we cannot find the subscriber in the channel with protocolSession and consumerTag.
- }
- }
- }
-
-
- public void enqueueForPreDelivery(final QueueEntry msg, final boolean deliverFirst)
- {
- if (_messages != null)
- {
- if (deliverFirst)
- {
- _messages.pushHead(msg);
- }
- else
- {
- _messages.offer(msg);
- }
- }
-
- }
-
- public boolean wouldSuspend(final QueueEntry msg)
- {
- return _subscription.wouldSuspend(msg);
- }
-
- public void addToResendQueue(final QueueEntry msg)
- {
- // add to our resend queue
- getResendQueue().add(msg);
-
- // Mark Queue has having content.
- if (getQueue() == null)
- {
- _logger.error("Queue is null won't be able to resend messages");
- }
- else
- {
- ((AMQQueueImpl) getQueue()).subscriberHasPendingResend(true, getSubscription(), msg);
- }
- }
-
- public Object getSendLock()
- {
- return _subscription.getSendLock();
- }
-
- private void requeue()
- {
- if (getQueue() != null)
- {
- if (_logger.isDebugEnabled())
- {
- _logger.debug("Requeuing :" + _resendQueue.size() + " messages");
- }
-
- while (!_resendQueue.isEmpty())
- {
- QueueEntry resent = _resendQueue.poll();
-
- if (_logger.isTraceEnabled())
- {
- _logger.trace("Removed for resending:" + resent.debugIdentity());
- }
-
- resent.release();
- ((AMQQueueImpl) getQueue()).subscriberHasPendingResend(false, getSubscription(), resent);
-
- try
- {
- getChannel().getTransactionalContext().requeue(resent);
- }
- catch (AMQException e)
- {
- _logger.error("MESSAGE LOSS : Unable to re-deliver messages", e);
- }
- }
-
- if (!_resendQueue.isEmpty())
- {
- _logger.error("[MESSAGES LOST]Unable to re-deliver messages as queue is null.");
- }
-
- ((AMQQueueImpl) getQueue()).subscriberHasPendingResend(false, getSubscription(), null);
- }
- else
- {
- if (!_resendQueue.isEmpty())
- {
- _logger.error("Unable to re-deliver messages as queue is null.");
- }
- }
-
- // Clear the messages
- _resendQueue = null;
- }
-
-
- public String toString()
- {
- return "[Delivery Agent for: "+getSubscription()+"]";
- }
-
- public AMQShortString getConsumerTag()
- {
- return getSubscription().getConumerTag();
- }
-
- boolean ableToDeliver()
- {
- // if the queue is not empty then this client is ready to receive a message.
- //FIXME the queue could be full of sent messages.
- // Either need to clean all PDQs after sending a message
- // OR have a clean up thread that runs the PDQs expunging the messages.
- return (!_subscription.filtersMessages() || getPreDeliveryQueue().isEmpty());
- }
-
- public void start()
- {
- // Check to see if we need to autoclose
- if (filtersMessages())
- {
- if (isAutoClose())
- {
- if (_messages.isEmpty())
- {
- autoclose();
- }
- }
- }
- }
-}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/Filterable.java b/java/broker/src/main/java/org/apache/qpid/server/queue/Filterable.java
new file mode 100644
index 0000000000..d38932bb61
--- /dev/null
+++ b/java/broker/src/main/java/org/apache/qpid/server/queue/Filterable.java
@@ -0,0 +1,33 @@
+/*
+*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*
+*/
+package org.apache.qpid.server.queue;
+
+import org.apache.qpid.framing.ContentHeaderBody;
+import org.apache.qpid.AMQException;
+
+public interface Filterable<E extends Exception>
+{
+ ContentHeaderBody getContentHeaderBody() throws E;
+
+ boolean isPersistent() throws E;
+
+ boolean isRedelivered();
+}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/IncomingMessage.java b/java/broker/src/main/java/org/apache/qpid/server/queue/IncomingMessage.java
index 2dd9f14574..6a3e7e49ef 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/queue/IncomingMessage.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/queue/IncomingMessage.java
@@ -38,8 +38,9 @@ import org.apache.log4j.Logger;
import java.util.List;
import java.util.ArrayList;
+import java.util.Collection;
-public class IncomingMessage
+public class IncomingMessage implements Filterable<RuntimeException>
{
/** Used for debugging purposes. */
@@ -66,7 +67,7 @@ public class IncomingMessage
* delivered. It is <b>cleared after delivery has been attempted</b>. Any persistent record of destinations is done
* by the message handle.
*/
- private List<AMQQueue> _destinationQueues;
+ private Collection<AMQQueue> _destinationQueues;
private AMQProtocolSession _publisher;
private MessageStore _messageStore;
@@ -160,7 +161,7 @@ public class IncomingMessage
// we get a reference to the destination queues now so that we can clear the
// transient message data as quickly as possible
- List<AMQQueue> destinationQueues = _destinationQueues;
+ Collection<AMQQueue> destinationQueues = _destinationQueues;
if (_logger.isDebugEnabled())
{
_logger.debug("Delivering message " + _messageId + " to " + destinationQueues);
@@ -175,9 +176,6 @@ public class IncomingMessage
_messageHandle.setPublishAndContentHeaderBody(_txnContext.getStoreContext(),
_messagePublishInfo, getContentHeaderBody());
- // we then allow the transactional context to do something with the message content
- // now that it has all been received, before we attempt delivery
- _txnContext.messageFullyReceived(isPersistent());
message = new AMQMessage(_messageHandle,_txnContext.getStoreContext(), _messagePublishInfo);
message.setPublisherIdentifier(_publisher.getClientIdentifier());
@@ -214,6 +212,10 @@ public class IncomingMessage
}
}
+ // we then allow the transactional context to do something with the message content
+ // now that it has all been received, before we attempt delivery
+ _txnContext.messageFullyReceived(isPersistent());
+
return message;
}
finally
@@ -283,6 +285,11 @@ public class IncomingMessage
((BasicContentHeaderProperties) getContentHeaderBody().properties).getDeliveryMode() == 2;
}
+ public boolean isRedelivered()
+ {
+ return false;
+ }
+
public void setMessageStore(final MessageStore messageStore)
{
_messageStore = messageStore;
@@ -303,7 +310,7 @@ public class IncomingMessage
_exchange.route(this);
}
- public void enqueue(final List<AMQQueue> queues)
+ public void enqueue(final Collection<AMQQueue> queues)
{
_destinationQueues = queues;
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/ManagedQueue.java b/java/broker/src/main/java/org/apache/qpid/server/queue/ManagedQueue.java
index 061ab56024..2bc94995e9 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/queue/ManagedQueue.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/queue/ManagedQueue.java
@@ -126,7 +126,7 @@ public interface ManagedQueue
* @param age maximum age of message.
* @throws IOException
*/
- @MBeanAttribute(name="MaximumMessageAge", description="Threshold high value for message age on thr broker")
+ @MBeanAttribute(name="MaximumMessageAge", description="Threshold high value for message age on the broker")
void setMaximumMessageAge(Long age) throws IOException;
/**
diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/PriorityQueueList.java b/java/broker/src/main/java/org/apache/qpid/server/queue/PriorityQueueList.java
new file mode 100644
index 0000000000..e6628832cb
--- /dev/null
+++ b/java/broker/src/main/java/org/apache/qpid/server/queue/PriorityQueueList.java
@@ -0,0 +1,164 @@
+/*
+*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*
+*/
+package org.apache.qpid.server.queue;
+
+import org.apache.qpid.framing.CommonContentHeaderProperties;
+import org.apache.qpid.AMQException;
+
+public class PriorityQueueList implements QueueEntryList
+{
+ private final AMQQueue _queue;
+ private final QueueEntryList[] _priorityLists;
+ private final int _priorities;
+ private final int _priorityOffset;
+
+ public PriorityQueueList(AMQQueue queue, int priorities)
+ {
+ _queue = queue;
+ _priorityLists = new QueueEntryList[priorities];
+ _priorities = priorities;
+ _priorityOffset = 5-((priorities + 1)/2);
+ for(int i = 0; i < priorities; i++)
+ {
+ _priorityLists[i] = new SimpleQueueEntryList(queue);
+ }
+ }
+
+ public AMQQueue getQueue()
+ {
+ return _queue;
+ }
+
+ public QueueEntry add(AMQMessage message)
+ {
+ try
+ {
+ int index = ((CommonContentHeaderProperties)((message.getContentHeaderBody().properties))).getPriority() - _priorityOffset;
+ if(index >= _priorities)
+ {
+ index = _priorities-1;
+ }
+ else if(index < 0)
+ {
+ index = 0;
+ }
+ return _priorityLists[index].add(message);
+ }
+ catch (AMQException e)
+ {
+ // TODO - fix AMQ Exception
+ throw new RuntimeException(e);
+ }
+
+ }
+
+ public QueueEntry next(QueueEntry node)
+ {
+ QueueEntryImpl nodeImpl = (QueueEntryImpl)node;
+ QueueEntry next = nodeImpl.getNext();
+
+ if(next == null)
+ {
+ QueueEntryList nodeEntryList = nodeImpl.getQueueEntryList();
+ int index;
+ for(index = _priorityLists.length-1; _priorityLists[index] != nodeEntryList; index--);
+
+ while(next == null && index != 0)
+ {
+ index--;
+ next = ((QueueEntryImpl)_priorityLists[index].getHead()).getNext();
+ }
+
+ }
+ return next;
+ }
+
+ private final class PriorityQueueEntryListIterator implements QueueEntryIterator
+ {
+ private final QueueEntryIterator[] _iterators = new QueueEntryIterator[ _priorityLists.length ];
+ private QueueEntry _lastNode;
+
+ PriorityQueueEntryListIterator()
+ {
+ for(int i = 0; i < _priorityLists.length; i++)
+ {
+ _iterators[i] = _priorityLists[i].iterator();
+ }
+ _lastNode = _iterators[_iterators.length - 1].getNode();
+ }
+
+
+ public boolean atTail()
+ {
+ for(int i = 0; i < _iterators.length; i++)
+ {
+ if(!_iterators[i].atTail())
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public QueueEntry getNode()
+ {
+ return _lastNode;
+ }
+
+ public boolean advance()
+ {
+ for(int i = _iterators.length-1; i >= 0; i--)
+ {
+ if(_iterators[i].advance())
+ {
+ _lastNode = _iterators[i].getNode();
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ public QueueEntryIterator iterator()
+ {
+ return new PriorityQueueEntryListIterator();
+ }
+
+ public QueueEntry getHead()
+ {
+ return _priorityLists[_priorities-1].getHead();
+ }
+
+ static class Factory implements QueueEntryListFactory
+ {
+ private final int _priorities;
+
+ Factory(int priorities)
+ {
+ _priorities = priorities;
+ }
+
+ public QueueEntryList createQueueEntryList(AMQQueue queue)
+ {
+ return new PriorityQueueList(queue, _priorities);
+ }
+ }
+}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntry.java b/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntry.java
index 08dcd0e329..39c28a7355 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntry.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntry.java
@@ -26,6 +26,99 @@ import org.apache.qpid.server.subscription.Subscription;
*/
public interface QueueEntry extends Comparable<QueueEntry>
{
+
+
+
+ public static enum State
+ {
+ AVAILABLE,
+ ACQUIRED,
+ EXPIRED,
+ DEQUEUED
+ }
+
+ public static interface StateChangeListener
+ {
+ public void stateChanged(QueueEntry entry, State oldSate, State newState);
+ }
+
+ public abstract class EntryState
+ {
+ private EntryState()
+ {
+ }
+
+ public abstract State getState();
+ }
+
+
+ public final class AvailableState extends EntryState
+ {
+
+ public State getState()
+ {
+ return State.AVAILABLE;
+ }
+ }
+
+
+ public final class DeletedState extends EntryState
+ {
+
+ public State getState()
+ {
+ return State.DEQUEUED;
+ }
+ }
+
+ public final class ExpiredState extends EntryState
+ {
+
+ public State getState()
+ {
+ return State.EXPIRED;
+ }
+ }
+
+
+ public final class NonSubscriptionAcquiredState extends EntryState
+ {
+ public State getState()
+ {
+ return State.ACQUIRED;
+ }
+ }
+
+ public final class SubscriptionAcquiredState extends EntryState
+ {
+ private final Subscription _subscription;
+
+ public SubscriptionAcquiredState(Subscription subscription)
+ {
+ _subscription = subscription;
+ }
+
+
+ public State getState()
+ {
+ return State.ACQUIRED;
+ }
+
+ public Subscription getSubscription()
+ {
+ return _subscription;
+ }
+ }
+
+
+ final static EntryState AVAILABLE_STATE = new AvailableState();
+ final static EntryState DELETED_STATE = new DeletedState();
+ final static EntryState EXPIRED_STATE = new ExpiredState();
+ final static EntryState NON_SUBSCRIPTION_ACQUIRED_STATE = new NonSubscriptionAcquiredState();
+
+
+
+
AMQQueue getQueue();
AMQMessage getMessage();
@@ -38,8 +131,12 @@ public interface QueueEntry extends Comparable<QueueEntry>
boolean isAcquired();
+ boolean acquire();
boolean acquire(Subscription sub);
+ boolean delete();
+ boolean isDeleted();
+
boolean acquiredBySubscription();
void setDeliveredToSubscription();
@@ -71,4 +168,7 @@ public interface QueueEntry extends Comparable<QueueEntry>
void discard(StoreContext storeContext) throws AMQException;
boolean isQueueDeleted();
+
+ void addStateChangeListener(StateChangeListener listener);
+ boolean removeStateChangeListener(StateChangeListener listener);
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java b/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java
index 54404e23e7..6225501c72 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java
@@ -27,8 +27,9 @@ import org.apache.log4j.Logger;
import java.util.Set;
import java.util.HashSet;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+import java.util.concurrent.atomic.AtomicLongFieldUpdater;
+import java.util.concurrent.CopyOnWriteArraySet;
public class QueueEntryImpl implements QueueEntry
@@ -39,42 +40,77 @@ public class QueueEntryImpl implements QueueEntry
*/
private static final Logger _log = Logger.getLogger(QueueEntryImpl.class);
- private final AMQQueue _queue;
+ private final SimpleQueueEntryList _queueEntryList;
+
private final AMQMessage _message;
private Set<Subscription> _rejectedBy = null;
- private final AtomicReference<Object> _owner = new AtomicReference<Object>();
- private final AtomicLong _entryId = new AtomicLong();
+ private volatile EntryState _state = AVAILABLE_STATE;
+
+ private static final
+ AtomicReferenceFieldUpdater<QueueEntryImpl, EntryState>
+ _stateUpdater =
+ AtomicReferenceFieldUpdater.newUpdater
+ (QueueEntryImpl.class, EntryState.class, "_state");
+
+
+ private volatile Set<StateChangeListener> _stateChangeListeners;
+
+ private static final
+ AtomicReferenceFieldUpdater<QueueEntryImpl, Set>
+ _listenersUpdater =
+ AtomicReferenceFieldUpdater.newUpdater
+ (QueueEntryImpl.class, Set.class, "_stateChangeListeners");
+
+ private static final
+ AtomicLongFieldUpdater<QueueEntryImpl>
+ _entryIdUpdater =
+ AtomicLongFieldUpdater.newUpdater
+ (QueueEntryImpl.class, "_entryId");
- public QueueEntryImpl(AMQQueue queue, AMQMessage message, final long entryId)
+
+ private volatile long _entryId;
+
+ volatile QueueEntryImpl _next;
+
+
+ QueueEntryImpl(SimpleQueueEntryList queueEntryList)
{
- _queue = queue;
+ this(queueEntryList,null,Long.MIN_VALUE);
+ _state = DELETED_STATE;
+ }
+
+
+ public QueueEntryImpl(SimpleQueueEntryList queueEntryList, AMQMessage message, final long entryId)
+ {
+ _queueEntryList = queueEntryList;
_message = message;
- _entryId.set(entryId);
+
+ _entryIdUpdater.set(this, entryId);
}
- public QueueEntryImpl(AMQQueue queue, AMQMessage message)
+ public QueueEntryImpl(SimpleQueueEntryList queueEntryList, AMQMessage message)
{
- _queue = queue;
+ _queueEntryList = queueEntryList;
_message = message;
}
protected void setEntryId(long entryId)
{
- _entryId.set(entryId);
+ _entryIdUpdater.set(this, entryId);
}
protected long getEntryId()
{
- return _entryId.get();
+ return _entryId;
}
public AMQQueue getQueue()
{
- return _queue;
+ return _queueEntryList.getQueue();
}
public AMQMessage getMessage()
@@ -94,23 +130,39 @@ public class QueueEntryImpl implements QueueEntry
public boolean expired() throws AMQException
{
- return getMessage().expired(_queue);
+ return getMessage().expired(getQueue());
}
public boolean isAcquired()
{
- return _owner.get() != null;
+ return _state.getState() == State.ACQUIRED;
+ }
+
+ public boolean acquire()
+ {
+ return acquire(NON_SUBSCRIPTION_ACQUIRED_STATE);
+ }
+
+ private boolean acquire(final EntryState state)
+ {
+ boolean acquired = _stateUpdater.compareAndSet(this,AVAILABLE_STATE, state);
+ if(acquired && _stateChangeListeners != null)
+ {
+ notifyStateChange(State.AVAILABLE, State.ACQUIRED);
+ }
+
+ return acquired;
}
public boolean acquire(Subscription sub)
{
- return !(_owner.compareAndSet(null, sub == null ? this : sub));
+ return acquire(sub.getOwningState());
}
public boolean acquiredBySubscription()
{
- Object owner = _owner.get();
- return (owner != null) && (owner != this);
+
+ return (_state instanceof SubscriptionAcquiredState);
}
public void setDeliveredToSubscription()
@@ -120,7 +172,7 @@ public class QueueEntryImpl implements QueueEntry
public void release()
{
- _owner.set(null);
+ _stateUpdater.set(this,AVAILABLE_STATE);
}
public String debugIdentity()
@@ -141,18 +193,16 @@ public class QueueEntryImpl implements QueueEntry
public Subscription getDeliveredSubscription()
{
- synchronized (this)
- {
- Object owner = _owner.get();
- if (owner instanceof Subscription)
+ EntryState state = _state;
+ if (state instanceof SubscriptionAcquiredState)
{
- return (Subscription) owner;
+ return ((SubscriptionAcquiredState) state).getSubscription();
}
else
{
return null;
}
- }
+
}
public void reject()
@@ -193,25 +243,44 @@ public class QueueEntryImpl implements QueueEntry
public void requeue(final StoreContext storeContext) throws AMQException
{
- _queue.requeue(storeContext, this);
+ getQueue().requeue(storeContext, this);
+ if(_stateChangeListeners != null)
+ {
+ notifyStateChange(QueueEntry.State.ACQUIRED, QueueEntry.State.AVAILABLE);
+ }
}
public void dequeue(final StoreContext storeContext) throws FailedDequeueException
{
+
+
getQueue().dequeue(storeContext, this);
+ if(_stateChangeListeners != null)
+ {
+ notifyStateChange(_state.getState() , QueueEntry.State.DEQUEUED);
+ }
+ }
+
+ private void notifyStateChange(final State oldState, final State newState)
+ {
+ for(StateChangeListener l : _stateChangeListeners)
+ {
+ l.stateChanged(this, oldState, newState);
+ }
}
public void dispose(final StoreContext storeContext) throws MessageCleanupException
{
getMessage().decrementReference(storeContext);
+ delete();
}
public void restoreCredit()
{
- Object owner = _owner.get();
- if(owner instanceof Subscription)
+ EntryState state = _state;
+ if(state instanceof SubscriptionAcquiredState)
{
- Subscription s = (Subscription) owner;
+ Subscription s = ((SubscriptionAcquiredState) _state).getSubscription();
s.restoreCredit(this);
}
}
@@ -232,9 +301,85 @@ public class QueueEntryImpl implements QueueEntry
return getQueue().isDeleted();
}
+ public void addStateChangeListener(StateChangeListener listener)
+ {
+ Set<StateChangeListener> listeners = _stateChangeListeners;
+ if(listeners == null)
+ {
+ _listenersUpdater.compareAndSet(this, null, new CopyOnWriteArraySet<StateChangeListener>());
+ listeners = _stateChangeListeners;
+ }
+
+ listeners.add(listener);
+ }
+
+ public boolean removeStateChangeListener(StateChangeListener listener)
+ {
+ Set<StateChangeListener> listeners = _stateChangeListeners;
+ if(listeners != null)
+ {
+ return listeners.remove(listener);
+ }
+
+ return false;
+ }
+
+
public int compareTo(final QueueEntry o)
{
QueueEntryImpl other = (QueueEntryImpl)o;
return getEntryId() > other.getEntryId() ? 1 : getEntryId() < other.getEntryId() ? -1 : 0;
}
+
+ public QueueEntryImpl getNext()
+ {
+
+ QueueEntryImpl next = nextNode();
+ while(next != null && next.isDeleted())
+ {
+
+ final QueueEntryImpl newNext = next.nextNode();
+ if(newNext != null)
+ {
+ SimpleQueueEntryList._nextUpdater.compareAndSet(this,next, newNext);
+ next = nextNode();
+ }
+ else
+ {
+ next = null;
+ }
+
+ }
+ return next;
+ }
+
+ QueueEntryImpl nextNode()
+ {
+ return _next;
+ }
+
+ public boolean isDeleted()
+ {
+ return _state == DELETED_STATE;
+ }
+
+ public boolean delete()
+ {
+ EntryState state = _state;
+
+ if(state != DELETED_STATE && _stateUpdater.compareAndSet(this,state,DELETED_STATE))
+ {
+ _queueEntryList.advanceHead();
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ public QueueEntryList getQueueEntryList()
+ {
+ return _queueEntryList;
+ }
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryIterator.java b/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryIterator.java
new file mode 100644
index 0000000000..c5c115a2d1
--- /dev/null
+++ b/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryIterator.java
@@ -0,0 +1,30 @@
+/*
+*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*
+*/
+package org.apache.qpid.server.queue;
+
+public interface QueueEntryIterator
+{
+ boolean atTail();
+
+ QueueEntry getNode();
+
+ boolean advance();
+}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryList.java b/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryList.java
index fc32909079..313e076f61 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryList.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryList.java
@@ -1,10 +1,3 @@
-package org.apache.qpid.server.queue;
-
-import org.apache.qpid.server.store.StoreContext;
-
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicReference;
-
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
@@ -25,196 +18,17 @@ import java.util.concurrent.atomic.AtomicReference;
* under the License.
*
*/
-public class QueueEntryList
-{
- private final QueueEntryNode _head = new QueueEntryNode();
-
- private AtomicReference<QueueEntryNode> _tail = new AtomicReference<QueueEntryNode>(_head);
- private final AMQQueue _queue;
-
-
-
- public final class QueueEntryNode extends QueueEntryImpl
- {
- private final AtomicBoolean _deleted = new AtomicBoolean();
- private final AtomicReference<QueueEntryNode> _next = new AtomicReference<QueueEntryNode>();
-
-
-
- public QueueEntryNode()
- {
- super(null,null,Long.MIN_VALUE);
- _deleted.set(true);
- }
-
- public QueueEntryNode(AMQQueue queue, AMQMessage message)
- {
- super(queue,message);
- }
-
- public QueueEntryNode getNext()
- {
-
- QueueEntryNode next = nextNode();
- while(next != null && next.isDeleted())
- {
-
- final QueueEntryList.QueueEntryNode newNext = next.nextNode();
- if(newNext != null)
- {
- _next.compareAndSet(next, newNext);
- next = nextNode();
- }
- else
- {
- next = null;
- }
-
- }
- return next;
- }
-
- private QueueEntryList.QueueEntryNode nextNode()
- {
- return _next.get();
- }
-
- public boolean isDeleted()
- {
- return _deleted.get();
- }
-
-
- public boolean delete()
- {
- if(_deleted.compareAndSet(false,true))
- {
- advanceHead();
- return true;
- }
- else
- {
- return false;
- }
- }
-
-
- public void dispose(final StoreContext storeContext) throws MessageCleanupException
- {
- super.dispose(storeContext);
- delete();
- }
- }
-
-
- public QueueEntryList(AMQQueue queue)
- {
- _queue = queue;
- }
-
- private void advanceHead()
- {
- QueueEntryNode head = _head.nextNode();
- while(head._next.get() != null && head.isDeleted())
- {
-
- final QueueEntryList.QueueEntryNode newhead = head.nextNode();
- if(newhead != null)
- {
- _head._next.compareAndSet(head, newhead);
- }
- head = _head.nextNode();
- }
- }
-
-
- public QueueEntry add(AMQMessage message)
- {
- QueueEntryNode node = new QueueEntryNode(_queue, message);
- for (;;)
- {
- QueueEntryNode tail = _tail.get();
- QueueEntryNode next = tail.nextNode();
- if (tail == _tail.get())
- {
- if (next == null)
- {
- node.setEntryId(tail.getEntryId()+1);
- if (tail._next.compareAndSet(null, node))
- {
- _tail.compareAndSet(tail, node);
-
- return node;
- }
- }
- else
- {
- _tail.compareAndSet(tail, next);
- }
- }
- }
- }
-
-
- public class QueueEntryIterator
- {
-
- private QueueEntryNode _lastNode;
-
- QueueEntryIterator(QueueEntryNode startNode)
- {
- _lastNode = startNode;
- }
-
-
- public boolean atTail()
- {
- return _lastNode.nextNode() == null;
- }
-
- public QueueEntryNode getNode()
- {
-
- return _lastNode;
-
- }
-
- boolean advance()
- {
-
- if(!atTail())
- {
- QueueEntryNode nextNode = _lastNode.nextNode();
- while(nextNode.isDeleted() && nextNode.nextNode() != null)
- {
- nextNode = nextNode.nextNode();
- }
- _lastNode = nextNode;
- return true;
-
- }
- else
- {
- return false;
- }
-
- }
-
- }
-
-
- public QueueEntryIterator iterator()
- {
- return new QueueEntryIterator(_head);
- }
+package org.apache.qpid.server.queue;
+public interface QueueEntryList
+{
+ AMQQueue getQueue();
- public QueueEntryNode getHead()
- {
- return _head;
- }
+ QueueEntry add(AMQMessage message);
+ QueueEntry next(QueueEntry node);
-
+ QueueEntryIterator iterator();
+ QueueEntry getHead();
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryListFactory.java b/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryListFactory.java
new file mode 100644
index 0000000000..4dbce45f67
--- /dev/null
+++ b/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryListFactory.java
@@ -0,0 +1,26 @@
+/*
+*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*
+*/
+package org.apache.qpid.server.queue;
+
+interface QueueEntryListFactory
+{
+ public QueueEntryList createQueueEntryList(AMQQueue queue);
+}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java b/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java
index 6dae7edb72..7e2f0fc56a 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java
@@ -5,8 +5,6 @@ import org.apache.qpid.framing.FieldTable;
import org.apache.qpid.server.virtualhost.VirtualHost;
import org.apache.qpid.server.exchange.Exchange;
import org.apache.qpid.server.store.StoreContext;
-import org.apache.qpid.server.protocol.AMQProtocolSession;
-import org.apache.qpid.server.AMQChannel;
import org.apache.qpid.server.subscription.Subscription;
import org.apache.qpid.server.subscription.SubscriptionList;
import org.apache.qpid.server.output.ProtocolOutputConverter;
@@ -72,9 +70,9 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener
private final List<Task> _deleteTaskList = new CopyOnWriteArrayList<Task>();
- private final AtomicInteger _messageCount = new AtomicInteger(0);
+ private final AtomicInteger _atomicQueueCount = new AtomicInteger(0);
- private final AtomicLong _depth = new AtomicLong(0L);
+ private final AtomicLong _atomicQueueSize = new AtomicLong(0L);
private final AtomicInteger _activeSubscriberCount = new AtomicInteger();
@@ -138,9 +136,22 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener
private AtomicInteger _deliveredMessages = new AtomicInteger();
- SimpleAMQQueue(AMQShortString name, boolean durable, AMQShortString owner, boolean autoDelete, VirtualHost virtualHost)
+
+
+ protected SimpleAMQQueue(AMQShortString name, boolean durable, AMQShortString owner, boolean autoDelete, VirtualHost virtualHost)
throws AMQException
{
+ this(name,durable,owner,autoDelete,virtualHost,new SimpleQueueEntryList.Factory());
+ }
+
+ protected SimpleAMQQueue(AMQShortString name,
+ boolean durable,
+ AMQShortString owner,
+ boolean autoDelete,
+ VirtualHost virtualHost,
+ QueueEntryListFactory entryListFactory)
+ throws AMQException
+ {
if (name == null)
{
@@ -157,7 +168,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener
_owner = owner;
_autoDelete = autoDelete;
_virtualHost = virtualHost;
- _entries = new QueueEntryList(this);
+ _entries = entryListFactory.createQueueEntryList(this);
_asyncDelivery = AsyncDeliveryConfig.getAsyncDeliveryExecutor();
@@ -257,13 +268,13 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener
_activeSubscriberCount.incrementAndGet();
subscription.setStateListener(this);
- subscription.setQueueContext(null,_entries.getHead());
+ subscription.setLastSeenEntry(null,_entries.getHead());
- if(!_deleted.get())
+ if(!isDeleted())
{
subscription.setQueue(this);
_subscriptionList.add(subscription);
- if(_deleted.get())
+ if(isDeleted())
{
subscription.queueDeleted(this);
}
@@ -330,10 +341,11 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener
// need to get the enqueue lock
- _messageCount.incrementAndGet();
+ incrementQueueCount();
+ incrementQueueSize(message);
_totalMessagesReceived.incrementAndGet();
- _depth.addAndGet(message.getSize());
+
QueueEntry entry = _entries.add(message);
@@ -366,7 +378,8 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener
}
-
+ // always do one extra loop after we believe we've finished
+ // this catches the case where we *just* miss an update
int loops = 2;
while(!entry.isAcquired() && loops != 0)
@@ -386,26 +399,23 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener
&& !sub.isSuspended()
&& sub.isActive())
{
-
- if( !sub.wouldSuspend(entry))
+ if( !sub.wouldSuspend(entry))
+ {
+ if(!sub.isBrowser() && !entry.acquire(sub))
+ {
+ sub.restoreCredit(entry);
+ }
+ else
{
- if(!sub.isBrowser() && entry.acquire(sub))
+ deliverMessage(sub, entry);
+ QueueEntry queueEntryNode = sub.getLastSeenEntry();
+ if(_entries.next(queueEntryNode) == entry)
{
- sub.restoreCredit(entry);
+ sub.setLastSeenEntry(queueEntryNode,entry);
}
- else
- {
- deliverMessage(sub, entry);
- QueueEntryList.QueueEntryNode queueEntryNode = (QueueEntryList.QueueEntryNode) sub.getQueueContext();
- if(queueEntryNode.getNext() == entry)
- {
- sub.setQueueContext(queueEntryNode,entry);
- }
- }
}
-
-
+ }
}
}
}
@@ -421,7 +431,26 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener
}
else if(!entry.isAcquired())
{
- deliverAsync();
+ // check that all subscriptions are not in advance of the entry
+ SubscriptionList.SubscriptionNodeIterator subIter = _subscriptionList.iterator();
+ while(subIter.advance() && !entry.isAcquired())
+ {
+ final Subscription subscription = subIter.getNode().getSubscription();
+ QueueEntry subnode = subscription.getLastSeenEntry();
+ while((entry.compareTo(subnode) < 0) && !entry.isAcquired())
+ {
+ if(subscription.setLastSeenEntry(subnode,entry))
+ {
+ break;
+ }
+ else
+ {
+ subnode = subscription.getLastSeenEntry();
+ }
+ }
+
+ }
+ deliverAsync();
}
@@ -439,6 +468,16 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener
}
+ private void incrementQueueSize(final AMQMessage message)
+ {
+ getAtomicQueueSize().addAndGet(message.getSize());
+ }
+
+ private void incrementQueueCount()
+ {
+ getAtomicQueueCount().incrementAndGet();
+ }
+
private void deliverMessage(final Subscription sub, final QueueEntry entry)
throws AMQException
{
@@ -450,15 +489,15 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener
private boolean subscriptionReadyAndHasInterest(final Subscription sub, final QueueEntry entry)
{
- QueueEntryList.QueueEntryNode node = (QueueEntryList.QueueEntryNode) sub.getQueueContext();
+ QueueEntry node = sub.getLastSeenEntry();
while(node.isAcquired() || node.isDeleted() || !sub.hasInterest(node) )
{
- QueueEntryList.QueueEntryNode newNode = node.getNext();
+ QueueEntry newNode = _entries.next(node);
if(newNode != null)
{
- sub.setQueueContext(node, newNode);
- node = (QueueEntryList.QueueEntryNode) sub.getQueueContext();
+ sub.setLastSeenEntry(node, newNode);
+ node = sub.getLastSeenEntry();
}
else
{
@@ -468,7 +507,29 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener
}
- return node == entry;
+ if(node == entry)
+ {
+ return true;
+ }
+ else
+ {
+ node = sub.getLastSeenEntry();
+ if(entry.compareTo(node) < 0 && sub.hasInterest(entry))
+ {
+ do
+ {
+ if(sub.setLastSeenEntry(node,entry))
+ {
+ return true;
+ }
+ else
+ {
+ node = sub.getLastSeenEntry();
+ }
+ } while (entry.compareTo(node) < 0);
+ }
+ return false;
+ }
}
@@ -484,16 +545,16 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener
// we don't make browsers send the same stuff twice
if(!sub.isBrowser())
{
- QueueEntry subEntry = (QueueEntry) sub.getQueueContext();
+ QueueEntry subEntry = sub.getLastSeenEntry();
while(entry.compareTo(subEntry)<0)
{
- if(sub.setQueueContext(subEntry,entry))
+ if(sub.setLastSeenEntry(subEntry,entry))
{
break;
}
else
{
- subEntry = (QueueEntry) sub.getQueueContext();
+ subEntry = sub.getLastSeenEntry();
}
}
}
@@ -507,8 +568,8 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener
public void dequeue(StoreContext storeContext, QueueEntry entry) throws FailedDequeueException
{
- _messageCount.decrementAndGet();
- _depth.addAndGet(-entry.getMessage().getSize());
+ decrementQueueCount();
+ decrementQueueSize(entry);
if(entry.acquiredBySubscription())
{
_deliveredMessages.decrementAndGet();
@@ -521,7 +582,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener
{
_virtualHost.getMessageStore().dequeueMessage(storeContext, getName(), msg.getMessageId());
}
- ((QueueEntryList.QueueEntryNode)entry).delete();
+ entry.delete();
}
catch (MessageCleanupException e)
@@ -540,6 +601,16 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener
}
+ private void decrementQueueSize(final QueueEntry entry)
+ {
+ getAtomicQueueSize().addAndGet(-entry.getMessage().getSize());
+ }
+
+ void decrementQueueCount()
+ {
+ getAtomicQueueCount().decrementAndGet();
+ }
+
public boolean resend(final QueueEntry entry, final Subscription subscription) throws AMQException
{
/* TODO : This is wrong as the subscription may be suspended, we should instead change the state of the message
@@ -585,12 +656,12 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener
public int getMessageCount()
{
- return _messageCount.get();
+ return getAtomicQueueCount().get();
}
public long getQueueDepth()
{
- return _depth.get();
+ return getAtomicQueueSize().get();
}
public int getUndeliveredMessageCount()
@@ -627,10 +698,10 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener
public List<QueueEntry> getMessagesOnTheQueue()
{
ArrayList<QueueEntry> entryList = new ArrayList<QueueEntry>();
- QueueEntryList.QueueEntryIterator queueListIterator = _entries.iterator();
+ QueueEntryIterator queueListIterator = _entries.iterator();
while(queueListIterator.advance())
{
- QueueEntryList.QueueEntryNode node = queueListIterator.getNode();
+ QueueEntry node = queueListIterator.getNode();
if(node != null && !node.isDeleted())
{
entryList.add(node);
@@ -647,13 +718,32 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener
_activeSubscriberCount.decrementAndGet();
}
- else if(newState == Subscription.State.ACTIVE && oldState != Subscription.State.ACTIVE)
- {
- _activeSubscriberCount.incrementAndGet();
+ else if(newState == Subscription.State.ACTIVE)
+ {
+ if(oldState != Subscription.State.ACTIVE)
+ {
+ _activeSubscriberCount.incrementAndGet();
+
+ }
deliverAsync();
}
}
+ public int compareTo(final AMQQueue o)
+ {
+ return _name.compareTo(o.getName());
+ }
+
+ public AtomicInteger getAtomicQueueCount()
+ {
+ return _atomicQueueCount;
+ }
+
+ public AtomicLong getAtomicQueueSize()
+ {
+ return _atomicQueueSize;
+ }
+
public static interface QueueEntryFilter
{
public boolean accept(QueueEntry entry);
@@ -705,10 +795,10 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener
public List<QueueEntry> getMessagesOnTheQueue(QueueEntryFilter filter)
{
ArrayList<QueueEntry> entryList = new ArrayList<QueueEntry>();
- QueueEntryList.QueueEntryIterator queueListIterator = _entries.iterator();
+ QueueEntryIterator queueListIterator = _entries.iterator();
while(queueListIterator.advance() && !filter.filterComplete())
{
- QueueEntryList.QueueEntryNode node = queueListIterator.getNode();
+ QueueEntry node = queueListIterator.getNode();
if(!node.isDeleted() && filter.accept(node))
{
entryList.add(node);
@@ -770,13 +860,13 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener
public void deleteMessageFromTop(StoreContext storeContext) throws AMQException
{
- QueueEntryList.QueueEntryIterator queueListIterator = _entries.iterator();
+ QueueEntryIterator queueListIterator = _entries.iterator();
boolean noDeletes = true;
while(noDeletes && queueListIterator.advance() )
{
- QueueEntryList.QueueEntryNode node = queueListIterator.getNode();
- if(!node.isDeleted() && !node.acquire(null))
+ QueueEntry node = queueListIterator.getNode();
+ if(!node.isDeleted() && node.acquire())
{
node.dequeue(storeContext);
@@ -791,13 +881,13 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener
public long clearQueue(StoreContext storeContext) throws AMQException
{
- QueueEntryList.QueueEntryIterator queueListIterator = _entries.iterator();
+ QueueEntryIterator queueListIterator = _entries.iterator();
long count = 0;
while(queueListIterator.advance())
{
- QueueEntryList.QueueEntryNode node = queueListIterator.getNode();
- if(!node.isDeleted() && !node.acquire(null))
+ QueueEntry node = queueListIterator.getNode();
+ if(!node.isDeleted() && node.acquire())
{
node.dequeue(storeContext);
@@ -864,8 +954,6 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener
{
public void run()
{
- String startName = Thread.currentThread().getName();
- Thread.currentThread().setName("async delivery (Q: " + getName() + " VH: " + _virtualHost.getName() +")");
try
{
processQueue(this);
@@ -874,8 +962,108 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener
{
_logger.error(e);
}
- Thread.currentThread().setName(startName);
+
+ }
+ }
+
+ public void flushSubscription(Subscription sub) throws AMQException
+ {
+ boolean atTail = false;
+ while(sub.isActive() && !atTail)
+ {
+
+ synchronized(sub.getSendLock())
+ {
+ if(sub.isActive())
+ {
+
+ QueueEntry node = moveSubscriptionToNextNode(sub);
+
+ if(!(node.isAcquired() || node.isDeleted()))
+ {
+ if(!sub.isSuspended())
+ {
+ if(sub.hasInterest(node))
+ {
+ if(!sub.wouldSuspend(node))
+ {
+ if(!sub.isBrowser() && !node.acquire(sub))
+ {
+ sub.restoreCredit(node);
+
+ }
+ else
+ {
+ deliverMessage(sub, node);
+
+ if(sub.isBrowser())
+ {
+ QueueEntry newNode = _entries.next(node);
+
+ if(newNode != null)
+ {
+ sub.setLastSeenEntry(node, newNode);
+ node = sub.getLastSeenEntry();
+ }
+
+
+ }
+ }
+
+ }
+ else
+ {
+ break;
+ }
+ }
+ else
+ {
+ // this subscription is not interested in this node so we can skip over it
+ QueueEntry newNode = _entries.next(node);
+ if(newNode != null)
+ {
+ sub.setLastSeenEntry(node, newNode);
+ }
+ }
+ }
+ }
+ atTail = (_entries.next(node) == null);
+
+ }
+ }
+
+ }
+ }
+
+ private QueueEntry moveSubscriptionToNextNode(final Subscription sub)
+ throws AMQException
+ {
+ QueueEntry node = sub.getLastSeenEntry();
+ while(node.isAcquired() || node.isDeleted() || node.expired())
+ {
+ if(!node.isAcquired() && !node.isDeleted() && node.expired())
+ {
+ if(node.acquire())
+ {
+ final StoreContext reapingStoreContext = new StoreContext();
+ node.dequeue(reapingStoreContext);
+ node.dispose(reapingStoreContext);
+
+ }
+ }
+ QueueEntry newNode = _entries.next(node);
+ if(newNode != null)
+ {
+ sub.setLastSeenEntry(node, newNode);
+ node = sub.getLastSeenEntry();
+ }
+ else
+ {
+ break;
+ }
+
}
+ return node;
}
@@ -916,31 +1104,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener
{
boolean advanced = false;
- QueueEntryList.QueueEntryNode node = (QueueEntryList.QueueEntryNode) sub.getQueueContext();
- while(node.isAcquired() || node.isDeleted() || node.expired())
- {
- if(!node.isAcquired() && !node.isDeleted() && node.expired())
- {
- if(node.acquire(null))
- {
- final StoreContext reapingStoreContext = new StoreContext();
- node.dequeue(reapingStoreContext);
- node.dispose(reapingStoreContext);
-
- }
- }
- QueueEntryList.QueueEntryNode newNode = node.getNext();
- if(newNode != null)
- {
- sub.setQueueContext(node, newNode);
- node = (QueueEntryList.QueueEntryNode) sub.getQueueContext();
- }
- else
- {
- break;
- }
-
- }
+ QueueEntry node = moveSubscriptionToNextNode(sub);
if(!(node.isAcquired() || node.isDeleted()))
{
if(!sub.isSuspended())
@@ -949,9 +1113,10 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener
{
if(!sub.wouldSuspend(node))
{
- if(!sub.isBrowser() && node.acquire(sub))
+ if(!sub.isBrowser() && !node.acquire(sub))
{
sub.restoreCredit(node);
+
}
else
{
@@ -959,12 +1124,12 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener
if(sub.isBrowser())
{
- QueueEntryList.QueueEntryNode newNode = node.getNext();
+ QueueEntry newNode = _entries.next(node);
if(newNode != null)
{
- sub.setQueueContext(node, newNode);
- node = (QueueEntryList.QueueEntryNode) sub.getQueueContext();
+ sub.setLastSeenEntry(node, newNode);
+ node = sub.getLastSeenEntry();
advanced = true;
}
@@ -973,19 +1138,23 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener
}
done = false;
}
+ else
+ {
+ node.addStateChangeListener(new QueueEntryListener(sub,node));
+ }
}
else
{
// this subscription is not interested in this node so we can skip over it
- QueueEntryList.QueueEntryNode newNode = node.getNext();
+ QueueEntry newNode = _entries.next(node);
if(newNode != null)
{
- sub.setQueueContext(node, newNode);
+ sub.setLastSeenEntry(node, newNode);
}
}
}
}
- final boolean atTail = (node.getNext() == null);
+ final boolean atTail = (_entries.next(node) == null);
done = done && atTail;
@@ -994,7 +1163,7 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener
unregisterSubscription(sub);
ProtocolOutputConverter converter = sub.getChannel().getProtocolSession().getProtocolOutputConverter();
- converter.confirmConsumerAutoClose(sub.getChannel().getChannelId(), sub.getConumerTag());
+ converter.confirmConsumerAutoClose(sub.getChannel().getChannelId(), sub.getConsumerTag());
}
@@ -1026,24 +1195,18 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener
}
- public boolean performGet(final AMQProtocolSession session, final AMQChannel channel, final boolean b)
- throws AMQException
- {
- return false; //To change body of implemented methods use File | Settings | File Templates.
- }
-
public void removeExpiredIfNoSubscribers() throws AMQException
{
final StoreContext storeContext = new StoreContext();
- QueueEntryList.QueueEntryIterator queueListIterator = _entries.iterator();
+ QueueEntryIterator queueListIterator = _entries.iterator();
while(queueListIterator.advance())
{
- QueueEntryList.QueueEntryNode node = queueListIterator.getNode();
- if(!node.isDeleted() && node.expired() && node.acquire(null))
+ QueueEntry node = queueListIterator.getNode();
+ if(!node.isDeleted() && node.expired() && node.acquire())
{
node.dequeue(storeContext);
@@ -1159,8 +1322,34 @@ public class SimpleAMQQueue implements AMQQueue, Subscription.StateListener
return _managedObject;
}
- public int compareTo(final Object o)
+ public int N(final Object o)
{
return _name.compareTo(((AMQQueue) o).getName());
}
+
+ private final class QueueEntryListener implements QueueEntry.StateChangeListener
+ {
+ private final QueueEntry _entry;
+
+ public QueueEntryListener(final Subscription sub, final QueueEntry entry)
+ {
+ _entry = entry;
+ }
+
+ public boolean equals(Object o)
+ {
+ return _entry == ((QueueEntryListener)o)._entry;
+ }
+
+ public int hashCode()
+ {
+ return System.identityHashCode(_entry);
+ }
+
+ public void stateChanged(QueueEntry entry, QueueEntry.State oldSate, QueueEntry.State newState)
+ {
+ entry.removeStateChangeListener(this);
+ deliverAsync();
+ }
+ }
} \ No newline at end of file
diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleQueueEntryList.java b/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleQueueEntryList.java
new file mode 100644
index 0000000000..a46c5ae2e8
--- /dev/null
+++ b/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleQueueEntryList.java
@@ -0,0 +1,178 @@
+package org.apache.qpid.server.queue;
+
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+
+/*
+*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*
+*/
+public class SimpleQueueEntryList implements QueueEntryList
+{
+ private final QueueEntryImpl _head;
+
+ private volatile QueueEntryImpl _tail;
+
+ static final AtomicReferenceFieldUpdater<SimpleQueueEntryList, QueueEntryImpl>
+ _tailUpdater =
+ AtomicReferenceFieldUpdater.newUpdater
+ (SimpleQueueEntryList.class, QueueEntryImpl.class, "_tail");
+
+
+ private final AMQQueue _queue;
+
+ static final AtomicReferenceFieldUpdater<QueueEntryImpl, QueueEntryImpl>
+ _nextUpdater =
+ AtomicReferenceFieldUpdater.newUpdater
+ (QueueEntryImpl.class, QueueEntryImpl.class, "_next");
+
+
+
+
+
+ public SimpleQueueEntryList(AMQQueue queue)
+ {
+ _queue = queue;
+ _head = new QueueEntryImpl(this);
+ _tail = _head;
+ }
+
+ void advanceHead()
+ {
+ QueueEntryImpl head = _head.nextNode();
+ while(head._next != null && head.isDeleted())
+ {
+
+ final QueueEntryImpl newhead = head.nextNode();
+ if(newhead != null)
+ {
+ _nextUpdater.compareAndSet(_head,head, newhead);
+ }
+ head = _head.nextNode();
+ }
+ }
+
+
+ public AMQQueue getQueue()
+ {
+ return _queue;
+ }
+
+
+ public QueueEntry add(AMQMessage message)
+ {
+ QueueEntryImpl node = new QueueEntryImpl(this, message);
+ for (;;)
+ {
+ QueueEntryImpl tail = _tail;
+ QueueEntryImpl next = tail.nextNode();
+ if (tail == _tail)
+ {
+ if (next == null)
+ {
+ node.setEntryId(tail.getEntryId()+1);
+ if (_nextUpdater.compareAndSet(tail, null, node))
+ {
+ _tailUpdater.compareAndSet(this, tail, node);
+
+ return node;
+ }
+ }
+ else
+ {
+ _tailUpdater.compareAndSet(this,tail, next);
+ }
+ }
+ }
+ }
+
+ public QueueEntry next(QueueEntry node)
+ {
+ return ((QueueEntryImpl)node).getNext();
+ }
+
+
+ public class QueueEntryIteratorImpl implements QueueEntryIterator
+ {
+
+ private QueueEntryImpl _lastNode;
+
+ QueueEntryIteratorImpl(QueueEntryImpl startNode)
+ {
+ _lastNode = startNode;
+ }
+
+
+ public boolean atTail()
+ {
+ return _lastNode.nextNode() == null;
+ }
+
+ public QueueEntry getNode()
+ {
+
+ return _lastNode;
+
+ }
+
+ public boolean advance()
+ {
+
+ if(!atTail())
+ {
+ QueueEntryImpl nextNode = _lastNode.nextNode();
+ while(nextNode.isDeleted() && nextNode.nextNode() != null)
+ {
+ nextNode = nextNode.nextNode();
+ }
+ _lastNode = nextNode;
+ return true;
+
+ }
+ else
+ {
+ return false;
+ }
+
+ }
+
+ }
+
+
+ public QueueEntryIterator iterator()
+ {
+ return new QueueEntryIteratorImpl(_head);
+ }
+
+
+ public QueueEntry getHead()
+ {
+ return _head;
+ }
+
+ static class Factory implements QueueEntryListFactory
+ {
+
+ public QueueEntryList createQueueEntryList(AMQQueue queue)
+ {
+ return new SimpleQueueEntryList(queue);
+ }
+ }
+
+
+}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionManager.java b/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionManager.java
deleted file mode 100644
index 4c39eb3397..0000000000
--- a/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionManager.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.queue;
-
-import org.apache.qpid.server.subscription.Subscription;
-
-import java.util.List;
-
-/**
- * Abstraction of actor that will determine the subscriber to whom
- * a message will be sent.
- */
-public interface SubscriptionManager
-{
- public List<Subscription> getSubscriptions();
- public boolean hasActiveSubscribers();
- public int getActiveConsumerCount();
- public int getConsumerCount();
- public Subscription nextSubscriber(QueueEntry entry);
- public void addSubscriber(Subscription subscription);
- public Subscription removeSubscriber(Subscription subscription);
- public boolean isEmpty();
- public void queueDeleted(AMQQueue queue);
-
-
- void setExclusive(final boolean b);
-
- Object getChangeLock();
-}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionSet.java b/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionSet.java
deleted file mode 100644
index 117799ad87..0000000000
--- a/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionSet.java
+++ /dev/null
@@ -1,278 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.queue;
-
-import java.util.List;
-import java.util.ListIterator;
-import java.util.Map;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.ConcurrentHashMap;
-
-import org.apache.log4j.Logger;
-import org.apache.qpid.server.subscription.Subscription;
-
-/** Holds a set of subscriptions for a queue and manages the round robin-ing of deliver etc. */
-class SubscriptionSet implements SubscriptionManager
-{
- private static final Logger _log = Logger.getLogger(SubscriptionSet.class);
-
- /** List of registered subscribers */
- private final List<Subscription> _subscriptions = new CopyOnWriteArrayList<Subscription>();
-
- private final Map<Subscription, DeliveryAgent> _deliveryAgents =
- new ConcurrentHashMap<Subscription, DeliveryAgent>();
-
-
- /** Used to control the round robin delivery of content */
- private int _currentSubscriber;
- private final Object _changeLock = new Object();
- private volatile boolean _exclusive;
-
-
- /** Accessor for unit tests. */
- int getCurrentSubscriber()
- {
- return _currentSubscriber;
- }
-
- public void addSubscriber(Subscription subscription)
- {
- synchronized (_changeLock)
- {
- _subscriptions.add(subscription);
- _deliveryAgents.put(subscription, new DeliveryAgent(subscription));
- }
- }
-
- /**
- * Remove the subscription, returning it if it was found
- *
- * @param subscription
- *
- * @return null if no match was found
- */
- public Subscription removeSubscriber(Subscription subscription)
- {
- // TODO: possibly need O(1) operation here.
-
- synchronized (_changeLock)
- {
-
- if (_subscriptions.remove(subscription))
- {
- _deliveryAgents.remove(subscription);
- return subscription;
- }
- else
- {
- _log.error("Unable to remove subscription:" + subscription);
- debugDumpSubscription(subscription);
- return null;
- }
- }
- }
-
- private void debugDumpSubscription(Subscription subscription)
- {
- if (_log.isDebugEnabled())
- {
- _log.debug("Subscription " + subscription + " not found. Dumping subscriptions:");
- for (Subscription s : _subscriptions)
- {
- _log.debug("Subscription: " + s);
- }
- _log.debug("Subscription dump complete");
- }
- }
-
- /**
- * Return the next unsuspended subscription or null if not found. <p/> Performance note: This method can scan all
- * items twice when looking for a subscription that is not suspended. The worst case occcurs when all subscriptions
- * are suspended. However, it is does this without synchronisation and subscriptions may be added and removed
- * concurrently. Also note that because of race conditions and when subscriptions are removed between calls to
- * nextSubscriber, the IndexOutOfBoundsException also causes the scan to start at the beginning.
- */
- public Subscription nextSubscriber(QueueEntry msg)
- {
-
-
- try
- {
- final Subscription result = nextSubscriberImpl(msg);
- if (result == null)
- {
- _currentSubscriber = 0;
- return nextSubscriberImpl(msg);
- }
- else
- {
- return result;
- }
- }
- catch (IndexOutOfBoundsException e)
- {
- _currentSubscriber = 0;
- return nextSubscriber(msg);
- }
- }
-
- private Subscription nextSubscriberImpl(QueueEntry msg)
- {
- if(_exclusive)
- {
- try
- {
- Subscription subscription = _subscriptions.get(0);
- subscriberScanned();
-
- if (!subscription.isSuspended() )
- {
- if (subscription.hasInterest(msg))
- {
- DeliveryAgent deliverAgent = _deliveryAgents.get(subscription);
-
- if (deliverAgent.ableToDeliver())
- {
- if(!subscription.wouldSuspend(msg))
- {
- return subscription;
- }
- }
- }
- }
- }
- catch(IndexOutOfBoundsException e)
- {
- }
- return null;
- }
- else
- {
- if (_subscriptions.isEmpty())
- {
- return null;
- }
- final ListIterator<Subscription> iterator = _subscriptions.listIterator(_currentSubscriber);
- while (iterator.hasNext())
- {
-
- Subscription subscription = iterator.next();
- DeliveryAgent deliverAgent = _deliveryAgents.get(subscription);
- ++_currentSubscriber;
- subscriberScanned();
-
- if (!(deliverAgent == null || subscription.isSuspended()))
- {
- if (subscription.hasInterest(msg) && deliverAgent.ableToDeliver())
- {
- if (!subscription.wouldSuspend(msg))
- {
- return subscription;
- }
- }
- }
- }
-
- return null;
- }
- }
-
- /** Overridden in test classes. */
- protected void subscriberScanned()
- {
- }
-
- public boolean isEmpty()
- {
- return _subscriptions.isEmpty();
- }
-
- public List<Subscription> getSubscriptions()
- {
- return _subscriptions;
- }
-
- public boolean hasActiveSubscribers()
- {
- for (Subscription s : _subscriptions)
- {
- if (!s.isSuspended())
- {
- return true;
- }
- }
- return false;
- }
-
- public int getActiveConsumerCount()
- {
- int count = 0;
- for (Subscription s : _subscriptions)
- {
- if (s.isActive())
- {
- count++;
- }
- }
- return count;
- }
-
- public int getConsumerCount()
- {
- return size();
- }
-
- /**
- * Notification that a queue has been deleted. This is called so that the subscription can inform the channel, which
- * in turn can update its list of unacknowledged messages.
- *
- * @param queue
- */
- public void queueDeleted(AMQQueue queue)
- {
- for (Subscription s : _subscriptions)
- {
- s.queueDeleted(queue);
- }
- }
-
- int size()
- {
- return _subscriptions.size();
- }
-
-
- public Object getChangeLock()
- {
- return _changeLock;
- }
-
- public void setExclusive(final boolean exclusive)
- {
- _exclusive = exclusive;
- }
-
-
- public DeliveryAgent getDeliveryAgent(final Subscription sub)
- {
- return _deliveryAgents.get(sub);
- }
-}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java b/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java
index 455983c6d8..d0dcd051f0 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java
@@ -94,7 +94,11 @@ public abstract class ApplicationRegistry implements IApplicationRegistry
{
try
{
- _instanceMap.get(instanceID).close();
+ IApplicationRegistry instance = _instanceMap.get(instanceID);
+ if(instance != null)
+ {
+ instance.close();
+ }
}
catch (Exception e)
{
diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/DerbyMessageStore.java b/java/broker/src/main/java/org/apache/qpid/server/store/DerbyMessageStore.java
index 180d47001b..59b4e62974 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/store/DerbyMessageStore.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/store/DerbyMessageStore.java
@@ -13,7 +13,6 @@ import org.apache.qpid.server.txn.NonTransactionalContext;
import org.apache.qpid.AMQException;
import org.apache.qpid.framing.AMQShortString;
import org.apache.qpid.framing.FieldTable;
-import org.apache.qpid.framing.BasicContentHeaderProperties;
import org.apache.qpid.framing.ContentHeaderBody;
import org.apache.qpid.framing.abstraction.ContentChunk;
import org.apache.qpid.framing.abstraction.MessagePublishInfo;
@@ -31,12 +30,9 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Blob;
import java.sql.Types;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.List;
-import java.util.Collections;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
@@ -328,7 +324,8 @@ public class DerbyMessageStore implements MessageStore
String queueName = rs.getString(1);
String owner = rs.getString(2);
AMQShortString queueNameShortString = new AMQShortString(queueName);
- AMQQueue q = AMQQueueFactory.createAMQQueueImpl(queueNameShortString, true, owner == null ? null : new AMQShortString(owner), false, _virtualHost);
+ AMQQueue q = AMQQueueFactory.createAMQQueueImpl(queueNameShortString, true, owner == null ? null : new AMQShortString(owner), false, _virtualHost,
+ null);
_virtualHost.getQueueRegistry().registerQueue(q);
queueMap.put(queueNameShortString,q);
@@ -1317,7 +1314,7 @@ public class DerbyMessageStore implements MessageStore
AMQQueue queue = queues.get(queueName);
if (queue == null)
{
- queue = AMQQueueFactory.createAMQQueueImpl(queueName, false, null, false, _virtualHost);
+ queue = AMQQueueFactory.createAMQQueueImpl(queueName, false, null, false, _virtualHost, null);
_virtualHost.getQueueRegistry().registerQueue(queue);
queues.put(queueName, queue);
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/MemoryMessageStore.java b/java/broker/src/main/java/org/apache/qpid/server/store/MemoryMessageStore.java
index 3316302719..f048059b9b 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/store/MemoryMessageStore.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/store/MemoryMessageStore.java
@@ -26,7 +26,6 @@ import org.apache.qpid.AMQException;
import org.apache.qpid.framing.AMQShortString;
import org.apache.qpid.framing.FieldTable;
import org.apache.qpid.framing.abstraction.ContentChunk;
-import org.apache.qpid.server.queue.AMQQueueImpl;
import org.apache.qpid.server.queue.MessageMetaData;
import org.apache.qpid.server.exchange.Exchange;
import org.apache.qpid.server.queue.AMQQueue;
diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/StoreContext.java b/java/broker/src/main/java/org/apache/qpid/server/store/StoreContext.java
index 3ee49d58cf..fdb56a1a55 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/store/StoreContext.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/store/StoreContext.java
@@ -37,7 +37,7 @@ public class StoreContext
public StoreContext()
{
- _name = super.toString();
+ _name = "StoreContext";
}
public StoreContext(String name)
@@ -52,7 +52,10 @@ public class StoreContext
public void setPayload(Object payload)
{
- _logger.debug("public void setPayload(Object payload = " + payload + "): called");
+ if(_logger.isDebugEnabled())
+ {
+ _logger.debug("public void setPayload(Object payload = " + payload + "): called");
+ }
_payload = payload;
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/subscription/ClientDeliveryMethod.java b/java/broker/src/main/java/org/apache/qpid/server/subscription/ClientDeliveryMethod.java
new file mode 100644
index 0000000000..fbc8b3af7d
--- /dev/null
+++ b/java/broker/src/main/java/org/apache/qpid/server/subscription/ClientDeliveryMethod.java
@@ -0,0 +1,29 @@
+/*
+*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*
+*/
+package org.apache.qpid.server.subscription;
+
+import org.apache.qpid.server.queue.QueueEntry;
+import org.apache.qpid.AMQException;
+
+public interface ClientDeliveryMethod
+{
+ void deliverToClient(final Subscription sub, final QueueEntry entry, final long deliveryTag) throws AMQException;
+}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/subscription/RecordDeliveryMethod.java b/java/broker/src/main/java/org/apache/qpid/server/subscription/RecordDeliveryMethod.java
new file mode 100644
index 0000000000..e2ed4104de
--- /dev/null
+++ b/java/broker/src/main/java/org/apache/qpid/server/subscription/RecordDeliveryMethod.java
@@ -0,0 +1,28 @@
+/*
+*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*
+*/
+package org.apache.qpid.server.subscription;
+
+import org.apache.qpid.server.queue.QueueEntry;
+
+public interface RecordDeliveryMethod
+{
+ void recordMessageDelivery(final Subscription sub, final QueueEntry entry, final long deliveryTag);
+}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription.java b/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription.java
index 3f9bd65008..acbeae0f40 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription.java
@@ -45,11 +45,13 @@ public interface Subscription
AMQQueue getQueue();
+ QueueEntry.SubscriptionAcquiredState getOwningState();
+
void setQueue(AMQQueue queue);
AMQChannel getChannel();
- AMQShortString getConumerTag();
+ AMQShortString getConsumerTag();
boolean isSuspended();
@@ -63,8 +65,6 @@ public interface Subscription
void close();
-
-
boolean filtersMessages();
void send(QueueEntry msg) throws AMQException;
@@ -72,26 +72,8 @@ public interface Subscription
void queueDeleted(AMQQueue queue);
-
-
-
-// Queue<QueueEntry> getPreDeliveryQueue();
-//
-// Queue<QueueEntry> getResendQueue();
-//
-// Queue<QueueEntry> getNextQueue(Queue<QueueEntry> messages);
-//
-// void enqueueForPreDelivery(QueueEntry msg, boolean deliverFirst);
-//
-
-
-
-
-
boolean wouldSuspend(QueueEntry msg);
-// void addToResendQueue(QueueEntry msg);
-//
Object getSendLock();
@@ -101,8 +83,8 @@ public interface Subscription
void setStateListener(final StateListener listener);
- Object getQueueContext();
+ QueueEntry getLastSeenEntry();
- boolean setQueueContext(Object expected, Object newValue);
+ boolean setLastSeenEntry(QueueEntry expected, QueueEntry newValue);
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionFactory.java b/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionFactory.java
index 3e6db071e3..ce0362d73f 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionFactory.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionFactory.java
@@ -26,6 +26,7 @@ import org.apache.qpid.framing.FieldTable;
import org.apache.qpid.server.protocol.AMQProtocolSession;
import org.apache.qpid.server.flow.FlowCreditManager;
import org.apache.qpid.server.subscription.Subscription;
+import org.apache.qpid.server.AMQChannel;
/**
* Allows the customisation of the creation of a subscription. This is typically done within an AMQQueue. This factory
@@ -44,4 +45,15 @@ public interface SubscriptionFactory
boolean noLocal, FlowCreditManager creditManager) throws AMQException;
+ Subscription createSubscription(AMQChannel channel,
+ AMQProtocolSession protocolSession,
+ AMQShortString consumerTag,
+ boolean acks,
+ FieldTable filters,
+ boolean noLocal,
+ FlowCreditManager creditManager,
+ ClientDeliveryMethod clientMethod,
+ RecordDeliveryMethod recordMethod
+ )
+ throws AMQException;
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionFactoryImpl.java b/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionFactoryImpl.java
index da419e4ff0..5badbad642 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionFactoryImpl.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionFactoryImpl.java
@@ -24,9 +24,11 @@ import org.apache.qpid.server.protocol.AMQProtocolSession;
import org.apache.qpid.server.flow.FlowCreditManager;
import org.apache.qpid.server.subscription.Subscription;
import org.apache.qpid.server.subscription.SubscriptionFactory;
+import org.apache.qpid.server.AMQChannel;
import org.apache.qpid.framing.AMQShortString;
import org.apache.qpid.framing.FieldTable;
import org.apache.qpid.AMQException;
+import org.apache.qpid.protocol.AMQConstant;
import org.apache.qpid.common.AMQPFilterTypes;
public class SubscriptionFactoryImpl implements SubscriptionFactory
@@ -37,11 +39,39 @@ public class SubscriptionFactoryImpl implements SubscriptionFactory
}*/
- public Subscription createSubscription(int channel, AMQProtocolSession protocolSession,
+ public Subscription createSubscription(int channelId, AMQProtocolSession protocolSession,
AMQShortString consumerTag, boolean acks, FieldTable filters,
boolean noLocal, FlowCreditManager creditManager) throws AMQException
{
+ AMQChannel channel = protocolSession.getChannel(channelId);
+ if (channel == null)
+ {
+ throw new AMQException(AMQConstant.NOT_FOUND, "channel :" + channelId + " not found in protocol session");
+ }
+ ClientDeliveryMethod clientMethod = channel.getClientDeliveryMethod();
+ RecordDeliveryMethod recordMethod = channel.getRecordDeliveryMethod();
+
+ return createSubscription(channel, protocolSession, consumerTag, acks, filters,
+ noLocal,
+ creditManager,
+ clientMethod,
+ recordMethod
+ );
+ }
+
+ public Subscription createSubscription(final AMQChannel channel,
+ final AMQProtocolSession protocolSession,
+ final AMQShortString consumerTag,
+ final boolean acks,
+ final FieldTable filters,
+ final boolean noLocal,
+ final FlowCreditManager creditManager,
+ final ClientDeliveryMethod clientMethod,
+ final RecordDeliveryMethod recordMethod
+ )
+ throws AMQException
+ {
boolean isBrowser;
if (filters != null)
@@ -56,19 +86,18 @@ public class SubscriptionFactoryImpl implements SubscriptionFactory
if(isBrowser)
{
- return new SubscriptionImpl.BrowserSubscription(channel, protocolSession, consumerTag, filters, noLocal, creditManager);
+ return new SubscriptionImpl.BrowserSubscription(channel, protocolSession, consumerTag, filters, noLocal, creditManager, clientMethod, recordMethod);
}
else if(acks)
{
- return new SubscriptionImpl.AckSubscription(channel, protocolSession, consumerTag, filters, noLocal, creditManager);
+ return new SubscriptionImpl.AckSubscription(channel, protocolSession, consumerTag, filters, noLocal, creditManager, clientMethod, recordMethod);
}
else
{
- return new SubscriptionImpl.NoAckSubscription(channel, protocolSession, consumerTag, filters, noLocal, creditManager);
+ return new SubscriptionImpl.NoAckSubscription(channel, protocolSession, consumerTag, filters, noLocal, creditManager, clientMethod, recordMethod);
}
}
-
public static final SubscriptionFactoryImpl INSTANCE = new SubscriptionFactoryImpl();
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java b/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java
index 6f28bbba64..653c7de514 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java
@@ -25,7 +25,6 @@ import java.util.concurrent.atomic.AtomicReference;
import org.apache.log4j.Logger;
import org.apache.qpid.AMQException;
-import org.apache.qpid.protocol.AMQConstant;
import org.apache.qpid.common.AMQPFilterTypes;
import org.apache.qpid.common.ClientProperties;
import org.apache.qpid.framing.AMQShortString;
@@ -46,7 +45,6 @@ import org.apache.qpid.server.store.StoreContext;
*/
public abstract class SubscriptionImpl implements Subscription, FlowCreditManager.FlowCreditManagerListener
{
- private final AtomicBoolean _suspended = new AtomicBoolean(false);
private StateListener _stateListener = new StateListener()
{
@@ -59,18 +57,26 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage
private final AtomicReference<State> _state = new AtomicReference<State>(State.ACTIVE);
- private final AtomicReference<Object> _queueContext = new AtomicReference<Object>(null);
+ private final AtomicReference<QueueEntry> _queueContext = new AtomicReference<QueueEntry>(null);
+ private final ClientDeliveryMethod _deliveryMethod;
+ private final RecordDeliveryMethod _recordMethod;
+
+ private QueueEntry.SubscriptionAcquiredState _owningState = new QueueEntry.SubscriptionAcquiredState(this);
+
static final class BrowserSubscription extends SubscriptionImpl
{
- public BrowserSubscription(int channelId, AMQProtocolSession protocolSession,
+ public BrowserSubscription(AMQChannel channel, AMQProtocolSession protocolSession,
AMQShortString consumerTag, FieldTable filters,
- boolean noLocal, FlowCreditManager creditManager)
+ boolean noLocal, FlowCreditManager creditManager,
+ ClientDeliveryMethod deliveryMethod,
+ RecordDeliveryMethod recordMethod)
throws AMQException
{
- super(channelId, protocolSession, consumerTag, filters, noLocal, creditManager);
+ super(channel, protocolSession, consumerTag, filters, noLocal, creditManager, deliveryMethod, recordMethod);
}
+
public boolean isBrowser()
{
return true;
@@ -91,21 +97,22 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage
synchronized (getChannel())
{
long deliveryTag = getChannel().getNextDeliveryTag();
-
- getProtocolSession().getProtocolOutputConverter().writeDeliver(msg.getMessage(), getChannel().getChannelId(), deliveryTag, getConumerTag());
+ sendToClient(msg, deliveryTag);
}
}
}
- static final class NoAckSubscription extends SubscriptionImpl
+ public static class NoAckSubscription extends SubscriptionImpl
{
- public NoAckSubscription(int channelId, AMQProtocolSession protocolSession,
+ public NoAckSubscription(AMQChannel channel, AMQProtocolSession protocolSession,
AMQShortString consumerTag, FieldTable filters,
- boolean noLocal, FlowCreditManager creditManager)
+ boolean noLocal, FlowCreditManager creditManager,
+ ClientDeliveryMethod deliveryMethod,
+ RecordDeliveryMethod recordMethod)
throws AMQException
{
- super(channelId, protocolSession, consumerTag, filters, noLocal, creditManager);
+ super(channel, protocolSession, consumerTag, filters, noLocal, creditManager, deliveryMethod, recordMethod);
}
@@ -142,7 +149,7 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage
{
long deliveryTag = getChannel().getNextDeliveryTag();
- getProtocolSession().getProtocolOutputConverter().writeDeliver(entry.getMessage(), getChannel().getChannelId(), deliveryTag, getConumerTag());
+ sendToClient(entry, deliveryTag);
}
entry.dispose(storeContext);
@@ -166,12 +173,14 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage
static final class AckSubscription extends SubscriptionImpl
{
- public AckSubscription(int channelId, AMQProtocolSession protocolSession,
+ public AckSubscription(AMQChannel channel, AMQProtocolSession protocolSession,
AMQShortString consumerTag, FieldTable filters,
- boolean noLocal, FlowCreditManager creditManager)
+ boolean noLocal, FlowCreditManager creditManager,
+ ClientDeliveryMethod deliveryMethod,
+ RecordDeliveryMethod recordMethod)
throws AMQException
{
- super(channelId, protocolSession, consumerTag, filters, noLocal, creditManager);
+ super(channel, protocolSession, consumerTag, filters, noLocal, creditManager, deliveryMethod, recordMethod);
}
public boolean isBrowser()
@@ -189,7 +198,7 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage
*/
public void send(QueueEntry entry) throws AMQException
{
- StoreContext storeContext = getChannel().getStoreContext();
+
try
{ // if we do not need to wait for client acknowledgements
// we can decrement the reference count immediately.
@@ -206,12 +215,10 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage
long deliveryTag = getChannel().getNextDeliveryTag();
+ recordMessageDelivery(entry, deliveryTag);
+ sendToClient(entry, deliveryTag);
- getChannel().addUnacknowledgedMessage(entry, deliveryTag, this);
-
- getProtocolSession().getProtocolOutputConverter().writeDeliver(entry.getMessage(), getChannel().getChannelId(), deliveryTag, getConumerTag());
-
}
}
finally
@@ -225,7 +232,6 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage
}
-
}
@@ -252,16 +258,13 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage
- public SubscriptionImpl(int channelId, AMQProtocolSession protocolSession,
+ public SubscriptionImpl(AMQChannel channel , AMQProtocolSession protocolSession,
AMQShortString consumerTag, FieldTable arguments,
- boolean noLocal, FlowCreditManager creditManager)
+ boolean noLocal, FlowCreditManager creditManager,
+ ClientDeliveryMethod deliveryMethod,
+ RecordDeliveryMethod recordMethod)
throws AMQException
{
- AMQChannel channel = protocolSession.getChannel(channelId);
- if (channel == null)
- {
- throw new AMQException(AMQConstant.NOT_FOUND, "channel :" + channelId + " not found in protocol session");
- }
_channel = channel;
_consumerTag = consumerTag;
@@ -274,11 +277,12 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage
_filters = FilterManagerFactory.createManager(arguments);
+ _deliveryMethod = deliveryMethod;
+ _recordMethod = recordMethod;
-
- if (_filters != null)
+ if (arguments != null)
{
Object autoClose = arguments.get(AMQPFilterTypes.AUTO_CLOSE.getValue());
if (autoClose != null)
@@ -421,6 +425,12 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage
return _autoClose;
}
+ public FlowCreditManager getCreditManager()
+ {
+ return _creditManager;
+ }
+
+
public void close()
{
boolean closed = false;
@@ -484,7 +494,7 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage
return _channel;
}
- public AMQShortString getConumerTag()
+ public AMQShortString getConsumerTag()
{
return _consumerTag;
}
@@ -514,6 +524,11 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage
{
_stateListener.stateChange(this, State.SUSPENDED, State.ACTIVE);
}
+ else
+ {
+ // this is a hack to get round the issue of increasing bytes credit
+ _stateListener.stateChange(this, State.ACTIVE, State.ACTIVE);
+ }
}
else
{
@@ -536,19 +551,38 @@ public abstract class SubscriptionImpl implements Subscription, FlowCreditManage
}
- public Object getQueueContext()
+ public QueueEntry getLastSeenEntry()
{
return _queueContext.get();
}
- public boolean setQueueContext(Object expected, Object newvalue)
+ public boolean setLastSeenEntry(QueueEntry expected, QueueEntry newvalue)
{
return _queueContext.compareAndSet(expected,newvalue);
}
+ protected void sendToClient(final QueueEntry entry, final long deliveryTag)
+ throws AMQException
+ {
+ _deliveryMethod.deliverToClient(this,entry,deliveryTag);
+ }
+
+
+ protected void recordMessageDelivery(final QueueEntry entry, final long deliveryTag)
+ {
+ _recordMethod.recordMessageDelivery(this,entry,deliveryTag);
+ }
+
+
public boolean isActive()
{
return getState() == State.ACTIVE;
}
+
+ public QueueEntry.SubscriptionAcquiredState getOwningState()
+ {
+ return _owningState;
+ }
+
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java b/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java
index 3ff9b8c356..8b764efa42 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/virtualhost/VirtualHost.java
@@ -178,35 +178,35 @@ public class VirtualHost implements Accessable
private void initialiseHouseKeeping(final Configuration hostConfig)
{
- long period = hostConfig.getLong("housekeeping.expiredMessageCheckPeriod", DEFAULT_HOUSEKEEPING_PERIOD);
+ long period = hostConfig.getLong("housekeeping.expiredMessageCheckPeriod", DEFAULT_HOUSEKEEPING_PERIOD);
- /* add a timer task to iterate over queues, cleaning expired messages from queues with no consumers */
- if(period != 0L)
- {
- class RemoveExpiredMessagesTask extends TimerTask
- {
- public void run()
- {
- for(AMQQueue q : _queueRegistry.getQueues())
- {
-
- try
- {
- q.removeExpiredIfNoSubscribers();
- }
- catch (AMQException e)
- {
- _logger.error("Exception in housekeeping for queue: " + q.getName().toString(),e);
- throw new RuntimeException(e);
- }
- }
- }
- }
-
- _houseKeepingTimer.scheduleAtFixedRate(new RemoveExpiredMessagesTask(),
- period/2,
- period);
- }
+ /* add a timer task to iterate over queues, cleaning expired messages from queues with no consumers */
+ if(period != 0L)
+ {
+ class RemoveExpiredMessagesTask extends TimerTask
+ {
+ public void run()
+ {
+ for(AMQQueue q : _queueRegistry.getQueues())
+ {
+
+ try
+ {
+ q.removeExpiredIfNoSubscribers();
+ }
+ catch (AMQException e)
+ {
+ _logger.error("Exception in housekeeping for queue: " + q.getName().toString(),e);
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ }
+
+ _houseKeepingTimer.scheduleAtFixedRate(new RemoveExpiredMessagesTask(),
+ period/2,
+ period);
+ }
}
private void initialiseMessageStore(Configuration config) throws Exception
diff --git a/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/List.java b/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/List.java
index e6de3ab560..df8b59ec19 100644
--- a/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/List.java
+++ b/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/List.java
@@ -22,7 +22,6 @@ package org.apache.qpid.tools.messagestore.commands;
import org.apache.qpid.framing.AMQShortString;
import org.apache.qpid.server.exchange.Exchange;
-import org.apache.qpid.server.queue.AMQQueueImpl;
import org.apache.qpid.server.queue.AMQQueue;
import org.apache.qpid.server.registry.ApplicationRegistry;
import org.apache.qpid.server.virtualhost.VirtualHost;
diff --git a/java/broker/src/test/java/org/apache/qpid/server/exchange/DestWildExchangeTest.java b/java/broker/src/test/java/org/apache/qpid/server/exchange/DestWildExchangeTest.java
index 22b58b3aa4..3e8b1d0998 100644
--- a/java/broker/src/test/java/org/apache/qpid/server/exchange/DestWildExchangeTest.java
+++ b/java/broker/src/test/java/org/apache/qpid/server/exchange/DestWildExchangeTest.java
@@ -63,7 +63,7 @@ public class DestWildExchangeTest extends TestCase
public void testNoRoute() throws AMQException
{
- AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a*#b"), false, null, false, _vhost);
+ AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a*#b"), false, null, false, _vhost, null);
_exchange.registerQueue(new AMQShortString("a.*.#.b"), queue, null);
@@ -78,7 +78,7 @@ public class DestWildExchangeTest extends TestCase
public void testDirectMatch() throws AMQException
{
- AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("ab"), false, null, false, _vhost);
+ AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("ab"), false, null, false, _vhost, null);
_exchange.registerQueue(new AMQShortString("a.b"), queue, null);
@@ -118,7 +118,7 @@ public class DestWildExchangeTest extends TestCase
public void testStarMatch() throws AMQException
{
- AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a*"), false, null, false, _vhost);
+ AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a*"), false, null, false, _vhost, null);
_exchange.registerQueue(new AMQShortString("a.*"), queue, null);
@@ -176,7 +176,7 @@ public class DestWildExchangeTest extends TestCase
public void testHashMatch() throws AMQException
{
- AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a#"), false, null, false, _vhost);
+ AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a#"), false, null, false, _vhost, null);
_exchange.registerQueue(new AMQShortString("a.#"), queue, null);
@@ -272,7 +272,7 @@ public class DestWildExchangeTest extends TestCase
public void testMidHash() throws AMQException
{
- AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a"), false, null, false, _vhost);
+ AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a"), false, null, false, _vhost, null);
_exchange.registerQueue(new AMQShortString("a.*.#.b"), queue, null);
@@ -316,7 +316,7 @@ public class DestWildExchangeTest extends TestCase
public void testMatchafterHash() throws AMQException
{
- AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a#"), false, null, false, _vhost);
+ AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a#"), false, null, false, _vhost, null);
_exchange.registerQueue(new AMQShortString("a.*.#.b.c"), queue, null);
@@ -389,7 +389,7 @@ public class DestWildExchangeTest extends TestCase
public void testHashAfterHash() throws AMQException
{
- AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a#"), false, null, false, _vhost);
+ AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a#"), false, null, false, _vhost, null);
_exchange.registerQueue(new AMQShortString("a.*.#.b.c.#.d"), queue, null);
@@ -429,7 +429,7 @@ public class DestWildExchangeTest extends TestCase
public void testHashHash() throws AMQException
{
- AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a#"), false, null, false, _vhost);
+ AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a#"), false, null, false, _vhost, null);
_exchange.registerQueue(new AMQShortString("a.#.*.#.d"), queue, null);
@@ -468,7 +468,7 @@ public class DestWildExchangeTest extends TestCase
public void testSubMatchFails() throws AMQException
{
- AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a"), false, null, false, _vhost);
+ AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a"), false, null, false, _vhost, null);
_exchange.registerQueue(new AMQShortString("a.b.c.d"), queue, null);
@@ -497,7 +497,7 @@ public class DestWildExchangeTest extends TestCase
public void testMoreRouting() throws AMQException
{
- AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a"), false, null, false, _vhost);
+ AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a"), false, null, false, _vhost, null);
_exchange.registerQueue(new AMQShortString("a.b"), queue, null);
@@ -518,7 +518,7 @@ public class DestWildExchangeTest extends TestCase
public void testMoreQueue() throws AMQException
{
- AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a"), false, null, false, _vhost);
+ AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a"), false, null, false, _vhost, null);
_exchange.registerQueue(new AMQShortString("a.b"), queue, null);
diff --git a/java/broker/src/test/java/org/apache/qpid/server/exchange/ExchangeMBeanTest.java b/java/broker/src/test/java/org/apache/qpid/server/exchange/ExchangeMBeanTest.java
index 5184bf92e0..2a2bc72950 100644
--- a/java/broker/src/test/java/org/apache/qpid/server/exchange/ExchangeMBeanTest.java
+++ b/java/broker/src/test/java/org/apache/qpid/server/exchange/ExchangeMBeanTest.java
@@ -132,7 +132,8 @@ public class ExchangeMBeanTest extends TestCase
IApplicationRegistry applicationRegistry = ApplicationRegistry.getInstance();
_virtualHost = applicationRegistry.getVirtualHostRegistry().getVirtualHost("test");
_queueRegistry = _virtualHost.getQueueRegistry();
- _queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue"), false, new AMQShortString("ExchangeMBeanTest"), false, _virtualHost);
+ _queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue"), false, new AMQShortString("ExchangeMBeanTest"), false, _virtualHost,
+ null);
_queueRegistry.registerQueue(_queue);
}
}
diff --git a/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueAlertTest.java b/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueAlertTest.java
index fe8e3c8055..9924d1c770 100644
--- a/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueAlertTest.java
+++ b/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueAlertTest.java
@@ -72,7 +72,8 @@ public class AMQQueueAlertTest extends TestCase
public void testMessageCountAlert() throws Exception
{
_queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue1"), false, new AMQShortString("AMQueueAlertTest"),
- false, _virtualHost);
+ false, _virtualHost,
+ null);
_queueMBean = (AMQQueueMBean) _queue.getManagedObject();
_queueMBean.setMaximumMessageCount(MAX_MESSAGE_COUNT);
@@ -95,7 +96,8 @@ public class AMQQueueAlertTest extends TestCase
public void testMessageSizeAlert() throws Exception
{
_queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue2"), false, new AMQShortString("AMQueueAlertTest"),
- false, _virtualHost);
+ false, _virtualHost,
+ null);
_queueMBean = (AMQQueueMBean) _queue.getManagedObject();
_queueMBean.setMaximumMessageCount(MAX_MESSAGE_COUNT);
_queueMBean.setMaximumMessageSize(MAX_MESSAGE_SIZE);
@@ -120,7 +122,8 @@ public class AMQQueueAlertTest extends TestCase
public void testQueueDepthAlertNoSubscriber() throws Exception
{
_queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue3"), false, new AMQShortString("AMQueueAlertTest"),
- false, _virtualHost);
+ false, _virtualHost,
+ null);
_queueMBean = (AMQQueueMBean) _queue.getManagedObject();
_queueMBean.setMaximumMessageCount(MAX_MESSAGE_COUNT);
_queueMBean.setMaximumQueueDepth(MAX_QUEUE_DEPTH);
@@ -148,7 +151,8 @@ public class AMQQueueAlertTest extends TestCase
public void testMessageAgeAlert() throws Exception
{
_queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue4"), false, new AMQShortString("AMQueueAlertTest"),
- false, _virtualHost);
+ false, _virtualHost,
+ null);
_queueMBean = (AMQQueueMBean) _queue.getManagedObject();
_queueMBean.setMaximumMessageCount(MAX_MESSAGE_COUNT);
_queueMBean.setMaximumMessageAge(MAX_MESSAGE_AGE);
@@ -335,6 +339,6 @@ public class AMQQueueAlertTest extends TestCase
false,
new AMQShortString("AMQueueAlertTest"),
false,
- _virtualHost);
+ _virtualHost, null);
}
}
diff --git a/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueMBeanTest.java b/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueMBeanTest.java
index ef30fb9093..98f78e3d69 100644
--- a/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueMBeanTest.java
+++ b/java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueMBeanTest.java
@@ -306,7 +306,8 @@ public class AMQQueueMBeanTest extends TestCase
new LinkedList<RequiredDeliveryException>()
);
- _queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue"), false, new AMQShortString("AMQueueMBeanTest"), false, _virtualHost);
+ _queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue"), false, new AMQShortString("AMQueueMBeanTest"), false, _virtualHost,
+ null);
_queueMBean = new AMQQueueMBean(_queue);
_protocolSession = new TestMinaProtocolSession();
diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQConnection.java b/java/client/src/main/java/org/apache/qpid/client/AMQConnection.java
index 4b8143cfb5..60f57aaf0e 100644
--- a/java/client/src/main/java/org/apache/qpid/client/AMQConnection.java
+++ b/java/client/src/main/java/org/apache/qpid/client/AMQConnection.java
@@ -730,6 +730,8 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect
}
}
+
+
private void createChannelOverWire(int channelId, int prefetchHigh, int prefetchLow, boolean transacted)
throws AMQException, FailoverException
{
diff --git a/java/client/src/main/java/org/apache/qpid/client/AMQSession.java b/java/client/src/main/java/org/apache/qpid/client/AMQSession.java
index bba39403a5..82970e69e1 100644
--- a/java/client/src/main/java/org/apache/qpid/client/AMQSession.java
+++ b/java/client/src/main/java/org/apache/qpid/client/AMQSession.java
@@ -1110,11 +1110,18 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi
public void createQueue(final AMQShortString name, final boolean autoDelete, final boolean durable,
final boolean exclusive) throws AMQException
{
+ createQueue(name, autoDelete, durable, exclusive, null);
+ }
+
+ public void createQueue(final AMQShortString name, final boolean autoDelete, final boolean durable,
+ final boolean exclusive, final FieldTable arguments) throws AMQException
+ {
+
new FailoverRetrySupport<Object, AMQException>(new FailoverProtectedOperation<Object, AMQException>()
{
public Object execute() throws AMQException, FailoverException
{
- QueueDeclareBody body = getMethodRegistry().createQueueDeclareBody(getTicket(),name,false,durable,exclusive,autoDelete,false,null);
+ QueueDeclareBody body = getMethodRegistry().createQueueDeclareBody(getTicket(),name,false,durable,exclusive,autoDelete,false,arguments);
AMQFrame queueDeclare = body.generateFrame(_channelId);
@@ -2357,6 +2364,28 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi
+
+ public void setPrefecthLimits(final int messagePrefetch, final long sizePrefetch) throws AMQException
+ {
+ new FailoverRetrySupport<Object, AMQException>(
+ new FailoverProtectedOperation<Object, AMQException>()
+ {
+ public Object execute() throws AMQException, FailoverException
+ {
+
+ BasicQosBody basicQosBody = getProtocolHandler().getMethodRegistry().createBasicQosBody(sizePrefetch, messagePrefetch, false);
+
+ // todo send low water mark when protocol allows.
+ // todo Be aware of possible changes to parameter order as versions change.
+ getProtocolHandler().syncWrite(basicQosBody.generateFrame(getChannelId()), BasicQosOkBody.class);
+
+ return null;
+ }
+ }, _connection).execute();
+ }
+
+
+
/**
* Declares the named exchange and type of exchange.
*
diff --git a/java/client/src/main/java/org/apache/qpid/client/transport/TransportConnection.java b/java/client/src/main/java/org/apache/qpid/client/transport/TransportConnection.java
index 7ae2ddf66c..5907bd90af 100644
--- a/java/client/src/main/java/org/apache/qpid/client/transport/TransportConnection.java
+++ b/java/client/src/main/java/org/apache/qpid/client/transport/TransportConnection.java
@@ -174,7 +174,7 @@ public class TransportConnection
return -1;
}
- private static ITransportConnection getVMTransport(BrokerDetails details, boolean AutoCreate)
+ private synchronized static ITransportConnection getVMTransport(BrokerDetails details, boolean AutoCreate)
throws AMQVMBrokerCreationException
{
int port = details.getPort();
@@ -197,7 +197,7 @@ public class TransportConnection
return new VmPipeTransportConnection(port);
}
- public static void createVMBroker(int port) throws AMQVMBrokerCreationException
+ public static synchronized void createVMBroker(int port) throws AMQVMBrokerCreationException
{
if (_acceptor == null)
{
@@ -275,7 +275,7 @@ public class TransportConnection
}
}
- private static IoHandlerAdapter createBrokerInstance(int port) throws AMQVMBrokerCreationException
+ private static synchronized IoHandlerAdapter createBrokerInstance(int port) throws AMQVMBrokerCreationException
{
String protocolProviderClass = System.getProperty("amqj.protocolprovider.class", DEFAULT_QPID_SERVER);
_logger.info("Creating Qpid protocol provider: " + protocolProviderClass);
@@ -314,7 +314,7 @@ public class TransportConnection
return provider;
}
- public static void killAllVMBrokers()
+ public synchronized static void killAllVMBrokers()
{
_logger.info("Killing all VM Brokers");
if (_acceptor != null)
@@ -330,7 +330,7 @@ public class TransportConnection
_currentVMPort = -1;
}
- public static void killVMBroker(int port)
+ public synchronized static void killVMBroker(int port)
{
synchronized (_inVmPipeAddress)
{
diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/topic/TopicSessionTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/topic/TopicSessionTest.java
index 39730ef3ac..241af2c861 100644
--- a/java/client/src/test/java/org/apache/qpid/test/unit/topic/TopicSessionTest.java
+++ b/java/client/src/test/java/org/apache/qpid/test/unit/topic/TopicSessionTest.java
@@ -24,6 +24,7 @@ import javax.jms.*;
import javax.jms.MessageConsumer;
import javax.jms.Session;
import javax.jms.Message;
+import javax.jms.MessageProducer;
import junit.framework.TestCase;
@@ -32,12 +33,16 @@ import org.apache.qpid.client.AMQSession;
import org.apache.qpid.client.AMQTopic;
import org.apache.qpid.client.AMQQueue;
import org.apache.qpid.client.BasicMessageProducer;
+import org.apache.qpid.client.AMQDestination;
+import org.apache.qpid.client.AMQUndefinedDestination;
import org.apache.qpid.client.transport.TransportConnection;
import org.apache.qpid.url.URLSyntaxException;
import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
import org.apache.qpid.jms.*;
import java.util.UUID;
+import java.util.concurrent.atomic.AtomicInteger;
/** @author Apache Software Foundation */
@@ -144,7 +149,7 @@ public class TopicSessionTest extends TestCase
while(true)
{
publisher.publish(session1.createTextMessage("hello"));
- Thread.sleep(THREADS);
+ Thread.sleep(20);
}
}
catch(Exception e)
diff --git a/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketIoProcessor.java b/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketIoProcessor.java
index 03838ca3f1..fcaae7740b 100644
--- a/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketIoProcessor.java
+++ b/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketIoProcessor.java
@@ -766,7 +766,7 @@ class MultiThreadSocketIoProcessor extends SocketIoProcessor
session.increaseWrittenBytes(writtenBytes);
}
- if (buf.hasRemaining() || (totalFlushedBytes <= MAX_FLUSH_BYTES_PER_SESSION))
+ if (buf.hasRemaining())
{
// Kernel buffer is full
synchronized (writeLock)
diff --git a/java/common/src/main/java/org/apache/qpid/framing/AMQShortString.java b/java/common/src/main/java/org/apache/qpid/framing/AMQShortString.java
index ad2ab2ac0b..f1a7d4970a 100644
--- a/java/common/src/main/java/org/apache/qpid/framing/AMQShortString.java
+++ b/java/common/src/main/java/org/apache/qpid/framing/AMQShortString.java
@@ -469,6 +469,8 @@ public final class AMQShortString implements CharSequence, Comparable<AMQShortSt
return false;
}
+
+
return (_offset == 0 && otherString._offset == 0 && _length == _data.length && otherString._length == otherString._data.length && Arrays.equals(_data,otherString._data))
|| Arrays.equals(getBytes(),otherString.getBytes());
diff --git a/java/common/src/main/java/org/apache/qpid/pool/Job.java b/java/common/src/main/java/org/apache/qpid/pool/Job.java
index b2a09ac592..d65fce0fd0 100644
--- a/java/common/src/main/java/org/apache/qpid/pool/Job.java
+++ b/java/common/src/main/java/org/apache/qpid/pool/Job.java
@@ -67,18 +67,22 @@ public class Job implements Runnable
/** Holds the completion continuation, called upon completion of a run of the job. */
private final JobCompletionHandler _completionHandler;
+ private final boolean _readJob;
+
/**
* Creates a new job that aggregates many continuations together.
*
* @param session The Mina session.
* @param completionHandler The per job run, terminal continuation.
* @param maxEvents The maximum number of aggregated continuations to process per run of the job.
+ * @param readJob
*/
- Job(IoSession session, JobCompletionHandler completionHandler, int maxEvents)
+ Job(IoSession session, JobCompletionHandler completionHandler, int maxEvents, final boolean readJob)
{
_session = session;
_completionHandler = completionHandler;
_maxEvents = maxEvents;
+ _readJob = readJob;
}
/**
@@ -157,6 +161,11 @@ public class Job implements Runnable
}
}
+ public boolean isReadJob()
+ {
+ return _readJob;
+ }
+
/**
* Another interface for a continuation.
*
diff --git a/java/common/src/main/java/org/apache/qpid/pool/PoolingFilter.java b/java/common/src/main/java/org/apache/qpid/pool/PoolingFilter.java
index 8352b5af77..dd2d7038fd 100644
--- a/java/common/src/main/java/org/apache/qpid/pool/PoolingFilter.java
+++ b/java/common/src/main/java/org/apache/qpid/pool/PoolingFilter.java
@@ -59,24 +59,6 @@ import java.util.concurrent.ExecutorService;
* <td> {@link Job}, {@link Job.JobCompletionHandler}
* </table>
*
- * @todo This seems a bit bizarre. ReadWriteThreadModel creates seperate pooling filters for read and write events.
- * The pooling filters themselves batch read and write events into jobs, but hand these jobs to a common thread
- * pool for execution. So the same thread pool ends up handling read and write events, albeit with many threads
- * so there is concurrency. But why go to the trouble of seperating out the read and write events in that case?
- * Why not just batch them into jobs together? Perhaps its so that seperate thread pools could be used for these
- * stages.
- *
- * @todo Why set an event limit of 10 on the Job? This also seems bizarre, as the job can have more than 10 events in
- * it. Its just that it runs them 10 at a time, but the completion hander here checks if there are more to run
- * and trips off another batch of 10 until they are all done. Why not just have a straight forward
- * consumer/producer queue scenario without the batches of 10? So instead of having many jobs with batches of 10
- * in them, just have one queue of events and worker threads taking the next event. There will be coordination
- * between worker threads and new events arriving on the job anyway, so the simpler scenario may have the same
- * amount of contention. I can see that the batches of 10 is done, so that no job is allowed to hog the worker
- * pool for too long. I'm not convinced this fairly complex scheme will actually add anything, and it might be
- * better to encapsulate it under a Queue interface anyway, so that different queue implementations can easily
- * be substituted in.
- *
* @todo The static helper methods are pointless. Could just call new.
*/
public abstract class PoolingFilter extends IoFilterAdapter implements Job.JobCompletionHandler
@@ -95,17 +77,20 @@ public abstract class PoolingFilter extends IoFilterAdapter implements Job.JobCo
private final int _maxEvents;
+ private final boolean _readFilter;
+
/**
* Creates a named pooling filter, on the specified shared thread pool.
*
* @param refCountingPool The thread pool reference.
* @param name The identifying name of the filter type.
*/
- public PoolingFilter(ReferenceCountingExecutorService refCountingPool, String name, int maxEvents)
+ public PoolingFilter(ReferenceCountingExecutorService refCountingPool, String name, int maxEvents, boolean readFilter)
{
_poolReference = refCountingPool;
_name = name;
_maxEvents = maxEvents;
+ _readFilter = readFilter;
}
/**
@@ -166,7 +151,6 @@ public abstract class PoolingFilter extends IoFilterAdapter implements Job.JobCo
void fireAsynchEvent(Job job, Event event)
{
- // job.acquire(); //prevents this job being removed from _jobs
job.add(event);
final ExecutorService pool = _poolReference.getPool();
@@ -200,7 +184,7 @@ public abstract class PoolingFilter extends IoFilterAdapter implements Job.JobCo
*/
public void createNewJobForSession(IoSession session)
{
- Job job = new Job(session, this, MAX_JOB_EVENTS);
+ Job job = new Job(session, this, MAX_JOB_EVENTS,_readFilter);
session.setAttribute(_name, job);
}
@@ -216,18 +200,6 @@ public abstract class PoolingFilter extends IoFilterAdapter implements Job.JobCo
return (Job) session.getAttribute(_name);
}
- /*private Job createJobForSession(IoSession session)
- {
- return addJobForSession(session, new Job(session, this, _maxEvents));
- }*/
-
- /*private Job addJobForSession(IoSession session, Job job)
- {
- // atomic so ensures all threads agree on the same job
- Job existing = _jobs.putIfAbsent(session, job);
-
- return (existing == null) ? job : existing;
- }*/
/**
* Implements a terminal continuation for the {@link Job} for this filter. Whenever the Job completes its processing
@@ -238,16 +210,6 @@ public abstract class PoolingFilter extends IoFilterAdapter implements Job.JobCo
*/
public void completed(IoSession session, Job job)
{
- // if (job.isComplete())
- // {
- // job.release();
- // if (!job.isReferenced())
- // {
- // _jobs.remove(session);
- // }
- // }
- // else
-
if (!job.isComplete())
{
@@ -454,7 +416,7 @@ public abstract class PoolingFilter extends IoFilterAdapter implements Job.JobCo
*/
public AsynchReadPoolingFilter(ReferenceCountingExecutorService refCountingPool, String name)
{
- super(refCountingPool, name, Integer.getInteger("amqj.server.read_write_pool.max_read_events", MAX_JOB_EVENTS));
+ super(refCountingPool, name, Integer.getInteger("amqj.server.read_write_pool.max_read_events", MAX_JOB_EVENTS),true);
}
/**
@@ -497,7 +459,7 @@ public abstract class PoolingFilter extends IoFilterAdapter implements Job.JobCo
*/
public AsynchWritePoolingFilter(ReferenceCountingExecutorService refCountingPool, String name)
{
- super(refCountingPool, name, Integer.getInteger("amqj.server.read_write_pool.max_write_events", MAX_JOB_EVENTS));
+ super(refCountingPool, name, Integer.getInteger("amqj.server.read_write_pool.max_write_events", MAX_JOB_EVENTS),false);
}
/**
diff --git a/java/common/src/main/java/org/apache/qpid/pool/ReadWriteJobQueue.java b/java/common/src/main/java/org/apache/qpid/pool/ReadWriteJobQueue.java
new file mode 100644
index 0000000000..05141aea7b
--- /dev/null
+++ b/java/common/src/main/java/org/apache/qpid/pool/ReadWriteJobQueue.java
@@ -0,0 +1,432 @@
+package org.apache.qpid.pool;
+
+import java.util.AbstractQueue;
+import java.util.Iterator;
+import java.util.Collection;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/*
+*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*
+*/
+public class ReadWriteJobQueue extends AbstractQueue<Runnable> implements BlockingQueue<Runnable>
+{
+
+ private final AtomicInteger _count = new AtomicInteger(0);
+
+ private final ReentrantLock _takeLock = new ReentrantLock();
+
+ private final Condition _notEmpty = _takeLock.newCondition();
+
+ private final ReentrantLock _putLock = new ReentrantLock();
+
+ private final ConcurrentLinkedQueue<Job> _readJobQueue = new ConcurrentLinkedQueue<Job>();
+
+ private final ConcurrentLinkedQueue<Job> _writeJobQueue = new ConcurrentLinkedQueue<Job>();
+
+
+ private class ReadWriteJobIterator implements Iterator<Runnable>
+ {
+
+ private boolean _onReads;
+ private Iterator<Job> _iter = _writeJobQueue.iterator();
+
+ public boolean hasNext()
+ {
+ if(!_iter.hasNext())
+ {
+ if(_onReads)
+ {
+ _iter = _readJobQueue.iterator();
+ _onReads = true;
+ return _iter.hasNext();
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else
+ {
+ return true;
+ }
+ }
+
+ public Runnable next()
+ {
+ if(_iter.hasNext())
+ {
+ return _iter.next();
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ public void remove()
+ {
+ _takeLock.lock();
+ try
+ {
+ _iter.remove();
+ _count.decrementAndGet();
+ }
+ finally
+ {
+ _takeLock.unlock();
+ }
+ }
+ }
+
+ public Iterator<Runnable> iterator()
+ {
+ return new ReadWriteJobIterator();
+ }
+
+ public int size()
+ {
+ return _count.get();
+ }
+
+ public boolean offer(final Runnable runnable)
+ {
+ final Job job = (Job) runnable;
+ final ReentrantLock putLock = _putLock;
+ putLock.lock();
+ try
+ {
+ if(job.isReadJob())
+ {
+ _readJobQueue.offer(job);
+ }
+ else
+ {
+ _writeJobQueue.offer(job);
+ }
+ if(_count.getAndIncrement() == 0)
+ {
+ _takeLock.lock();
+ try
+ {
+ _notEmpty.signal();
+ }
+ finally
+ {
+ _takeLock.unlock();
+ }
+ }
+ return true;
+ }
+ finally
+ {
+ putLock.unlock();
+ }
+ }
+
+ public void put(final Runnable runnable) throws InterruptedException
+ {
+ final Job job = (Job) runnable;
+ final ReentrantLock putLock = _putLock;
+ putLock.lock();
+
+ try
+ {
+ if(job.isReadJob())
+ {
+ _readJobQueue.offer(job);
+ }
+ else
+ {
+ _writeJobQueue.offer(job);
+ }
+ if(_count.getAndIncrement() == 0)
+ {
+ _takeLock.lock();
+ try
+ {
+ _notEmpty.signal();
+ }
+ finally
+ {
+ _takeLock.unlock();
+ }
+ }
+
+ }
+ finally
+ {
+ putLock.unlock();
+ }
+ }
+
+
+
+ public boolean offer(final Runnable runnable, final long timeout, final TimeUnit unit) throws InterruptedException
+ {
+ final Job job = (Job) runnable;
+ final ReentrantLock putLock = _putLock;
+ putLock.lock();
+
+ try
+ {
+ if(job.isReadJob())
+ {
+ _readJobQueue.offer(job);
+ }
+ else
+ {
+ _writeJobQueue.offer(job);
+ }
+ if(_count.getAndIncrement() == 0)
+ {
+ _takeLock.lock();
+ try
+ {
+ _notEmpty.signal();
+ }
+ finally
+ {
+ _takeLock.unlock();
+ }
+ }
+
+ return true;
+ }
+ finally
+ {
+ putLock.unlock();
+ }
+
+ }
+
+ public Runnable take() throws InterruptedException
+ {
+ final ReentrantLock takeLock = _takeLock;
+ takeLock.lockInterruptibly();
+ try
+ {
+ try
+ {
+ while (_count.get() == 0)
+ {
+ _notEmpty.await();
+ }
+ }
+ catch (InterruptedException ie)
+ {
+ _notEmpty.signal();
+ throw ie;
+ }
+
+ Job job = _writeJobQueue.poll();
+ if(job == null)
+ {
+ job = _readJobQueue.poll();
+ }
+ int c = _count.getAndDecrement();
+ if (c > 1)
+ {
+ _notEmpty.signal();
+ }
+ return job;
+ }
+ finally
+ {
+ takeLock.unlock();
+ }
+
+
+ }
+
+ public Runnable poll(final long timeout, final TimeUnit unit) throws InterruptedException
+ {
+ final ReentrantLock takeLock = _takeLock;
+ final AtomicInteger count = _count;
+ long nanos = unit.toNanos(timeout);
+ takeLock.lockInterruptibly();
+ Job job = null;
+ try
+ {
+
+ for (;;)
+ {
+ if (count.get() > 0)
+ {
+ job = _writeJobQueue.poll();
+ if(job == null)
+ {
+ job = _readJobQueue.poll();
+ }
+ int c = count.getAndDecrement();
+ if (c > 1)
+ {
+ _notEmpty.signal();
+ }
+ break;
+ }
+ if (nanos <= 0)
+ {
+ return null;
+ }
+ try
+ {
+ nanos = _notEmpty.awaitNanos(nanos);
+ }
+ catch (InterruptedException ie)
+ {
+ _notEmpty.signal();
+ throw ie;
+ }
+ }
+ }
+ finally
+ {
+ takeLock.unlock();
+ }
+
+ return job;
+ }
+
+ public int remainingCapacity()
+ {
+ return Integer.MAX_VALUE;
+ }
+
+ public int drainTo(final Collection<? super Runnable> c)
+ {
+ int total = 0;
+
+ _putLock.lock();
+ _takeLock.lock();
+ try
+ {
+ Job job;
+ while((job = _writeJobQueue.peek())!= null)
+ {
+ c.add(job);
+ _writeJobQueue.poll();
+ _count.decrementAndGet();
+ total++;
+ }
+
+ while((job = _readJobQueue.peek())!= null)
+ {
+ c.add(job);
+ _readJobQueue.poll();
+ _count.decrementAndGet();
+ total++;
+ }
+
+ }
+ finally
+ {
+ _takeLock.unlock();
+ _putLock.unlock();
+ }
+ return total;
+ }
+
+ public int drainTo(final Collection<? super Runnable> c, final int maxElements)
+ {
+ int total = 0;
+
+ _putLock.lock();
+ _takeLock.lock();
+ try
+ {
+ Job job;
+ while(total<=maxElements && (job = _writeJobQueue.peek())!= null)
+ {
+ c.add(job);
+ _writeJobQueue.poll();
+ _count.decrementAndGet();
+ total++;
+ }
+
+ while(total<=maxElements && (job = _readJobQueue.peek())!= null)
+ {
+ c.add(job);
+ _readJobQueue.poll();
+ _count.decrementAndGet();
+ total++;
+ }
+
+ }
+ finally
+ {
+ _takeLock.unlock();
+ _putLock.unlock();
+ }
+ return total;
+
+ }
+
+ public Runnable poll()
+ {
+ final ReentrantLock takeLock = _takeLock;
+ takeLock.lock();
+ try
+ {
+ if(_count.get() > 0)
+ {
+ Job job = _writeJobQueue.poll();
+ if(job == null)
+ {
+ job = _readJobQueue.poll();
+ }
+ _count.decrementAndGet();
+ return job;
+ }
+ else
+ {
+ return null;
+ }
+ }
+ finally
+ {
+ takeLock.unlock();
+ }
+
+ }
+
+ public Runnable peek()
+ {
+ final ReentrantLock takeLock = _takeLock;
+ takeLock.lock();
+ try
+ {
+ Job job = _writeJobQueue.peek();
+ if(job == null)
+ {
+ job = _readJobQueue.peek();
+ }
+ return job;
+ }
+ finally
+ {
+ takeLock.unlock();
+ }
+ }
+}
diff --git a/java/common/src/main/java/org/apache/qpid/pool/ReferenceCountingExecutorService.java b/java/common/src/main/java/org/apache/qpid/pool/ReferenceCountingExecutorService.java
index 84c9e1f465..1359e56958 100644
--- a/java/common/src/main/java/org/apache/qpid/pool/ReferenceCountingExecutorService.java
+++ b/java/common/src/main/java/org/apache/qpid/pool/ReferenceCountingExecutorService.java
@@ -22,6 +22,9 @@ package org.apache.qpid.pool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.LinkedBlockingQueue;
/**
* ReferenceCountingExecutorService wraps an ExecutorService in order to provide shared reference to it. It counts
@@ -111,7 +114,12 @@ public class ReferenceCountingExecutorService
{
if (_refCount++ == 0)
{
- _pool = Executors.newFixedThreadPool(_poolSize);
+// _pool = Executors.newFixedThreadPool(_poolSize);
+
+ // Use a job queue that biases to writes
+ _pool = new ThreadPoolExecutor(_poolSize, _poolSize,
+ 0L, TimeUnit.MILLISECONDS,
+ new ReadWriteJobQueue());
}
return _pool;
diff --git a/java/management/eclipse-plugin/src/test/java/org/apache/qpid/management/ui/ManagementConsoleTest.java b/java/management/eclipse-plugin/src/test/java/org/apache/qpid/management/ui/ManagementConsoleTest.java
index 6aec1cb164..fb616e2b59 100644
--- a/java/management/eclipse-plugin/src/test/java/org/apache/qpid/management/ui/ManagementConsoleTest.java
+++ b/java/management/eclipse-plugin/src/test/java/org/apache/qpid/management/ui/ManagementConsoleTest.java
@@ -63,7 +63,8 @@ public class ManagementConsoleTest extends TestCase
{
// If this test fails due to changes in the broker code,
// then the constants in the Constants.java shoule be updated accordingly
- AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueueForManagement"), false, null, false, _virtualHost);
+ AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueueForManagement"), false, null, false, _virtualHost,
+ null);
AMQManagedObject mbean = new AMQQueueMBean(queue);
MBeanInfo mbeanInfo = mbean.getMBeanInfo();
diff --git a/java/plugins/src/main/java/org/apache/qpid/extras/exchanges/diagnostic/DiagnosticExchange.java b/java/plugins/src/main/java/org/apache/qpid/extras/exchanges/diagnostic/DiagnosticExchange.java
index 600ae3f7be..779d47be1c 100644
--- a/java/plugins/src/main/java/org/apache/qpid/extras/exchanges/diagnostic/DiagnosticExchange.java
+++ b/java/plugins/src/main/java/org/apache/qpid/extras/exchanges/diagnostic/DiagnosticExchange.java
@@ -34,7 +34,6 @@ import org.apache.qpid.framing.FieldTable;
import org.apache.qpid.server.exchange.AbstractExchange;
import org.apache.qpid.server.management.MBeanConstructor;
import org.apache.qpid.server.management.MBeanDescription;
-import org.apache.qpid.server.queue.AMQQueueImpl;
import org.apache.qpid.server.queue.IncomingMessage;
import org.apache.qpid.server.queue.AMQQueue;
@@ -207,12 +206,7 @@ public class DiagnosticExchange extends AbstractExchange
}
- @Override
- public Map<AMQShortString, List<AMQQueue>> getBindings() {
- // TODO Auto-generated method stub
- return null;
- }
-
+
public boolean isBound(AMQShortString routingKey, FieldTable arguments,
AMQQueue queue) {
// TODO Auto-generated method stub
diff --git a/java/plugins/src/main/java/org/apache/qpid/extras/exchanges/example/TestExchange.java b/java/plugins/src/main/java/org/apache/qpid/extras/exchanges/example/TestExchange.java
index 7589a7974b..067125de56 100644
--- a/java/plugins/src/main/java/org/apache/qpid/extras/exchanges/example/TestExchange.java
+++ b/java/plugins/src/main/java/org/apache/qpid/extras/exchanges/example/TestExchange.java
@@ -7,7 +7,6 @@ import org.apache.qpid.AMQException;
import org.apache.qpid.framing.AMQShortString;
import org.apache.qpid.framing.FieldTable;
import org.apache.qpid.server.exchange.Exchange;
-import org.apache.qpid.server.queue.AMQQueueImpl;
import org.apache.qpid.server.queue.IncomingMessage;
import org.apache.qpid.server.queue.AMQQueue;
import org.apache.qpid.server.virtualhost.VirtualHost;
diff --git a/java/systests/pom.xml b/java/systests/pom.xml
index e6c1fa092e..f25410c2d2 100644
--- a/java/systests/pom.xml
+++ b/java/systests/pom.xml
@@ -151,7 +151,6 @@
<MessageReturnTest>-n MessageReturnTest org.apache.qpid.server.queue.MessageReturnTest </MessageReturnTest>
<QueueDepthWithSelectorTest>-n QueueDepthWithSelectorTest org.apache.qpid.server.queue.QueueDepthWithSelectorTest </QueueDepthWithSelectorTest>
<!--<SubscriptionManagerTest>-n SubscriptionManagerTest org.apache.qpid.server.queue.SubscriptionManagerTest </SubscriptionManagerTest>-->
- <SubscriptionSetTest>-n SubscriptionSetTest org.apache.qpid.server.queue.SubscriptionSetTest </SubscriptionSetTest>
<TimeToLiveTest>-n TimeToLiveTest org.apache.qpid.server.queue.TimeToLiveTest </TimeToLiveTest>
<TxnBufferTest>-n TxnBufferTest org.apache.qpid.server.txn.TxnBufferTest </TxnBufferTest>
<!--<TxnTest>-n TxnTest org.apache.qpid.server.txn.TxnTest </TxnTest>-->
diff --git a/java/systests/src/main/java/org/apache/qpid/server/ack/TxAckTest.java b/java/systests/src/main/java/org/apache/qpid/server/ack/TxAckTest.java
index 144e4be6af..4e39367da1 100644
--- a/java/systests/src/main/java/org/apache/qpid/server/ack/TxAckTest.java
+++ b/java/systests/src/main/java/org/apache/qpid/server/ack/TxAckTest.java
@@ -26,13 +26,16 @@ import org.apache.qpid.framing.AMQShortString;
import org.apache.qpid.framing.ContentHeaderBody;
import org.apache.qpid.framing.abstraction.MessagePublishInfo;
import org.apache.qpid.server.RequiredDeliveryException;
+import org.apache.qpid.server.virtualhost.VirtualHost;
import org.apache.qpid.server.queue.AMQMessage;
-import org.apache.qpid.server.queue.QueueEntryImpl;
import org.apache.qpid.server.queue.MessageHandleFactory;
import org.apache.qpid.server.queue.QueueEntry;
import org.apache.qpid.server.queue.AMQMessageHandle;
+import org.apache.qpid.server.queue.AMQQueueFactory;
+import org.apache.qpid.server.queue.AMQQueue;
import org.apache.qpid.server.store.TestableMemoryMessageStore;
import org.apache.qpid.server.store.StoreContext;
+import org.apache.qpid.server.store.MemoryMessageStore;
import org.apache.qpid.server.txn.NonTransactionalContext;
import org.apache.qpid.server.txn.TransactionalContext;
@@ -100,12 +103,16 @@ public class TxAckTest extends TestCase
private final List<Long> _unacked;
private StoreContext _storeContext = new StoreContext();
- Scenario(int messageCount, List<Long> acked, List<Long> unacked) throws AMQException
+ Scenario(int messageCount, List<Long> acked, List<Long> unacked) throws Exception
{
TransactionalContext txnContext = new NonTransactionalContext(new TestableMemoryMessageStore(),
_storeContext, null,
new LinkedList<RequiredDeliveryException>()
);
+ AMQQueue queue =
+ AMQQueueFactory.createAMQQueueImpl(new AMQShortString("test"), false, null, false, new VirtualHost("", new MemoryMessageStore()),
+ null);
+
for (int i = 0; i < messageCount; i++)
{
long deliveryTag = i + 1;
@@ -140,7 +147,7 @@ public class TxAckTest extends TestCase
};
TestMessage message = new TestMessage(deliveryTag, i, info, txnContext.getStoreContext());
- _map.add(deliveryTag, new QueueEntryImpl(null,message, Long.MIN_VALUE));
+ _map.add(deliveryTag, queue.enqueue(new StoreContext(), message));
}
_acked = acked;
_unacked = unacked;
diff --git a/java/systests/src/main/java/org/apache/qpid/server/exchange/AbstractHeadersExchangeTestBase.java b/java/systests/src/main/java/org/apache/qpid/server/exchange/AbstractHeadersExchangeTestBase.java
index 80470d44b3..74d7b39b72 100644
--- a/java/systests/src/main/java/org/apache/qpid/server/exchange/AbstractHeadersExchangeTestBase.java
+++ b/java/systests/src/main/java/org/apache/qpid/server/exchange/AbstractHeadersExchangeTestBase.java
@@ -33,6 +33,7 @@ import org.apache.qpid.server.store.StoreContext;
import org.apache.qpid.server.txn.NonTransactionalContext;
import org.apache.qpid.server.txn.TransactionalContext;
import org.apache.qpid.server.RequiredDeliveryException;
+import org.apache.qpid.server.subscription.Subscription;
import org.apache.qpid.server.protocol.AMQProtocolSession;
import org.apache.log4j.Logger;
@@ -236,7 +237,7 @@ public class AbstractHeadersExchangeTestBase extends TestCase
return properties;
}
- static class TestQueue extends AMQQueueImpl
+ static class TestQueue extends SimpleAMQQueue
{
final List<HeadersExchangeTest.Message> messages = new ArrayList<HeadersExchangeTest.Message>();
@@ -250,12 +251,160 @@ public class AbstractHeadersExchangeTestBase extends TestCase
* not invoked. It is unnecessary since for this test we only care to know whether the message was
* sent to the queue; the queue processing logic is not being tested.
* @param msg
- * @param deliverFirst
* @throws AMQException
*/
- public void process(StoreContext context, QueueEntry msg, boolean deliverFirst) throws AMQException
+ @Override
+ public QueueEntry enqueue(StoreContext context, AMQMessage msg) throws AMQException
{
- messages.add( new HeadersExchangeTest.Message(msg.getMessage()));
+ messages.add( new HeadersExchangeTest.Message(msg));
+ return new QueueEntry()
+ {
+
+ public AMQQueue getQueue()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public AMQMessage getMessage()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public long getSize()
+ {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean getDeliveredToConsumer()
+ {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean expired() throws AMQException
+ {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean isAcquired()
+ {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean acquire()
+ {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean acquire(Subscription sub)
+ {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean delete()
+ {
+ return false;
+ }
+
+ public boolean isDeleted()
+ {
+ return false;
+ }
+
+ public boolean acquiredBySubscription()
+ {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void setDeliveredToSubscription()
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void release()
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public String debugIdentity()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean immediateAndNotDelivered()
+ {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void setRedelivered(boolean b)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public Subscription getDeliveredSubscription()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void reject()
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void reject(Subscription subscription)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean isRejectedBy(Subscription subscription)
+ {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void requeue(StoreContext storeContext) throws AMQException
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void dequeue(final StoreContext storeContext) throws FailedDequeueException
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void dispose(final StoreContext storeContext) throws MessageCleanupException
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void restoreCredit()
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void discard(StoreContext storeContext) throws AMQException
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean isQueueDeleted()
+ {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void addStateChangeListener(StateChangeListener listener)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean removeStateChangeListener(StateChangeListener listener)
+ {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public int compareTo(final QueueEntry o)
+ {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+ };
}
boolean isInQueue(Message msg)
diff --git a/java/systests/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBeanTest.java b/java/systests/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBeanTest.java
index 6f8963131b..8e7038eec3 100644
--- a/java/systests/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBeanTest.java
+++ b/java/systests/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBeanTest.java
@@ -59,7 +59,7 @@ public class AMQProtocolSessionMBeanTest extends TestCase
false,
new AMQShortString("test"),
true,
- _protocolSession.getVirtualHost());
+ _protocolSession.getVirtualHost(), null);
AMQChannel channel = new AMQChannel(_protocolSession,2, _messageStore);
channel.setDefaultQueue(queue);
_protocolSession.addChannel(channel);
diff --git a/java/systests/src/main/java/org/apache/qpid/server/queue/AckTest.java b/java/systests/src/main/java/org/apache/qpid/server/queue/AckTest.java
index 30240217a2..dae81a875d 100644
--- a/java/systests/src/main/java/org/apache/qpid/server/queue/AckTest.java
+++ b/java/systests/src/main/java/org/apache/qpid/server/queue/AckTest.java
@@ -78,7 +78,8 @@ public class AckTest extends TestCase
_protocolSession.addChannel(_channel);
- _queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("myQ"), false, new AMQShortString("guest"), true, ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHost("test"));
+ _queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("myQ"), false, new AMQShortString("guest"), true, ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHost("test"),
+ null);
}
diff --git a/java/systests/src/main/java/org/apache/qpid/server/queue/ConcurrencyTestDisabled.java b/java/systests/src/main/java/org/apache/qpid/server/queue/ConcurrencyTestDisabled.java
deleted file mode 100644
index 3ff691c792..0000000000
--- a/java/systests/src/main/java/org/apache/qpid/server/queue/ConcurrencyTestDisabled.java
+++ /dev/null
@@ -1,267 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.queue;
-
-import org.apache.qpid.AMQException;
-import org.apache.qpid.framing.AMQShortString;
-import org.apache.qpid.server.handler.OnCurrentThreadExecutor;
-import org.apache.qpid.server.registry.IApplicationRegistry;
-import org.apache.qpid.server.registry.ApplicationRegistry;
-import org.apache.qpid.server.virtualhost.VirtualHost;
-import org.apache.qpid.server.subscription.Subscription;
-
-import java.util.*;
-import java.util.concurrent.Executor;
-
-/**
- * Tests delivery in the face of concurrent incoming _messages, subscription alterations
- * and attempts to asynchronously process queued _messages.
- */
-public class ConcurrencyTestDisabled extends MessageTestHelper
-{
- private final Random random = new Random();
-
- private final int numMessages = 1000;
-
- private final List<SubscriptionTestHelper> _subscribers = new ArrayList<SubscriptionTestHelper>();
- private final Set<Subscription> _active = new HashSet<Subscription>();
- private final List<QueueEntry> _messages = new ArrayList<QueueEntry>();
- private int next = 0;//index to next message to send
- private final List<QueueEntry> _received = Collections.synchronizedList(new ArrayList<QueueEntry>());
- private final Executor _executor = new OnCurrentThreadExecutor();
- private final List<Thread> _threads = new ArrayList<Thread>();
-
- private final SubscriptionSet _subscriptionMgr;
- private final ConcurrentSelectorDeliveryManager _deliveryMgr;
-
- private boolean isComplete;
- private boolean failed;
- private VirtualHost _virtualHost;
-
- public ConcurrencyTestDisabled() throws Exception
- {
-
- IApplicationRegistry applicationRegistry = ApplicationRegistry.getInstance();
- _virtualHost = applicationRegistry.getVirtualHostRegistry().getVirtualHost("test");
- _deliveryMgr = new ConcurrentSelectorDeliveryManager( new AMQQueueImpl(new AMQShortString("myQ"), false, new AMQShortString("guest"), false,
- _virtualHost));
- _subscriptionMgr = _deliveryMgr.getSubscribers();
- }
-
- public void testConcurrent1() throws InterruptedException, AMQException
- {
- initSubscriptions(10);
- initMessages(numMessages);
- initThreads(1, 4, 4, 4);
- doRun();
- check();
- }
-
- public void testConcurrent2() throws InterruptedException, AMQException
- {
- initSubscriptions(10);
- initMessages(numMessages);
- initThreads(4, 2, 2, 2);
- doRun();
- check();
- }
-
- void check()
- {
- assertFalse("Failed", failed);
-
- _deliveryMgr.processAsync(_executor);
-
- assertEquals("Did not recieve the correct number of messages", _messages.size(), _received.size());
- for(int i = 0; i < _messages.size(); i++)
- {
- assertEquals("Wrong message at " + i, _messages.get(i), _received.get(i));
- }
- }
-
- void initSubscriptions(int subscriptions)
- {
- for(int i = 0; i < subscriptions; i++)
- {
- _subscribers.add(new SubscriptionTestHelper("Subscriber" + i, _received));
- }
- }
-
- void initMessages(int messages) throws AMQException
- {
- for(int i = 0; i < messages; i++)
- {
- _messages.add(message());
- }
- }
-
- void initThreads(int senders, int subscribers, int suspenders, int processors)
- {
- addThreads(senders, senders == 1 ? new Sender() : new OrderedSender());
- addThreads(subscribers, new Subscriber());
- addThreads(suspenders, new Suspender());
- addThreads(processors, new Processor());
- }
-
- void addThreads(int count, Runnable runner)
- {
- for(int i = 0; i < count; i++)
- {
- _threads.add(new Thread(runner, runner.toString()));
- }
- }
-
- void doRun() throws InterruptedException
- {
- for(Thread t : _threads)
- {
- t.start();
- }
-
- for(Thread t : _threads)
- {
- t.join();
- }
- }
-
- private void toggle(Subscription s)
- {
- synchronized (_active)
- {
- if (_active.contains(s))
- {
- _active.remove(s);
- Subscription result = _subscriptionMgr.removeSubscriber(s);
- assertTrue("Removed subscription " + result + " but trying to remove subscription " + s,
- result != null && result.equals(s));
- }
- else
- {
- _active.add(s);
- _subscriptionMgr.addSubscriber(s);
- }
- }
- }
-
- private QueueEntry nextMessage()
- {
- synchronized (_messages)
- {
- if (next < _messages.size())
- {
- return _messages.get(next++);
- }
- else
- {
- if (!_deliveryMgr.hasQueuedMessages()) {
- isComplete = true;
- }
- return null;
- }
- }
- }
-
- private boolean randomBoolean()
- {
- return random.nextBoolean();
- }
-
- private SubscriptionTestHelper randomSubscriber()
- {
- return _subscribers.get(random.nextInt(_subscribers.size()));
- }
-
- private class Sender extends Runner
- {
- void doRun() throws Throwable
- {
- QueueEntry msg = nextMessage();
- if (msg != null)
- {
- _deliveryMgr.deliver(null, new AMQShortString(toString()), msg, false);
- }
- }
- }
-
- private class OrderedSender extends Sender
- {
- synchronized void doRun() throws Throwable
- {
- super.doRun();
- }
- }
-
- private class Suspender extends Runner
- {
- void doRun() throws Throwable
- {
- randomSubscriber().setSuspended(randomBoolean());
- }
- }
-
- private class Subscriber extends Runner
- {
- void doRun() throws Throwable
- {
- toggle(randomSubscriber());
- }
- }
-
- private class Processor extends Runner
- {
- void doRun() throws Throwable
- {
- _deliveryMgr.processAsync(_executor);
- }
- }
-
- private abstract class Runner implements Runnable
- {
- public void run()
- {
- try
- {
- while (!stop())
- {
- doRun();
- }
- }
- catch (Throwable t)
- {
- failed = true;
- t.printStackTrace();
- }
- }
-
- abstract void doRun() throws Throwable;
-
- boolean stop()
- {
- return isComplete || failed;
- }
- }
-
- public static junit.framework.Test suite()
- {
- return new junit.framework.TestSuite(ConcurrencyTestDisabled.class);
- }
-
-}
diff --git a/java/systests/src/main/java/org/apache/qpid/server/queue/PriorityTest.java b/java/systests/src/main/java/org/apache/qpid/server/queue/PriorityTest.java
new file mode 100644
index 0000000000..3d53b87b45
--- /dev/null
+++ b/java/systests/src/main/java/org/apache/qpid/server/queue/PriorityTest.java
@@ -0,0 +1,171 @@
+/*
+*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*
+*/
+package org.apache.qpid.server.queue;
+
+import junit.framework.TestCase;
+import junit.framework.Assert;
+import org.apache.log4j.Logger;
+import org.apache.qpid.client.transport.TransportConnection;
+import org.apache.qpid.client.AMQConnection;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.client.AMQDestination;
+import org.apache.qpid.jndi.PropertiesFileInitialContextFactory;
+import org.apache.qpid.url.URLSyntaxException;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.FieldTable;
+
+import javax.jms.*;
+import javax.naming.NamingException;
+import javax.naming.Context;
+import javax.naming.spi.InitialContextFactory;
+import java.util.Hashtable;
+
+/** Test Case provided by client Non-functional Test NF101: heap exhaustion behaviour */
+public class PriorityTest extends TestCase
+{
+ private static final Logger _logger = Logger.getLogger(PriorityTest.class);
+
+
+ protected final String BROKER = "vm://:1";
+ protected final String VHOST = "/test";
+ protected final String QUEUE = "PriorityQueue";
+
+
+ private static final int MSG_COUNT = 50;
+
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+
+ if (usingInVMBroker())
+ {
+ TransportConnection.createVMBroker(1);
+ }
+
+
+ }
+
+ private boolean usingInVMBroker()
+ {
+ return BROKER.startsWith("vm://");
+ }
+
+ protected void tearDown() throws Exception
+ {
+ if (usingInVMBroker())
+ {
+ TransportConnection.killAllVMBrokers();
+ }
+ super.tearDown();
+ }
+
+ public void testPriority() throws JMSException, NamingException, AMQException
+ {
+ InitialContextFactory factory = new PropertiesFileInitialContextFactory();
+
+ Hashtable<String, String> env = new Hashtable<String, String>();
+
+ env.put("connectionfactory.connection", "amqp://guest:guest@TTL_TEST_ID" + VHOST + "?brokerlist='" + BROKER + "'");
+ env.put("queue.queue", QUEUE);
+
+ Context context = factory.getInitialContext(env);
+
+ Connection producerConnection = ((ConnectionFactory) context.lookup("connection")).createConnection();
+
+ Session producerSession = producerConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ final FieldTable arguments = new FieldTable();
+ arguments.put(new AMQShortString("x-qpid-priorities"),10);
+
+ ((AMQSession) producerSession).createQueue(new AMQShortString(QUEUE), true, false, false, arguments);
+
+ Queue queue = (Queue) context.lookup("queue");
+
+ ((AMQSession) producerSession).declareAndBind((AMQDestination)queue);
+
+
+
+
+
+
+ producerConnection.start();
+
+
+ MessageProducer producer = producerSession.createProducer(queue);
+
+
+
+
+
+ for (int msg = 0; msg < MSG_COUNT; msg++)
+ {
+ producer.setPriority(msg % 10);
+ producer.send(nextMessage(msg, false, producerSession, producer));
+ }
+
+ producer.close();
+ producerSession.close();
+ producerConnection.close();
+
+
+ Connection consumerConnection = ((ConnectionFactory) context.lookup("connection")).createConnection();
+ Session consumerSession = consumerConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ MessageConsumer consumer = consumerSession.createConsumer(queue);
+
+
+
+
+ consumerConnection.start();
+
+ Message received;
+ //Receive Message 0
+ StringBuilder buf = new StringBuilder();
+ int receivedCount = 0;
+ Message previous = null;
+ while((received = consumer.receive(1000))!=null)
+ {
+ if(previous != null)
+ {
+ assertTrue("Messages arrived in unexpected order", (previous.getJMSPriority() > received.getJMSPriority()) || ((previous.getJMSPriority() == received.getJMSPriority()) && previous.getIntProperty("msg") < received.getIntProperty("msg")) );
+ }
+
+ previous = received;
+ receivedCount++;
+ }
+
+ assertEquals("Incorrect number of message received", 50, receivedCount);
+
+ producerSession.close();
+ producer.close();
+
+ }
+
+ private Message nextMessage(int msg, boolean first, Session producerSession, MessageProducer producer) throws JMSException
+ {
+ Message send = producerSession.createTextMessage("Message: " + msg);
+ send.setIntProperty("msg", msg);
+
+ return send;
+ }
+
+
+}
diff --git a/java/systests/src/main/java/org/apache/qpid/server/queue/SubscriptionManagerTest.java b/java/systests/src/main/java/org/apache/qpid/server/queue/SubscriptionManagerTest.java
deleted file mode 100644
index 1200d7fc2b..0000000000
--- a/java/systests/src/main/java/org/apache/qpid/server/queue/SubscriptionManagerTest.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.queue;
-
-import junit.framework.TestCase;
-import org.apache.qpid.server.subscription.Subscription;
-
-public class SubscriptionManagerTest extends TestCase
-{
- private final SubscriptionSet mgr = new SubscriptionSet();
-
- public void testBasicSubscriptionManagement()
- {
- assertTrue(mgr.isEmpty());
- assertFalse(mgr.hasActiveSubscribers());
- SubscriptionTestHelper s1 = new SubscriptionTestHelper("S1");
- mgr.addSubscriber(s1);
- assertFalse(mgr.isEmpty());
- assertTrue(mgr.hasActiveSubscribers());
-
- SubscriptionTestHelper s2 = new SubscriptionTestHelper("S2");
- mgr.addSubscriber(s2);
-
- s2.setSuspended(true);
- assertFalse(mgr.isEmpty());
- assertTrue(mgr.hasActiveSubscribers());
- assertTrue(s2.isSuspended());
- assertFalse(s1.isSuspended());
-
- s1.setSuspended(true);
- assertFalse(mgr.hasActiveSubscribers());
-
- mgr.removeSubscriber(s1);
- assertFalse(mgr.isEmpty());
- mgr.removeSubscriber(s2);
- assertTrue(mgr.isEmpty());
- }
-
- public void testRoundRobin()
- {
- SubscriptionTestHelper a = new SubscriptionTestHelper("A");
- SubscriptionTestHelper b = new SubscriptionTestHelper("B");
- SubscriptionTestHelper c = new SubscriptionTestHelper("C");
- SubscriptionTestHelper d = new SubscriptionTestHelper("D");
- mgr.addSubscriber(a);
- mgr.addSubscriber(b);
- mgr.addSubscriber(c);
- mgr.addSubscriber(d);
-
- for (int i = 0; i < 3; i++)
- {
- assertEquals(a, mgr.nextSubscriber(null));
- assertEquals(b, mgr.nextSubscriber(null));
- assertEquals(c, mgr.nextSubscriber(null));
- assertEquals(d, mgr.nextSubscriber(null));
- }
-
- c.setSuspended(true);
-
- for (int i = 0; i < 3; i++)
- {
- assertEquals(a, mgr.nextSubscriber(null));
- assertEquals(b, mgr.nextSubscriber(null));
- assertEquals(d, mgr.nextSubscriber(null));
- }
-
- mgr.removeSubscriber(a);
- d.setSuspended(true);
- c.setSuspended(false);
- Subscription e = new SubscriptionTestHelper("D");
- mgr.addSubscriber(e);
-
- for (int i = 0; i < 3; i++)
- {
- assertEquals(b, mgr.nextSubscriber(null));
- assertEquals(c, mgr.nextSubscriber(null));
- assertEquals(e, mgr.nextSubscriber(null));
- }
- }
-
- public static junit.framework.Test suite()
- {
- return new junit.framework.TestSuite(SubscriptionManagerTest.class);
- }
-}
diff --git a/java/systests/src/main/java/org/apache/qpid/server/queue/SubscriptionSetTest.java b/java/systests/src/main/java/org/apache/qpid/server/queue/SubscriptionSetTest.java
deleted file mode 100644
index bcf54693d3..0000000000
--- a/java/systests/src/main/java/org/apache/qpid/server/queue/SubscriptionSetTest.java
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-package org.apache.qpid.server.queue;
-
-import junit.framework.TestCase;
-
-public class SubscriptionSetTest extends TestCase
-{
- /**
- * A SubscriptionSet that counts the number of items scanned.
- */
- static class TestSubscriptionSet extends SubscriptionSet
- {
- private int scanned = 0;
-
- void resetScanned()
- {
- scanned = 0;
- }
-
- protected void subscriberScanned()
- {
- ++scanned;
- }
-
- int getScanned()
- {
- return scanned;
- }
- }
-
- final SubscriptionTestHelper sub1 = new SubscriptionTestHelper("1");
- final SubscriptionTestHelper sub2 = new SubscriptionTestHelper("2");
- final SubscriptionTestHelper sub3 = new SubscriptionTestHelper("3");
-
- final SubscriptionTestHelper suspendedSub1 = new SubscriptionTestHelper("sus1", true);
- final SubscriptionTestHelper suspendedSub2 = new SubscriptionTestHelper("sus2", true);
- final SubscriptionTestHelper suspendedSub3 = new SubscriptionTestHelper("sus3", true);
-
- public void testNextMessage()
- {
- SubscriptionSet ss = new SubscriptionSet();
- assertNull(ss.nextSubscriber(null));
- assertEquals(0, ss.getCurrentSubscriber());
-
- ss.addSubscriber(sub1);
- assertEquals(sub1, ss.nextSubscriber(null));
- assertEquals(1, ss.getCurrentSubscriber());
- assertEquals(sub1, ss.nextSubscriber(null));
- assertEquals(1, ss.getCurrentSubscriber());
-
- ss.addSubscriber(sub2);
- ss.addSubscriber(sub3);
-
- assertEquals(sub2, ss.nextSubscriber(null));
- assertEquals(2, ss.getCurrentSubscriber());
-
- assertEquals(sub3, ss.nextSubscriber(null));
- assertEquals(3, ss.getCurrentSubscriber());
- }
-
- public void testNextMessageWhenAllSuspended()
- {
- SubscriptionSet ss = createAllSuspendedSubscriptionSet();
- assertNull(ss.nextSubscriber(null));
- assertEquals(3, ss.getCurrentSubscriber());
-
- assertNull(ss.nextSubscriber(null));
- assertEquals(3, ss.getCurrentSubscriber());
- }
-
- private TestSubscriptionSet createAllSuspendedSubscriptionSet()
- {
- TestSubscriptionSet ss = new TestSubscriptionSet();
- ss.addSubscriber(suspendedSub1);
- ss.addSubscriber(suspendedSub2);
- ss.addSubscriber(suspendedSub3);
- return ss;
- }
-
- public void testNextMessageAfterRemove()
- {
- SubscriptionSet ss = new SubscriptionSet();
- ss.addSubscriber(suspendedSub1);
- ss.addSubscriber(suspendedSub2);
- ss.addSubscriber(sub3);
- assertEquals(sub3, ss.nextSubscriber(null));
- assertEquals(3, ss.getCurrentSubscriber());
-
- assertEquals(suspendedSub1, ss.removeSubscriber(suspendedSub1));
-
- assertEquals(sub3, ss.nextSubscriber(null)); // Current implementation handles OutOfBoundsException here.
- assertEquals(2, ss.getCurrentSubscriber());
- }
-
- public void testNextMessageOverScanning()
- {
- TestSubscriptionSet ss = new TestSubscriptionSet();
- SubscriptionTestHelper sub = new SubscriptionTestHelper("test");
- ss.addSubscriber(suspendedSub1);
- ss.addSubscriber(sub);
- ss.addSubscriber(suspendedSub3);
- assertEquals(sub, ss.nextSubscriber(null));
- assertEquals(2, ss.getCurrentSubscriber());
- assertEquals(2, ss.getScanned());
-
- ss.resetScanned();
- sub.setSuspended(true);
- assertNull(ss.nextSubscriber(null));
- assertEquals(3, ss.getCurrentSubscriber());
- // Current implementation overscans by one item here.
- assertEquals(ss.size() + 1, ss.getScanned());
- }
-
- public void testNextMessageOverscanWorstCase() {
- TestSubscriptionSet ss = createAllSuspendedSubscriptionSet();
- ss.nextSubscriber(null);
- // Scans the subscriptions twice.
- assertEquals(ss.size() * 2, ss.getScanned());
- }
-
- public static junit.framework.Test suite()
- {
- return new junit.framework.TestSuite(SubscriptionSetTest.class);
- }
-}
diff --git a/java/systests/src/main/java/org/apache/qpid/server/queue/SubscriptionTestHelper.java b/java/systests/src/main/java/org/apache/qpid/server/queue/SubscriptionTestHelper.java
index 624a368b1f..7fd46474ab 100644
--- a/java/systests/src/main/java/org/apache/qpid/server/queue/SubscriptionTestHelper.java
+++ b/java/systests/src/main/java/org/apache/qpid/server/queue/SubscriptionTestHelper.java
@@ -106,12 +106,12 @@ public class SubscriptionTestHelper implements Subscription
//To change body of implemented methods use File | Settings | File Templates.
}
- public Object getQueueContext()
+ public QueueEntry getLastSeenEntry()
{
return null; //To change body of implemented methods use File | Settings | File Templates.
}
- public boolean setQueueContext(Object expected, Object newValue)
+ public boolean setLastSeenEntry(QueueEntry expected, QueueEntry newValue)
{
return false; //To change body of implemented methods use File | Settings | File Templates.
}
@@ -126,7 +126,7 @@ public class SubscriptionTestHelper implements Subscription
//no-op
}
- public AMQShortString getConumerTag()
+ public AMQShortString getConsumerTag()
{
return null; //To change body of implemented methods use File | Settings | File Templates.
}
@@ -141,6 +141,11 @@ public class SubscriptionTestHelper implements Subscription
return null;
}
+ public QueueEntry.SubscriptionAcquiredState getOwningState()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
public void queueDeleted(AMQQueue queue)
{
}
diff --git a/java/systests/src/main/java/org/apache/qpid/server/store/SkeletonMessageStore.java b/java/systests/src/main/java/org/apache/qpid/server/store/SkeletonMessageStore.java
index 57bc173812..dea52fea5a 100644
--- a/java/systests/src/main/java/org/apache/qpid/server/store/SkeletonMessageStore.java
+++ b/java/systests/src/main/java/org/apache/qpid/server/store/SkeletonMessageStore.java
@@ -25,7 +25,6 @@ import org.apache.qpid.AMQException;
import org.apache.qpid.framing.AMQShortString;
import org.apache.qpid.framing.FieldTable;
import org.apache.qpid.framing.abstraction.ContentChunk;
-import org.apache.qpid.server.queue.AMQQueueImpl;
import org.apache.qpid.server.queue.MessageMetaData;
import org.apache.qpid.server.queue.AMQQueue;
import org.apache.qpid.server.virtualhost.VirtualHost;
diff --git a/java/systests/src/main/java/org/apache/qpid/test/client/FlowControlTest.java b/java/systests/src/main/java/org/apache/qpid/test/client/FlowControlTest.java
new file mode 100644
index 0000000000..347217f732
--- /dev/null
+++ b/java/systests/src/main/java/org/apache/qpid/test/client/FlowControlTest.java
@@ -0,0 +1,209 @@
+/*
+*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*
+*/
+package org.apache.qpid.test.client;
+
+import org.apache.qpid.test.VMTestCase;
+import org.apache.qpid.client.AMQSession;
+import org.apache.qpid.client.AMQQueue;
+import org.apache.qpid.AMQException;
+import org.apache.log4j.Logger;
+
+import javax.jms.*;
+import javax.naming.NamingException;
+import java.util.Enumeration;
+
+public class FlowControlTest extends VMTestCase
+{
+ private static final Logger _logger = Logger.getLogger(FlowControlTest.class);
+
+ private Connection _clientConnection;
+ private Session _clientSession;
+ private Queue _queue;
+
+ public void setUp() throws Exception
+ {
+
+ super.setUp();
+
+
+ }
+
+ /**
+ * Simply
+ */
+ public void testBasicBytesFlowControl() throws JMSException, NamingException, AMQException
+ {
+ _queue = new AMQQueue("amq.direct","testqueue");//(Queue) _context.lookup("queue");
+
+ //Create Client
+ _clientConnection = ((ConnectionFactory) _context.lookup("connection")).createConnection();
+
+ _clientConnection.start();
+
+ _clientSession = _clientConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ //Ensure _queue is created
+ _clientSession.createConsumer(_queue).close();
+
+ Connection producerConnection = ((ConnectionFactory) _context.lookup("connection")).createConnection();
+
+ producerConnection.start();
+
+ Session producerSession = producerConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ MessageProducer producer = producerSession.createProducer(_queue);
+
+ BytesMessage m1 = producerSession.createBytesMessage();
+ m1.writeBytes(new byte[128]);
+ m1.setIntProperty("msg",1);
+ producer.send(m1);
+ BytesMessage m2 = producerSession.createBytesMessage();
+ m2.writeBytes(new byte[128]);
+ m2.setIntProperty("msg",2);
+ producer.send(m2);
+ BytesMessage m3 = producerSession.createBytesMessage();
+ m3.writeBytes(new byte[256]);
+ m3.setIntProperty("msg",3);
+ producer.send(m3);
+
+ producer.close();
+ producerSession.close();
+ producerConnection.close();
+
+
+ Connection consumerConnection = ((ConnectionFactory) _context.lookup("connection")).createConnection();
+ Session consumerSession = consumerConnection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+ ((AMQSession)consumerSession).setPrefecthLimits(0,256);
+ MessageConsumer recv = consumerSession.createConsumer(_queue);
+ consumerConnection.start();
+
+ Message r1 = recv.receive(RECEIVE_TIMEOUT);
+ assertNotNull("First message not received", r1);
+ assertEquals("Messages in wrong order", 1, r1.getIntProperty("msg"));
+
+ Message r2 = recv.receive(RECEIVE_TIMEOUT);
+ assertNotNull("Second message not received", r2);
+ assertEquals("Messages in wrong order", 2, r2.getIntProperty("msg"));
+
+ Message r3 = recv.receiveNoWait();
+ assertNull("Third message incorrectly delivered", r3);
+
+ r1.acknowledge();
+
+ r3 = recv.receiveNoWait();
+ assertNull("Third message incorrectly delivered", r3);
+
+ r2.acknowledge();
+
+
+ r3 = recv.receive(RECEIVE_TIMEOUT);
+ assertNotNull("Third message not received", r3);
+ assertEquals("Messages in wrong order", 3, r3.getIntProperty("msg"));
+
+ r3.acknowledge();
+ recv.close();
+ consumerSession.close();
+ consumerConnection.close();
+
+ }
+
+ public void testTwoConsumersBytesFlowControl() throws JMSException, NamingException, AMQException
+ {
+ _queue = new AMQQueue("amq.direct","testqueue1");//(Queue) _context.lookup("queue");
+
+ //Create Client
+ _clientConnection = ((ConnectionFactory) _context.lookup("connection")).createConnection();
+
+ _clientConnection.start();
+
+ _clientSession = _clientConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+ //Ensure _queue is created
+ _clientSession.createConsumer(_queue).close();
+
+ Connection producerConnection = ((ConnectionFactory) _context.lookup("connection")).createConnection();
+
+ producerConnection.start();
+
+ Session producerSession = producerConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+ MessageProducer producer = producerSession.createProducer(_queue);
+
+ BytesMessage m1 = producerSession.createBytesMessage();
+ m1.writeBytes(new byte[128]);
+ m1.setIntProperty("msg",1);
+ producer.send(m1);
+ BytesMessage m2 = producerSession.createBytesMessage();
+ m2.writeBytes(new byte[256]);
+ m2.setIntProperty("msg",2);
+ producer.send(m2);
+ BytesMessage m3 = producerSession.createBytesMessage();
+ m3.writeBytes(new byte[128]);
+ m3.setIntProperty("msg",3);
+ producer.send(m3);
+
+ producer.close();
+ producerSession.close();
+ producerConnection.close();
+
+
+ Connection consumerConnection = ((ConnectionFactory) _context.lookup("connection")).createConnection();
+ Session consumerSession1 = consumerConnection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+ ((AMQSession)consumerSession1).setPrefecthLimits(0,256);
+ MessageConsumer recv1 = consumerSession1.createConsumer(_queue);
+
+ consumerConnection.start();
+
+ Message r1 = recv1.receive(RECEIVE_TIMEOUT);
+ assertNotNull("First message not received", r1);
+ assertEquals("Messages in wrong order", 1, r1.getIntProperty("msg"));
+
+
+ Message r2 = recv1.receiveNoWait();
+ assertNull("Second message incorrectly delivered", r2);
+
+ Session consumerSession2 = consumerConnection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
+ ((AMQSession)consumerSession2).setPrefecthLimits(0,256);
+ MessageConsumer recv2 = consumerSession2.createConsumer(_queue);
+
+
+ r2 = recv2.receive(100000L);//RECEIVE_TIMEOUT);
+ assertNotNull("Second message not received", r2);
+ assertEquals("Messages in wrong order", 2, r2.getIntProperty("msg"));
+
+ Message r3 = recv2.receiveNoWait();
+ assertNull("Third message incorrectly delivered", r3);
+
+ r3 = recv1.receive(100000L);//RECEIVE_TIMEOUT);
+ assertNotNull("Third message not received", r3);
+ assertEquals("Messages in wrong order", 3, r3.getIntProperty("msg"));
+
+
+
+ r2.acknowledge();
+ r3.acknowledge();
+ recv1.close();
+ recv2.close();
+ consumerSession1.close();
+ consumerSession2.close();
+ consumerConnection.close();
+
+ }
+
+}