summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Godfrey <rgodfrey@apache.org>2008-04-21 14:03:35 +0000
committerRobert Godfrey <rgodfrey@apache.org>2008-04-21 14:03:35 +0000
commit9930676624d4c27f866c9d40227fd7c282f4dac2 (patch)
treeef3639dcdbd8e93160a8286b7f13fecb4e55abcb
parent1d69ea16b30dd67ac32683e6dc512f4c58ef93f1 (diff)
downloadqpid-python-9930676624d4c27f866c9d40227fd7c282f4dac2.tar.gz
Initial checkpoint of queue refactoring work
git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/branches/broker-queue-refactor@650148 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--java/broker/etc/config.xml12
-rw-r--r--java/broker/etc/virtualhosts.xml26
-rwxr-xr-xjava/broker/python-test.xml2
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/AMQBrokerManagerMBean.java5
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/AMQChannel.java517
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/RequiredDeliveryException.java15
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/ack/TxAck.java14
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessage.java91
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessageMap.java22
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessageMapImpl.java59
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/configuration/VirtualHostConfiguration.java7
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/exchange/AbstractExchange.java1
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeFactory.java4
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeRegistry.java5
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/exchange/DirectExchange.java (renamed from java/broker/src/main/java/org/apache/qpid/server/exchange/DestNameExchange.java)53
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/exchange/Exchange.java5
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java464
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/exchange/HeadersExchange.java20
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/exchange/Index.java2
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/exchange/MessageRouter.java3
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/exchange/NoRouteException.java5
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/exchange/TopicExchange.java (renamed from java/broker/src/main/java/org/apache/qpid/server/exchange/DestWildExchange.java)72
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeaderKey.java40
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeaderKeyDictionary.java50
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeaderMatcherResult.java25
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeadersMatcherDFAState.java339
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeadersParser.java439
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicMatcherDFAState.java236
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicMatcherResult.java25
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicParser.java448
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicWord.java34
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicWordDictionary.java63
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/flow/AbstractFlowCreditManager.java57
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/flow/BytesOnlyCreditManager.java77
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/flow/FlowCreditManager.java44
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/flow/LimitlessCreditManager.java44
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/flow/MessageAndBytesCreditManager.java79
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/flow/MessageOnlyCreditManager.java76
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/flow/Pre0_10CreditManager.java177
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/handler/AccessRequestHandler.java4
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/handler/BasicCancelMethodHandler.java4
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/handler/BasicConsumeMethodHandler.java5
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/handler/BasicGetMethodHandler.java2
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/handler/BasicPublishMethodHandler.java2
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/handler/BasicQosHandler.java6
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/handler/BasicRejectMethodHandler.java27
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeBoundHandler.java4
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/handler/QueueBindHandler.java2
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeclareHandler.java7
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeleteHandler.java8
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/handler/QueuePurgeHandler.java4
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/handler/QueueUnbindHandler.java4
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/handler/TxCommitHandler.java4
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/output/amqp0_8/ProtocolOutputConverterImpl.java15
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/output/amqp0_9/ProtocolOutputConverterImpl.java18
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/protocol/AMQMinaProtocolSession.java14
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/queue/AMQMessage.java408
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/queue/AMQMessageHandle.java32
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueue.java1001
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueFactory.java (renamed from java/broker/src/main/java/org/apache/qpid/server/queue/WeightedSubscriptionManager.java)22
-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/AMQQueueMBean.java4
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/queue/ConcurrentSelectorDeliveryManager.java183
-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/DeliveryManager.java102
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/queue/ExchangeBindings.java4
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/queue/InMemoryMessageHandle.java42
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/queue/IncomingMessage.java310
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/queue/MessageHandleFactory.java4
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntry.java225
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java240
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryList.java220
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java1174
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/queue/Subscription.java63
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionImpl.java689
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionManager.java13
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionSet.java82
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/queue/WeakReferenceMessageHandle.java66
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/store/DerbyMessageStore.java1414
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/store/MemoryMessageStore.java3
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/store/MessageStore.java2
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription.java108
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionFactory.java (renamed from java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionFactory.java)14
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionFactoryImpl.java74
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java554
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionList.java247
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/txn/CleanupMessageOperation.java77
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/txn/LocalTransactionalContext.java136
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/txn/NonTransactionalContext.java90
-rw-r--r--java/broker/src/main/java/org/apache/qpid/server/txn/TransactionalContext.java23
-rw-r--r--java/broker/src/main/java/org/apache/qpid/tools/messagestore/MessageStoreTool.java2
-rw-r--r--java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Copy.java2
-rw-r--r--java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Dump.java3
-rw-r--r--java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/List.java1
-rw-r--r--java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Move.java2
-rw-r--r--java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Purge.java3
-rw-r--r--java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Select.java2
-rw-r--r--java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Show.java2
-rw-r--r--java/broker/src/test/java/org/apache/qpid/server/exchange/DestWildExchangeTest.java182
-rw-r--r--java/broker/src/test/java/org/apache/qpid/server/exchange/ExchangeMBeanTest.java10
-rw-r--r--java/broker/src/test/java/org/apache/qpid/server/protocol/TestMinaProtocolSession.java2
-rw-r--r--java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueAlertTest.java98
-rw-r--r--java/broker/src/test/java/org/apache/qpid/server/queue/AMQQueueMBeanTest.java82
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/AMQSession.java12
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer.java6
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/handler/BasicDeliverMethodHandler.java2
-rw-r--r--java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolSession.java15
-rw-r--r--java/client/src/test/java/org/apache/qpid/client/AMQQueueDeferredOrderingTest.java2
-rw-r--r--java/client/src/test/java/org/apache/qpid/client/MessageListenerMultiConsumerTest.java16
-rw-r--r--java/client/src/test/java/org/apache/qpid/client/ResetMessageListenerTest.java11
-rw-r--r--java/client/src/test/java/org/apache/qpid/test/unit/ack/RecoverTest.java1
-rw-r--r--java/client/src/test/java/org/apache/qpid/test/unit/basic/SessionStartTest.java4
-rw-r--r--java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseOkTest.java24
-rw-r--r--java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/Client.java6
-rw-r--r--java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/CombinedTest.java11
-rw-r--r--java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/Service.java10
-rw-r--r--java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/ServiceCreator.java9
-rw-r--r--java/client/src/test/java/org/apache/qpid/test/unit/close/MessageRequeueTest.java73
-rw-r--r--java/client/src/test/java/org/apache/qpid/test/unit/topic/DurableSubscriptionTest.java2
-rw-r--r--java/client/src/test/java/org/apache/qpid/test/unit/topic/TopicSessionTest.java225
-rw-r--r--java/common/src/main/java/org/apache/mina/common/FixedSizeByteBufferAllocator.java63
-rw-r--r--java/common/src/main/java/org/apache/mina/filter/codec/OurCumulativeProtocolDecoder.java2
-rw-r--r--java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketConnector.java3
-rw-r--r--java/common/src/main/java/org/apache/qpid/common/ClientProperties.java23
-rw-r--r--java/common/src/main/java/org/apache/qpid/framing/AMQShortString.java56
-rw-r--r--java/common/src/main/java/org/apache/qpid/framing/AMQTypedValue.java20
-rw-r--r--java/common/src/main/java/org/apache/qpid/framing/FieldTable.java105
-rw-r--r--java/management/eclipse-plugin/src/test/java/org/apache/qpid/management/ui/ManagementConsoleTest.java9
-rw-r--r--java/plugins/src/main/java/org/apache/qpid/extras/exchanges/diagnostic/DiagnosticExchange.java5
-rw-r--r--java/plugins/src/main/java/org/apache/qpid/extras/exchanges/example/TestExchange.java5
-rw-r--r--java/systests/src/main/java/org/apache/qpid/server/ack/TxAckTest.java64
-rw-r--r--java/systests/src/main/java/org/apache/qpid/server/exchange/AbstractHeadersExchangeTestBase.java113
-rw-r--r--java/systests/src/main/java/org/apache/qpid/server/protocol/AMQProtocolSessionMBeanTest.java11
-rw-r--r--java/systests/src/main/java/org/apache/qpid/server/queue/AckTest.java64
-rw-r--r--java/systests/src/main/java/org/apache/qpid/server/queue/ConcurrencyTestDisabled.java8
-rw-r--r--java/systests/src/main/java/org/apache/qpid/server/queue/DeliveryManagerTest.java177
-rw-r--r--java/systests/src/main/java/org/apache/qpid/server/queue/MessageTestHelper.java24
-rw-r--r--java/systests/src/main/java/org/apache/qpid/server/queue/SubscriptionManagerTest.java5
-rw-r--r--java/systests/src/main/java/org/apache/qpid/server/queue/SubscriptionTestHelper.java56
-rw-r--r--java/systests/src/main/java/org/apache/qpid/server/store/SkeletonMessageStore.java4
-rw-r--r--java/systests/src/main/java/org/apache/qpid/server/store/TestReferenceCounting.java44
-rw-r--r--java/systests/src/main/java/org/apache/qpid/test/client/DupsOkTest.java13
142 files changed, 10311 insertions, 4143 deletions
diff --git a/java/broker/etc/config.xml b/java/broker/etc/config.xml
index 80ee039ee5..0f4ba3e769 100644
--- a/java/broker/etc/config.xml
+++ b/java/broker/etc/config.xml
@@ -17,21 +17,13 @@
- KIND, either express or implied. See the License for the
- specific language governing permissions and limitations
- under the License.
- -
+ - AMQ
-->
<broker>
<prefix>${QPID_HOME}</prefix>
<work>${QPID_WORK}</work>
<conf>${prefix}/etc</conf>
<connector>
- <!-- Uncomment out this block and edit the keystorePath and keystorePassword
- to enable SSL support
- <ssl>
- <enabled>true</enabled>
- <sslOnly>true</sslOnly>
- <keystorePath>/path/to/keystore.ks</keystorePath>
- <keystorePassword>keystorepass</keystorePassword>
- </ssl>-->
<qpidnio>false</qpidnio>
<protectio>
<enabled>false</enabled>
@@ -87,6 +79,7 @@
<name>localhost</name>
<localhost>
<store>
+ <!-- <class>org.apache.qpid.server.store.MemoryMessageStore</class> -->
<class>org.apache.qpid.server.store.MemoryMessageStore</class>
</store>
@@ -110,6 +103,7 @@
<name>test</name>
<test>
<store>
+ <!-- <class>org.apache.qpid.server.store.MemoryMessageStore</class> -->
<class>org.apache.qpid.server.store.MemoryMessageStore</class>
</store>
</test>
diff --git a/java/broker/etc/virtualhosts.xml b/java/broker/etc/virtualhosts.xml
index f62ec3f5d7..ce38742e3f 100644
--- a/java/broker/etc/virtualhosts.xml
+++ b/java/broker/etc/virtualhosts.xml
@@ -26,14 +26,16 @@
<localhost>
<exchanges>
<exchange>
- <type>direct</type>
- <name>test.direct</name>
- <durable>true</durable>
+ <name>testdirect</name>
+ <testdirect>
+ <durable>true</durable>
+ </testdirect>
</exchange>
<exchange>
- <type>topic</type>
- <name>test.topic</name>
+ <name>testtopic</name>
</exchange>
+
+
</exchanges>
<queues>
<exchange>amq.direct</exchange>
@@ -69,6 +71,17 @@
<virtualhost>
<name>development</name>
<development>
+ <exchanges>
+ <exchange>
+ <name>testdirect</name>
+ <testdirect>
+ <durable>true</durable>
+ </testdirect>
+ </exchange>
+ <exchange>
+ <name>testtopic</name>
+ </exchange>
+ </exchanges>
<queues>
<minimumAlertRepeatGap>30000</minimumAlertRepeatGap>
<maximumMessageCount>5000</maximumMessageCount>
@@ -85,6 +98,7 @@
<name>ping</name>
<ping>
<exchange>amq.direct</exchange>
+ <durable>true</durable>
<maximumQueueDepth>4235264</maximumQueueDepth> <!-- 4Mb -->
<maximumMessageSize>2117632</maximumMessageSize> <!-- 2Mb -->
<maximumMessageAge>600000</maximumMessageAge> <!-- 10 mins -->
@@ -97,8 +111,6 @@
<name>test</name>
<test>
<queues>
- <minimumAlertRepeatGap>30000</minimumAlertRepeatGap>
- <maximumMessageCount>5000</maximumMessageCount>
<queue>
<name>queue</name>
<queue>
diff --git a/java/broker/python-test.xml b/java/broker/python-test.xml
index 5c263e3169..dfba7256d2 100755
--- a/java/broker/python-test.xml
+++ b/java/broker/python-test.xml
@@ -46,6 +46,8 @@ under the License.
<arg value="2110"/>
<arg value="-m"/>
<arg value="2111"/>
+ <arg value="-s"/>
+ <arg value="../spec/amqp.0-9.no-wip.xml"/>
<classpath refid="maven.test.classpath"/>
<sysproperty key="QPID_HOME" value="${broker.dir}"/>
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 9335723bc5..cf1af650b7 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
@@ -56,8 +56,9 @@ import org.apache.qpid.server.management.MBeanConstructor;
import org.apache.qpid.server.management.MBeanDescription;
import org.apache.qpid.server.management.ManagedBroker;
import org.apache.qpid.server.management.ManagedObject;
-import org.apache.qpid.server.queue.AMQQueue;
import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.queue.AMQQueueFactory;
import org.apache.qpid.server.store.MessageStore;
import org.apache.qpid.server.virtualhost.VirtualHost;
@@ -175,7 +176,7 @@ public class AMQBrokerManagerMBean extends AMQManagedObject implements ManagedBr
ownerShortString = new AMQShortString(owner);
}
- queue = new AMQQueue(new AMQShortString(queueName), durable, ownerShortString, false, getVirtualHost());
+ queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString(queueName), durable, ownerShortString, false, getVirtualHost());
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 17b4fa5d65..0b8913f650 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
@@ -21,7 +21,6 @@
package org.apache.qpid.server;
import org.apache.log4j.Logger;
-
import org.apache.qpid.AMQException;
import org.apache.qpid.configuration.Configured;
import org.apache.qpid.framing.AMQShortString;
@@ -30,14 +29,21 @@ import org.apache.qpid.framing.ContentBody;
import org.apache.qpid.framing.ContentHeaderBody;
import org.apache.qpid.framing.FieldTable;
import org.apache.qpid.framing.abstraction.MessagePublishInfo;
-import org.apache.qpid.server.ack.UnacknowledgedMessage;
import org.apache.qpid.server.ack.UnacknowledgedMessageMap;
import org.apache.qpid.server.ack.UnacknowledgedMessageMapImpl;
import org.apache.qpid.server.configuration.Configurator;
-import org.apache.qpid.server.exchange.NoRouteException;
import org.apache.qpid.server.exchange.Exchange;
+import org.apache.qpid.server.exchange.NoRouteException;
+import org.apache.qpid.server.flow.FlowCreditManager;
+import org.apache.qpid.server.flow.Pre0_10CreditManager;
import org.apache.qpid.server.protocol.AMQProtocolSession;
-import org.apache.qpid.server.queue.*;
+import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.queue.IncomingMessage;
+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.store.MessageStore;
import org.apache.qpid.server.store.StoreContext;
import org.apache.qpid.server.txn.LocalTransactionalContext;
@@ -45,12 +51,11 @@ import org.apache.qpid.server.txn.NonTransactionalContext;
import org.apache.qpid.server.txn.TransactionalContext;
import java.util.Collection;
-import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
public class AMQChannel
@@ -61,13 +66,8 @@ public class AMQChannel
private final int _channelId;
- // private boolean _transactional;
-
- private long _prefetch_HighWaterMark;
- private long _prefetch_LowWaterMark;
-
- private long _prefetchSize;
+ private final Pre0_10CreditManager _creditManager = new Pre0_10CreditManager(0l,0l);
/**
* The delivery tag is unique per channel. This is pre-incremented before putting into the deliver frame so that
@@ -86,10 +86,11 @@ public class AMQChannel
* been received by this channel. As the frames are received the message gets updated and once all frames have been
* received the message can then be routed.
*/
- private AMQMessage _currentMessage;
+ private IncomingMessage _currentMessage;
+
+ /** Maps from consumer tag to subscription instance. Allows us to unsubscribe from a queue. */
+ private final Map<AMQShortString, Subscription> _tag2SubscriptionMap = new HashMap<AMQShortString, Subscription>();
- /** Maps from consumer tag to queue instance. Allows us to unsubscribe from a queue. */
- private final Map<AMQShortString, AMQQueue> _consumerTag2QueueMap = new ConcurrentHashMap<AMQShortString, AMQQueue>();
private final MessageStore _messageStore;
@@ -97,7 +98,7 @@ public class AMQChannel
private final AtomicBoolean _suspended = new AtomicBoolean(false);
- private TransactionalContext _txnContext, _nonTransactedContext;
+ private TransactionalContext _txnContext;
/**
* A context used by the message store enabling it to track context for a given channel even across thread
@@ -109,8 +110,6 @@ public class AMQChannel
private MessageHandleFactory _messageHandleFactory = new MessageHandleFactory();
- private Set<Long> _browsedAcks = new HashSet<Long>();
-
// Why do we need this reference ? - ritchiem
private final AMQProtocolSession _session;
private boolean _closing;
@@ -118,7 +117,7 @@ public class AMQChannel
@Configured(path = "advanced.enableJMSXUserID",
defaultValue = "false")
public boolean ENABLE_JMSXUserID;
-
+
public AMQChannel(AMQProtocolSession session, int channelId, MessageStore messageStore)
throws AMQException
@@ -129,18 +128,18 @@ public class AMQChannel
_session = session;
_channelId = channelId;
_storeContext = new StoreContext("Session: " + session.getClientIdentifier() + "; channel: " + channelId);
- _prefetch_HighWaterMark = DEFAULT_PREFETCH;
- _prefetch_LowWaterMark = _prefetch_HighWaterMark / 2;
+
+
_messageStore = messageStore;
// by default the session is non-transactional
- _txnContext = new NonTransactionalContext(_messageStore, _storeContext, this, _returnMessages, _browsedAcks);
+ _txnContext = new NonTransactionalContext(_messageStore, _storeContext, this, _returnMessages);
}
/** Sets this channel to be part of a local transaction */
public void setLocalTransactional()
{
- _txnContext = new LocalTransactionalContext(_messageStore, _storeContext, _returnMessages);
+ _txnContext = new LocalTransactionalContext(this);
}
public boolean isTransactional()
@@ -156,55 +155,15 @@ public class AMQChannel
return _channelId;
}
- public long getPrefetchCount()
+ public void setPublishFrame(MessagePublishInfo info, final Exchange e) throws AMQException
{
- return _prefetch_HighWaterMark;
- }
- public void setPrefetchCount(long prefetchCount)
- {
- _prefetch_HighWaterMark = prefetchCount;
- }
-
- public long getPrefetchSize()
- {
- return _prefetchSize;
- }
-
- public void setPrefetchSize(long prefetchSize)
- {
- _prefetchSize = prefetchSize;
- }
-
- public long getPrefetchLowMarkCount()
- {
- return _prefetch_LowWaterMark;
- }
-
- public void setPrefetchLowMarkCount(long prefetchCount)
- {
- _prefetch_LowWaterMark = prefetchCount;
- }
-
- public long getPrefetchHighMarkCount()
- {
- return _prefetch_HighWaterMark;
- }
-
- public void setPrefetchHighMarkCount(long prefetchCount)
- {
- _prefetch_HighWaterMark = prefetchCount;
- }
-
- public void setPublishFrame(MessagePublishInfo info, AMQProtocolSession publisher, final Exchange e) throws AMQException
- {
-
- _currentMessage = new AMQMessage(_messageStore.getNewMessageId(), info, _txnContext);
- _currentMessage.setPublisher(publisher);
+ _currentMessage = new IncomingMessage(_messageStore.getNewMessageId(), info, _txnContext, _session);
+ _currentMessage.setMessageStore(_messageStore);
_currentMessage.setExchange(e);
}
- public void publishContentHeader(ContentHeaderBody contentHeaderBody, AMQProtocolSession protocolSession)
+ public void publishContentHeader(ContentHeaderBody contentHeaderBody)
throws AMQException
{
if (_currentMessage == null)
@@ -215,7 +174,7 @@ public class AMQChannel
{
if (_log.isDebugEnabled())
{
- _log.debug(debugIdentity() + "Content header received on channel " + _channelId);
+ _log.debug("Content header received on channel " + _channelId);
}
if (ENABLE_JMSXUserID)
@@ -225,25 +184,48 @@ public class AMQChannel
//fixme: fudge for QPID-677
properties.getHeaders().keySet();
- properties.setUserId(protocolSession.getAuthorizedID().getName());
+ properties.setUserId(_session.getAuthorizedID().getName());
}
_currentMessage.setContentHeaderBody(contentHeaderBody);
+
_currentMessage.setExpiration();
routeCurrentMessage();
- _currentMessage.routingComplete(_messageStore, _storeContext, _messageHandleFactory);
- // check and deliver if header says body length is zero
- if (contentHeaderBody.bodySize == 0)
+ _currentMessage.routingComplete(_messageStore, _messageHandleFactory);
+
+ deliverCurrentMessageIfComplete();
+
+ }
+ }
+
+ private void deliverCurrentMessageIfComplete()
+ throws AMQException
+ {
+ // check and deliver if header says body length is zero
+ if (_currentMessage.allContentReceived())
+ {
+ try
{
- _txnContext.messageProcessed(protocolSession);
+ _currentMessage.deliverToQueues();
+ }
+ catch (NoRouteException e)
+ {
+ _returnMessages.add(e);
+ }
+ finally
+ {
+ // callback to allow the context to do any post message processing
+ // primary use is to allow message return processing in the non-tx case
+ _txnContext.messageProcessed(_session);
_currentMessage = null;
}
}
+
}
- public void publishContentBody(ContentBody contentBody, AMQProtocolSession protocolSession) throws AMQException
+ public void publishContentBody(ContentBody contentBody) throws AMQException
{
if (_currentMessage == null)
{
@@ -260,15 +242,11 @@ public class AMQChannel
// returns true iff the message was delivered (i.e. if all data was
// received
- if (_currentMessage.addContentBodyFrame(_storeContext,
- protocolSession.getMethodRegistry().getProtocolVersionMethodConverter().convertToContentChunk(
- contentBody)))
- {
- // callback to allow the context to do any post message processing
- // primary use is to allow message return processing in the non-tx case
- _txnContext.messageProcessed(protocolSession);
- _currentMessage = null;
- }
+ _currentMessage.addContentBodyFrame(
+ _session.getMethodRegistry().getProtocolVersionMethodConverter().convertToContentChunk(
+ contentBody));
+
+ deliverCurrentMessageIfComplete();
}
catch (AMQException e)
{
@@ -287,6 +265,7 @@ public class AMQChannel
}
catch (NoRouteException e)
{
+ //_currentMessage.incrementReference();
_returnMessages.add(e);
}
}
@@ -307,18 +286,17 @@ public class AMQChannel
*
* @param tag the tag chosen by the client (if null, server will generate one)
* @param queue the queue to subscribe to
- * @param session the protocol session of the subscriber
- * @param noLocal Flag stopping own messages being receivied.
- * @param exclusive Flag requesting exclusive access to the queue
* @param acks Are acks enabled for this subscriber
* @param filters Filters to apply to this subscriber
*
+ * @param noLocal Flag stopping own messages being receivied.
+ * @param exclusive Flag requesting exclusive access to the queue
* @return the consumer tag. This is returned to the subscriber and used in subsequent unsubscribe requests
*
* @throws ConsumerTagNotUniqueException if the tag is not unique
* @throws AMQException if something goes wrong
*/
- public AMQShortString subscribeToQueue(AMQShortString tag, AMQQueue queue, AMQProtocolSession session, boolean acks,
+ public AMQShortString subscribeToQueue(AMQShortString tag, AMQQueue queue, boolean acks,
FieldTable filters, boolean noLocal, boolean exclusive) throws AMQException, ConsumerTagNotUniqueException
{
if (tag == null)
@@ -326,77 +304,65 @@ public class AMQChannel
tag = new AMQShortString("sgen_" + getNextConsumerTag());
}
- if (_consumerTag2QueueMap.containsKey(tag))
+ if (_tag2SubscriptionMap.containsKey(tag))
{
throw new ConsumerTagNotUniqueException();
}
+ Subscription subscription =
+ SubscriptionFactoryImpl.INSTANCE.createSubscription(_channelId, _session, tag, acks, filters, noLocal, _creditManager);
+
+
+ // So to keep things straight we put before the call and catch all exceptions from the register and tidy up.
// We add before we register as the Async Delivery process may AutoClose the subscriber
// so calling _cT2QM.remove before we have done put which was after the register succeeded.
// So to keep things straight we put before the call and catch all exceptions from the register and tidy up.
- _consumerTag2QueueMap.put(tag, queue);
+
+ _tag2SubscriptionMap.put(tag, subscription);
try
{
- queue.registerProtocolSession(session, _channelId, tag, acks, filters, noLocal, exclusive);
+ queue.registerSubscription(subscription, exclusive);
}
catch (AMQException e)
{
- _consumerTag2QueueMap.remove(tag);
+ _tag2SubscriptionMap.remove(tag);
throw e;
}
-
return tag;
}
/**
* Unsubscribe a consumer from a queue.
- * @param session
* @param consumerTag
* @return true if the consumerTag had a mapped queue that could be unregistered.
* @throws AMQException
*/
- public boolean unsubscribeConsumer(AMQProtocolSession session, AMQShortString consumerTag) throws AMQException
+ public boolean unsubscribeConsumer(AMQShortString consumerTag) throws AMQException
{
- if (_log.isDebugEnabled())
- {
- _log.debug("Unacked Map Dump size:" + _unacknowledgedMessageMap.size());
- _unacknowledgedMessageMap.visit(new UnacknowledgedMessageMap.Visitor()
- {
-
- public boolean callback(UnacknowledgedMessage message) throws AMQException
- {
- _log.debug(message);
-
- return true;
- }
- public void visitComplete()
- {
- }
- });
- }
-
- AMQQueue q = _consumerTag2QueueMap.remove(consumerTag);
- if (q != null)
+ Subscription sub = _tag2SubscriptionMap.remove(consumerTag);
+ if (sub != null)
{
- q.unregisterProtocolSession(session, _channelId, consumerTag);
+ sub.getQueue().unregisterSubscription(sub);
return true;
}
+ else
+ {
+ _log.warn("Attempt to unsubscribe consumer with tag '"+consumerTag+"' which is not registered.");
+ }
return false;
}
/**
* Called from the protocol session to close this channel and clean up. T
*
- * @param session The session to close
- *
* @throws AMQException if there is an error during closure
*/
- public void close(AMQProtocolSession session) throws AMQException
+ public void close() throws AMQException
{
_txnContext.rollback();
- unsubscribeAllConsumers(session);
+ unsubscribeAllConsumers();
try
{
requeue();
@@ -414,11 +380,11 @@ public class AMQChannel
_closing = closing;
}
- private void unsubscribeAllConsumers(AMQProtocolSession session) throws AMQException
+ private void unsubscribeAllConsumers() throws AMQException
{
if (_log.isInfoEnabled())
{
- if (!_consumerTag2QueueMap.isEmpty())
+ if (!_tag2SubscriptionMap.isEmpty())
{
_log.info("Unsubscribing all consumers on channel " + toString());
}
@@ -428,17 +394,19 @@ public class AMQChannel
}
}
- for (Map.Entry<AMQShortString, AMQQueue> me : _consumerTag2QueueMap.entrySet())
+ for (Map.Entry<AMQShortString, Subscription> me : _tag2SubscriptionMap.entrySet())
{
if (_log.isInfoEnabled())
{
_log.info("Unsubscribing consumer '" + me.getKey() + "' on channel " + toString());
}
- me.getValue().unregisterProtocolSession(session, _channelId, me.getKey());
+ Subscription sub = me.getValue();
+
+ sub.getQueue().unregisterSubscription(sub);
}
- _consumerTag2QueueMap.clear();
+ _tag2SubscriptionMap.clear();
}
/**
@@ -447,9 +415,9 @@ public class AMQChannel
* @param entry the record of the message on the queue that was delivered
* @param deliveryTag the delivery tag used when delivering the message (see protocol spec for description of the
* delivery tag)
- * @param consumerTag The tag for the consumer that is to acknowledge this message.
+ * @param subscription The consumer that is to acknowledge this message.
*/
- public void addUnacknowledgedMessage(QueueEntry entry, long deliveryTag, AMQShortString consumerTag)
+ public void addUnacknowledgedMessage(QueueEntry entry, long deliveryTag, Subscription subscription)
{
if (_log.isDebugEnabled())
{
@@ -462,15 +430,14 @@ public class AMQChannel
if (_log.isDebugEnabled())
{
_log.debug(debugIdentity() + " Adding unacked message(" + entry.getMessage().toString() + " DT:" + deliveryTag
- + ") with a queue(" + entry.getQueue() + ") for " + consumerTag);
+ + ") with a queue(" + entry.getQueue() + ") for " + subscription);
}
}
}
synchronized (_unacknowledgedMessageMap.getLock())
{
- _unacknowledgedMessageMap.add(deliveryTag, new UnacknowledgedMessage(entry, consumerTag, deliveryTag));
- checkSuspension();
+ _unacknowledgedMessageMap.add(deliveryTag, entry);
}
}
@@ -490,7 +457,7 @@ public class AMQChannel
public void requeue() throws AMQException
{
// we must create a new map since all the messages will get a new delivery tag when they are redelivered
- Collection<UnacknowledgedMessage> messagesToBeDelivered = _unacknowledgedMessageMap.cancelAllMessages();
+ Collection<QueueEntry> messagesToBeDelivered = _unacknowledgedMessageMap.cancelAllMessages();
// Deliver these messages out of the transaction as their delivery was never
// part of the transaction only the receive.
@@ -505,13 +472,9 @@ public class AMQChannel
if (!(_txnContext instanceof NonTransactionalContext))
{
- // if (_nonTransactedContext == null)
- {
- _nonTransactedContext =
- new NonTransactionalContext(_messageStore, _storeContext, this, _returnMessages, _browsedAcks);
- }
- deliveryContext = _nonTransactedContext;
+ deliveryContext =
+ new NonTransactionalContext(_messageStore, _storeContext, this, _returnMessages);
}
else
{
@@ -519,23 +482,27 @@ public class AMQChannel
}
}
- for (UnacknowledgedMessage unacked : messagesToBeDelivered)
+ for (QueueEntry unacked : messagesToBeDelivered)
{
if (!unacked.isQueueDeleted())
{
- // Ensure message is released for redelivery
- unacked.entry.release();
-
// Mark message redelivered
unacked.getMessage().setRedelivered(true);
+ // Ensure message is released for redelivery
+ unacked.release();
+
// Deliver Message
- deliveryContext.deliver(unacked.entry, false);
+ deliveryContext.requeue(unacked);
// Should we allow access To the DM to directy deliver the message?
// As we don't need to check for Consumers or worry about incrementing the message count?
// unacked.queue.getDeliveryManager().deliver(_storeContext, unacked.queue.getName(), unacked.message, false);
}
+ else
+ {
+ unacked.discard(_storeContext);
+ }
}
}
@@ -549,32 +516,29 @@ public class AMQChannel
*/
public void requeue(long deliveryTag) throws AMQException
{
- UnacknowledgedMessage unacked = _unacknowledgedMessageMap.remove(deliveryTag);
+ QueueEntry unacked = _unacknowledgedMessageMap.remove(deliveryTag);
if (unacked != null)
{
+ // Mark message redelivered
+ unacked.getMessage().setRedelivered(true);
// Ensure message is released for redelivery
if (!unacked.isQueueDeleted())
{
- unacked.entry.release();
+ unacked.release();
}
- // Mark message redelivered
- unacked.getMessage().setRedelivered(true);
// Deliver these messages out of the transaction as their delivery was never
// part of the transaction only the receive.
TransactionalContext deliveryContext;
if (!(_txnContext instanceof NonTransactionalContext))
{
- // if (_nonTransactedContext == null)
- {
- _nonTransactedContext =
- new NonTransactionalContext(_messageStore, _storeContext, this, _returnMessages, _browsedAcks);
- }
- deliveryContext = _nonTransactedContext;
+ deliveryContext =
+ new NonTransactionalContext(_messageStore, _storeContext, this, _returnMessages);
+
}
else
{
@@ -584,7 +548,7 @@ public class AMQChannel
if (!unacked.isQueueDeleted())
{
// Redeliver the messages to the front of the queue
- deliveryContext.deliver(unacked.entry, true);
+ deliveryContext.requeue(unacked);
// Deliver increments the message count but we have already deliverted this once so don't increment it again
// this was because deliver did an increment changed this.
}
@@ -592,11 +556,8 @@ public class AMQChannel
{
_log.warn(System.identityHashCode(this) + " Requested requeue of message(" + unacked.getMessage().debugIdentity()
+ "):" + deliveryTag + " but no queue defined and no DeadLetter queue so DROPPING message.");
- // _log.error("Requested requeue of message:" + deliveryTag +
- // " but no queue defined using DeadLetter queue:" + getDeadLetterQueue());
- //
- // deliveryContext.deliver(unacked.message, getDeadLetterQueue(), false);
- //
+
+ unacked.discard(_storeContext);
}
}
else
@@ -604,25 +565,6 @@ public class AMQChannel
_log.warn("Requested requeue of message:" + deliveryTag + " but no such delivery tag exists."
+ _unacknowledgedMessageMap.size());
- if (_log.isDebugEnabled())
- {
- _unacknowledgedMessageMap.visit(new UnacknowledgedMessageMap.Visitor()
- {
- int count = 0;
-
- public boolean callback(UnacknowledgedMessage message) throws AMQException
- {
- _log.debug(
- (count++) + ": (" + message.getMessage().debugIdentity() + ")" + "[" + message.deliveryTag + "]");
-
- return false; // Continue
- }
-
- public void visitComplete()
- {
- }
- });
- }
}
}
@@ -636,8 +578,10 @@ public class AMQChannel
*/
public void resend(final boolean requeue) throws AMQException
{
- final List<UnacknowledgedMessage> msgToRequeue = new LinkedList<UnacknowledgedMessage>();
- final List<UnacknowledgedMessage> msgToResend = new LinkedList<UnacknowledgedMessage>();
+
+
+ final Map<Long, QueueEntry> msgToRequeue = new LinkedHashMap<Long, QueueEntry>();
+ final Map<Long, QueueEntry> msgToResend = new LinkedHashMap<Long, QueueEntry>();
if (_log.isDebugEnabled())
{
@@ -647,23 +591,25 @@ public class AMQChannel
// Process the Unacked-Map.
// Marking messages who still have a consumer for to be resent
// and those that don't to be requeued.
+
_unacknowledgedMessageMap.visit(new UnacknowledgedMessageMap.Visitor()
{
- public boolean callback(UnacknowledgedMessage message) throws AMQException
+ public boolean callback(final long deliveryTag, QueueEntry message) throws AMQException
{
- AMQShortString consumerTag = message.consumerTag;
+
AMQMessage msg = message.getMessage();
msg.setRedelivered(true);
- if (consumerTag != null)
+ final Subscription subscription = message.getDeliveredSubscription();
+ if (subscription != null)
{
// Consumer exists
- if (_consumerTag2QueueMap.containsKey(consumerTag))
+ if (!subscription.isClosed())
{
- msgToResend.add(message);
+ msgToResend.put(deliveryTag, message);
}
else // consumer has gone
{
- msgToRequeue.add(message);
+ msgToRequeue.put(deliveryTag, message);
}
}
else
@@ -675,7 +621,7 @@ public class AMQChannel
{
if (requeue)
{
- msgToRequeue.add(message);
+ msgToRequeue.put(deliveryTag, message);
}
else
{
@@ -684,7 +630,8 @@ public class AMQChannel
}
else
{
- _log.info("Message.queue is null and no DeadLetter Queue so dropping message:" + message);
+ message.discard(_storeContext);
+ _log.warn("Message.queue is null and no DeadLetter Queue so dropping message:" + message);
}
}
@@ -697,6 +644,8 @@ public class AMQChannel
}
});
+ _unacknowledgedMessageMap.clear();
+
// Process Messages to Resend
if (_log.isDebugEnabled())
{
@@ -710,9 +659,15 @@ public class AMQChannel
}
}
- for (UnacknowledgedMessage message : msgToResend)
+ for (Map.Entry<Long, QueueEntry> entry : msgToResend.entrySet())
{
+ QueueEntry message = entry.getValue();
+ long deliveryTag = entry.getKey();
+
+
+
AMQMessage msg = message.getMessage();
+ AMQQueue queue = message.getQueue();
// Our Java Client will always suspend the channel when resending!
// If the client has requested the messages be resent then it is
@@ -727,46 +682,20 @@ public class AMQChannel
// else
// {
// release to allow it to be delivered
- message.entry.release();
// Without any details from the client about what has been processed we have to mark
// all messages in the unacked map as redelivered.
msg.setRedelivered(true);
- Subscription sub = message.entry.getDeliveredSubscription();
+ Subscription sub = message.getDeliveredSubscription();
if (sub != null)
{
- // 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(!queue.resend(message, sub))
{
- if (sub.isClosed())
- {
- if (_log.isDebugEnabled())
- {
- _log.debug("Subscription(" + System.identityHashCode(sub)
- + ") closed during resend so requeuing message");
- }
- // move this message to requeue
- msgToRequeue.add(message);
- }
- else
- {
- if (_log.isDebugEnabled())
- {
- _log.debug("Requeuing " + msg.debugIdentity() + " for resend via sub:"
- + System.identityHashCode(sub));
- }
-
- sub.addToResendQueue(message.entry);
- _unacknowledgedMessageMap.remove(message.deliveryTag);
- }
- } // sync(sub.getSendLock)
+ msgToRequeue.put(deliveryTag, message);
+ }
}
else
{
@@ -777,7 +706,7 @@ public class AMQChannel
+ ")to prevent loss");
}
// move this message to requeue
- msgToRequeue.add(message);
+ msgToRequeue.put(deliveryTag, message);
}
} // for all messages
// } else !isSuspend
@@ -795,13 +724,9 @@ public class AMQChannel
TransactionalContext deliveryContext;
if (!(_txnContext instanceof NonTransactionalContext))
{
- if (_nonTransactedContext == null)
- {
- _nonTransactedContext =
- new NonTransactionalContext(_messageStore, _storeContext, this, _returnMessages, _browsedAcks);
- }
- deliveryContext = _nonTransactedContext;
+ deliveryContext =
+ new NonTransactionalContext(_messageStore, _storeContext, this, _returnMessages);
}
else
{
@@ -809,14 +734,17 @@ public class AMQChannel
}
// Process Messages to Requeue at the front of the queue
- for (UnacknowledgedMessage message : msgToRequeue)
+ for (Map.Entry<Long, QueueEntry> entry : msgToRequeue.entrySet())
{
- message.entry.release();
- message.entry.setRedelivered(true);
+ QueueEntry message = entry.getValue();
+ long deliveryTag = entry.getKey();
+
+ message.release();
+ message.setRedelivered(true);
- deliveryContext.deliver(message.entry, true);
+ deliveryContext.requeue(message);
- _unacknowledgedMessageMap.remove(message.deliveryTag);
+ _unacknowledgedMessageMap.remove(deliveryTag);
}
}
@@ -827,38 +755,47 @@ public class AMQChannel
*
* @param queue the queue that has been deleted
*
- * @throws org.apache.qpid.AMQException if there is an error processing the unacked messages
*/
- public void queueDeleted(final AMQQueue queue) throws AMQException
+ /* public void queueDeleted(final AMQQueue queue)
{
- _unacknowledgedMessageMap.visit(new UnacknowledgedMessageMap.Visitor()
+ try
{
- public boolean callback(UnacknowledgedMessage message) throws AMQException
+ _unacknowledgedMessageMap.visit(new UnacknowledgedMessageMap.Visitor()
{
- if (message.getQueue() == queue)
+ public boolean callback(UnacknowledgedMessage message)
{
- try
+ if (message.getQueue() == queue)
{
- message.discard(_storeContext);
- message.setQueueDeleted(true);
+ try
+ {
+ message.discard(_storeContext);
+ message.setQueueDeleted(true);
+ }
+ catch (AMQException e)
+ {
+ _log.error(
+ "Error decrementing ref count on message " + message.getMessage().getMessageId() + ": " + e, e);
+ throw new RuntimeException(e);
+ }
}
- catch (AMQException e)
- {
- _log.error(
- "Error decrementing ref count on message " + message.getMessage().getMessageId() + ": " + e, e);
- }
+
+ return false;
}
- return false;
- }
+ public void visitComplete()
+ {
+ }
+ });
+ }
+ catch (AMQException e)
+ {
+ _log.error("Unexpected Error while handling deletion of queue", e);
+ throw new RuntimeException(e);
+ }
- public void visitComplete()
- {
- }
- });
}
-
+*/
/**
* Acknowledge one or more messages.
*
@@ -886,7 +823,6 @@ public class AMQChannel
}
- checkSuspension();
}
/**
@@ -899,26 +835,11 @@ public class AMQChannel
return _unacknowledgedMessageMap;
}
- private void checkSuspension()
- {
- boolean suspend;
-
- suspend =
- ((_prefetch_HighWaterMark != 0) && (_unacknowledgedMessageMap.size() >= _prefetch_HighWaterMark))
- || ((_prefetchSize != 0) && (_prefetchSize < _unacknowledgedMessageMap.getUnacknowledgeBytes()));
- setSuspended(suspend);
- }
public void setSuspended(boolean suspended)
{
- boolean isSuspended = _suspended.get();
- if (isSuspended && !suspended)
- {
- // Continue being suspended if we are above the _prefetch_LowWaterMark
- suspended = _unacknowledgedMessageMap.size() > _prefetch_LowWaterMark;
- }
boolean wasSuspended = _suspended.getAndSet(suspended);
if (wasSuspended != suspended)
@@ -927,9 +848,9 @@ public class AMQChannel
{
_log.debug("Unsuspending channel " + this);
// may need to deliver queued messages
- for (AMQQueue q : _consumerTag2QueueMap.values())
+ for (Subscription s : _tag2SubscriptionMap.values())
{
- q.deliverAsync();
+ s.getQueue().deliverAsync();
}
}
else
@@ -961,12 +882,7 @@ public class AMQChannel
public String toString()
{
- StringBuilder sb = new StringBuilder(30);
- sb.append("Channel: id ").append(_channelId).append(", transaction mode: ").append(isTransactional());
- sb.append(", prefetch marks: ").append(_prefetch_LowWaterMark);
- sb.append("/").append(_prefetch_HighWaterMark);
-
- return sb.toString();
+ return "["+_session.toString()+":"+_channelId+"]";
}
public void setDefaultQueue(AMQQueue queue)
@@ -984,14 +900,14 @@ public class AMQChannel
return _storeContext;
}
- public void processReturns(AMQProtocolSession session) throws AMQException
+ public void processReturns() throws AMQException
{
if (!_returnMessages.isEmpty())
{
for (RequiredDeliveryException bouncedMessage : _returnMessages)
{
AMQMessage message = bouncedMessage.getAMQMessage();
- session.getProtocolOutputConverter().writeReturn(message, _channelId, bouncedMessage.getReplyCode().getCode(),
+ _session.getProtocolOutputConverter().writeReturn(message, _channelId, bouncedMessage.getReplyCode().getCode(),
new AMQShortString(bouncedMessage.getMessage()));
message.decrementReference(_storeContext);
@@ -1001,40 +917,39 @@ public class AMQChannel
}
}
- public boolean wouldSuspend(AMQMessage msg)
+
+ public TransactionalContext getTransactionalContext()
{
- if (isSuspended())
- {
- return true;
- }
- else
- {
- boolean willSuspend =
- ((_prefetch_HighWaterMark != 0) && ((_unacknowledgedMessageMap.size() + 1) > _prefetch_HighWaterMark));
- if (!willSuspend)
- {
- final long unackedSize = _unacknowledgedMessageMap.getUnacknowledgeBytes();
+ return _txnContext;
+ }
- willSuspend = (_prefetchSize != 0) && (unackedSize != 0) && (_prefetchSize < (msg.getSize() + unackedSize));
- }
+ public boolean isClosing()
+ {
+ return _closing;
+ }
- if (willSuspend)
- {
- setSuspended(true);
- }
+ public AMQProtocolSession getProtocolSession()
+ {
+ return _session;
+ }
- return willSuspend;
- }
+ public FlowCreditManager getCreditManager()
+ {
+ return _creditManager;
+ }
+ public void setCredit(final long prefetchSize, final int prefetchCount)
+ {
+ _creditManager.setCreditLimits(prefetchSize, prefetchCount);
}
- public TransactionalContext getTransactionalContext()
+ public List<RequiredDeliveryException> getReturnMessages()
{
- return _txnContext;
+ return _returnMessages;
}
- public boolean isClosing()
+ public MessageStore getMessageStore()
{
- return _closing;
+ return _messageStore;
}
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/RequiredDeliveryException.java b/java/broker/src/main/java/org/apache/qpid/server/RequiredDeliveryException.java
index d61bb8916a..3f1947d65a 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/RequiredDeliveryException.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/RequiredDeliveryException.java
@@ -39,19 +39,30 @@ import org.apache.qpid.server.queue.AMQMessage;
*/
public abstract class RequiredDeliveryException extends AMQException
{
- private final AMQMessage _amqMessage;
+ private AMQMessage _amqMessage;
public RequiredDeliveryException(String message, AMQMessage payload)
{
super(message);
+ setMessage(payload);
+ }
+
+
+ public RequiredDeliveryException(String message)
+ {
+ super(message);
+ }
+
+ public void setMessage(final AMQMessage payload)
+ {
+
// Increment the reference as this message is in the routing phase
// and so will have the ref decremented as routing fails.
// we need to keep this message around so we can return it in the
// handler. So increment here.
_amqMessage = payload.takeReference();
- // payload.incrementReference();
}
public AMQMessage getAMQMessage()
diff --git a/java/broker/src/main/java/org/apache/qpid/server/ack/TxAck.java b/java/broker/src/main/java/org/apache/qpid/server/ack/TxAck.java
index c62a7880a8..55d636696d 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/ack/TxAck.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/ack/TxAck.java
@@ -22,10 +22,13 @@ package org.apache.qpid.server.ack;
import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
+import java.util.HashMap;
import org.apache.qpid.AMQException;
import org.apache.qpid.server.store.StoreContext;
import org.apache.qpid.server.txn.TxnOp;
+import org.apache.qpid.server.queue.QueueEntry;
/**
* A TxnOp implementation for handling accumulated acks
@@ -33,7 +36,7 @@ import org.apache.qpid.server.txn.TxnOp;
public class TxAck implements TxnOp
{
private final UnacknowledgedMessageMap _map;
- private final List <UnacknowledgedMessage> _unacked = new LinkedList<UnacknowledgedMessage>();
+ private final Map<Long, QueueEntry> _unacked = new HashMap<Long,QueueEntry>();
private final List<Long> _individual = new LinkedList<Long>();
private long _deliveryTag;
private boolean _multiple;
@@ -47,6 +50,7 @@ public class TxAck implements TxnOp
{
if (!multiple)
{
+
//have acked a single message that is not part of
//the previously acked region so record
//individually
@@ -85,7 +89,7 @@ public class TxAck implements TxnOp
{
//if any of the messages in unacked are persistent the txn
//buffer must be marked as persistent:
- for (UnacknowledgedMessage msg : _unacked)
+ for (QueueEntry msg : _unacked.values())
{
if (msg.getMessage().isPersistent())
{
@@ -98,7 +102,7 @@ public class TxAck implements TxnOp
public void prepare(StoreContext storeContext) throws AMQException
{
//make persistent changes, i.e. dequeue and decrementReference
- for (UnacknowledgedMessage msg : _unacked)
+ for (QueueEntry msg : _unacked.values())
{
//Message has been ack so discard it. This will dequeue and decrement the reference.
msg.discard(storeContext);
@@ -112,7 +116,7 @@ public class TxAck implements TxnOp
//in memory counter) so if we failed in prepare for full
//txn, this op will have to compensate by fixing the count
//in memory (persistent changes will be rolled back by store)
- for (UnacknowledgedMessage msg : _unacked)
+ for (QueueEntry msg : _unacked.values())
{
msg.getMessage().takeReference();
}
@@ -121,7 +125,7 @@ public class TxAck implements TxnOp
public void commit(StoreContext storeContext)
{
//remove the unacked messages from the channels map
- _map.remove(_unacked);
+ _map.remove(_unacked);
}
public void rollback(StoreContext storeContext)
diff --git a/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessage.java b/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessage.java
deleted file mode 100644
index df7cecc940..0000000000
--- a/java/broker/src/main/java/org/apache/qpid/server/ack/UnacknowledgedMessage.java
+++ /dev/null
@@ -1,91 +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.ack;
-
-import org.apache.qpid.AMQException;
-import org.apache.qpid.framing.AMQShortString;
-import org.apache.qpid.server.queue.AMQMessage;
-import org.apache.qpid.server.queue.AMQQueue;
-import org.apache.qpid.server.queue.QueueEntry;
-import org.apache.qpid.server.store.StoreContext;
-
-public class UnacknowledgedMessage
-{
- public final QueueEntry entry;
- public final AMQShortString consumerTag;
- public final long deliveryTag;
-
- private boolean _queueDeleted;
-
-
- public UnacknowledgedMessage(QueueEntry entry, AMQShortString consumerTag, long deliveryTag)
- {
- this.entry = entry;
- this.consumerTag = consumerTag;
- this.deliveryTag = deliveryTag;
- }
-
- public String toString()
- {
- StringBuilder sb = new StringBuilder();
- sb.append("Q:");
- sb.append(entry.getQueue());
- sb.append(" M:");
- sb.append(entry.getMessage());
- sb.append(" CT:");
- sb.append(consumerTag);
- sb.append(" DT:");
- sb.append(deliveryTag);
-
- return sb.toString();
- }
-
- public void discard(StoreContext storeContext) throws AMQException
- {
- if (entry.getQueue() != null)
- {
- entry.getQueue().dequeue(storeContext, entry);
- }
- //if the queue is null then the message is waiting to be acked, but has been removed.
- entry.getMessage().decrementReference(storeContext);
- }
-
- public AMQMessage getMessage()
- {
- return entry.getMessage();
- }
-
- public AMQQueue getQueue()
- {
- return entry.getQueue();
- }
-
- public void setQueueDeleted(boolean queueDeleted)
- {
- _queueDeleted = queueDeleted;
- }
-
- public boolean isQueueDeleted()
- {
- return _queueDeleted;
- }
-}
-
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 b69a917081..431eeb38fc 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
@@ -23,20 +23,22 @@ package org.apache.qpid.server.ack;
import java.util.Collection;
import java.util.List;
import java.util.Set;
+import java.util.Map;
import org.apache.qpid.AMQException;
import org.apache.qpid.server.txn.TransactionalContext;
+import org.apache.qpid.server.queue.QueueEntry;
public interface UnacknowledgedMessageMap
{
public interface Visitor
{
/**
- * @param message the message being iterated over
- * @return true to stop iteration, false to continue
+ * @param deliveryTag
+ *@param message the message being iterated over @return true to stop iteration, false to continue
* @throws AMQException
*/
- boolean callback(UnacknowledgedMessage message) throws AMQException;
+ boolean callback(final long deliveryTag, QueueEntry message) throws AMQException;
void visitComplete();
}
@@ -45,19 +47,19 @@ public interface UnacknowledgedMessageMap
Object getLock();
- void add(long deliveryTag, UnacknowledgedMessage message);
+ void add(long deliveryTag, QueueEntry message);
- void collect(long deliveryTag, boolean multiple, List<UnacknowledgedMessage> msgs);
+ void collect(long deliveryTag, boolean multiple, Map<Long, QueueEntry> msgs);
boolean contains(long deliveryTag) throws AMQException;
- void remove(List<UnacknowledgedMessage> msgs);
+ void remove(Map<Long,QueueEntry> msgs);
- UnacknowledgedMessage remove(long deliveryTag);
+ QueueEntry remove(long deliveryTag);
- void drainTo(Collection<UnacknowledgedMessage> destination, long deliveryTag) throws AMQException;
+ void drainTo(Collection<QueueEntry> destination, long deliveryTag) throws AMQException;
- Collection<UnacknowledgedMessage> cancelAllMessages();
+ Collection<QueueEntry> cancelAllMessages();
void acknowledgeMessage(long deliveryTag, boolean multiple, TransactionalContext txnContext) throws AMQException;
@@ -65,7 +67,7 @@ public interface UnacknowledgedMessageMap
void clear();
- UnacknowledgedMessage get(long deliveryTag);
+ QueueEntry get(long deliveryTag);
/**
* Get the set of delivery tags that are outstanding.
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 20ee646a40..fcce6ff44e 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
@@ -31,6 +31,7 @@ import org.apache.qpid.AMQException;
import org.apache.qpid.framing.AMQShortString;
import org.apache.qpid.server.protocol.AMQProtocolSession;
import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.queue.QueueEntry;
import org.apache.qpid.server.txn.TransactionalContext;
public class UnacknowledgedMessageMapImpl implements UnacknowledgedMessageMap
@@ -39,7 +40,7 @@ public class UnacknowledgedMessageMapImpl implements UnacknowledgedMessageMap
private long _unackedSize;
- private Map<Long, UnacknowledgedMessage> _map;
+ private Map<Long, QueueEntry> _map;
private long _lastDeliveryTag;
@@ -48,16 +49,10 @@ public class UnacknowledgedMessageMapImpl implements UnacknowledgedMessageMap
public UnacknowledgedMessageMapImpl(int prefetchLimit)
{
_prefetchLimit = prefetchLimit;
- _map = new LinkedHashMap<Long, UnacknowledgedMessage>(prefetchLimit);
+ _map = new LinkedHashMap<Long, QueueEntry>(prefetchLimit);
}
- /*public UnacknowledgedMessageMapImpl(Object lock, Map<Long, UnacknowledgedMessage> map)
- {
- _lock = lock;
- _map = map;
- } */
-
- public void collect(long deliveryTag, boolean multiple, List<UnacknowledgedMessage> msgs)
+ public void collect(long deliveryTag, boolean multiple, Map<Long, QueueEntry> msgs)
{
if (multiple)
{
@@ -65,7 +60,7 @@ public class UnacknowledgedMessageMapImpl implements UnacknowledgedMessageMap
}
else
{
- msgs.add(get(deliveryTag));
+ msgs.put(deliveryTag, get(deliveryTag));
}
}
@@ -78,26 +73,27 @@ public class UnacknowledgedMessageMapImpl implements UnacknowledgedMessageMap
}
}
- public void remove(List<UnacknowledgedMessage> msgs)
+ public void remove(Map<Long,QueueEntry> msgs)
{
synchronized (_lock)
{
- for (UnacknowledgedMessage msg : msgs)
+ for (Long deliveryTag : msgs.keySet())
{
- remove(msg.deliveryTag);
+ remove(deliveryTag);
}
}
}
- public UnacknowledgedMessage remove(long deliveryTag)
+ public QueueEntry remove(long deliveryTag)
{
synchronized (_lock)
{
- UnacknowledgedMessage message = _map.remove(deliveryTag);
+ QueueEntry message = _map.remove(deliveryTag);
if(message != null)
{
_unackedSize -= message.getMessage().getSize();
+ message.restoreCredit();
}
return message;
@@ -108,10 +104,10 @@ public class UnacknowledgedMessageMapImpl implements UnacknowledgedMessageMap
{
synchronized (_lock)
{
- Collection<UnacknowledgedMessage> currentEntries = _map.values();
- for (UnacknowledgedMessage msg : currentEntries)
+ Set<Map.Entry<Long, QueueEntry>> currentEntries = _map.entrySet();
+ for (Map.Entry<Long, QueueEntry> entry : currentEntries)
{
- visitor.callback(msg);
+ visitor.callback(entry.getKey().longValue(), entry.getValue());
}
visitor.visitComplete();
}
@@ -122,7 +118,7 @@ public class UnacknowledgedMessageMapImpl implements UnacknowledgedMessageMap
return _lock;
}
- public void add(long deliveryTag, UnacknowledgedMessage message)
+ public void add(long deliveryTag, QueueEntry message)
{
synchronized (_lock)
{
@@ -132,12 +128,12 @@ public class UnacknowledgedMessageMapImpl implements UnacknowledgedMessageMap
}
}
- public Collection<UnacknowledgedMessage> cancelAllMessages()
+ public Collection<QueueEntry> cancelAllMessages()
{
synchronized (_lock)
{
- Collection<UnacknowledgedMessage> currentEntries = _map.values();
- _map = new LinkedHashMap<Long, UnacknowledgedMessage>(_prefetchLimit);
+ Collection<QueueEntry> currentEntries = _map.values();
+ _map = new LinkedHashMap<Long, QueueEntry>(_prefetchLimit);
_unackedSize = 0l;
return currentEntries;
}
@@ -169,14 +165,14 @@ public class UnacknowledgedMessageMapImpl implements UnacknowledgedMessageMap
}
}
- public void drainTo(Collection<UnacknowledgedMessage> destination, long deliveryTag) throws AMQException
+ public void drainTo(Collection<QueueEntry> destination, long deliveryTag) throws AMQException
{
synchronized (_lock)
{
- Iterator<Map.Entry<Long, UnacknowledgedMessage>> it = _map.entrySet().iterator();
+ Iterator<Map.Entry<Long, QueueEntry>> it = _map.entrySet().iterator();
while (it.hasNext())
{
- Map.Entry<Long, UnacknowledgedMessage> unacked = it.next();
+ Map.Entry<Long, QueueEntry> unacked = it.next();
if (unacked.getKey() > deliveryTag)
{
@@ -184,10 +180,13 @@ public class UnacknowledgedMessageMapImpl implements UnacknowledgedMessageMap
throw new AMQException("UnacknowledgedMessageMap is out of order:" + unacked.getKey() +
" When deliveryTag is:" + deliveryTag + "ES:" + _map.entrySet().toString());
}
-
it.remove();
+
_unackedSize -= unacked.getValue().getMessage().getSize();
+ unacked.getValue().restoreCredit();
+
+
destination.add(unacked.getValue());
if (unacked.getKey() == deliveryTag)
{
@@ -197,7 +196,7 @@ public class UnacknowledgedMessageMapImpl implements UnacknowledgedMessageMap
}
}
- public UnacknowledgedMessage get(long key)
+ public QueueEntry get(long key)
{
synchronized (_lock)
{
@@ -213,13 +212,13 @@ public class UnacknowledgedMessageMapImpl implements UnacknowledgedMessageMap
}
}
- private void collect(long key, List<UnacknowledgedMessage> msgs)
+ private void collect(long key, Map<Long, QueueEntry> msgs)
{
synchronized (_lock)
{
- for (Map.Entry<Long, UnacknowledgedMessage> entry : _map.entrySet())
+ for (Map.Entry<Long, QueueEntry> entry : _map.entrySet())
{
- msgs.add(entry.getValue());
+ msgs.put(entry.getKey(),entry.getValue());
if (entry.getKey() == key)
{
break;
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 8573902af4..c9598436e0 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
@@ -35,6 +35,7 @@ import org.apache.qpid.server.exchange.ExchangeRegistry;
import org.apache.qpid.server.exchange.ExchangeFactory;
import org.apache.qpid.server.queue.AMQQueue;
import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.queue.AMQQueueFactory;
import org.apache.qpid.server.registry.ApplicationRegistry;
import org.apache.qpid.server.store.MessageStore;
import org.apache.qpid.server.virtualhost.VirtualHost;
@@ -177,7 +178,7 @@ public class VirtualHostConfiguration
boolean autodelete = queueConfiguration.getBoolean("autodelete", false);
String owner = queueConfiguration.getString("owner", null);
- queue = new AMQQueue(queueName,
+ queue = AMQQueueFactory.createAMQQueueImpl(queueName,
durable,
owner == null ? null : new AMQShortString(owner) /* These queues will have no owner */,
autodelete /* Therefore autodelete makes no sence */, virtualHost);
@@ -221,7 +222,7 @@ public class VirtualHostConfiguration
AMQShortString routingKey = new AMQShortString(String.valueOf(routingKeyNameObj));
- queue.bind(routingKey, null, exchange);
+ queue.bind(exchange, routingKey, null);
_logger.info("Queue '" + queue.getName() + "' bound to exchange:" + exchangeName + " RK:'" + routingKey + "'");
@@ -229,7 +230,7 @@ public class VirtualHostConfiguration
if(exchange != virtualHost.getExchangeRegistry().getDefaultExchange())
{
- queue.bind(queue.getName(), null, virtualHost.getExchangeRegistry().getDefaultExchange());
+ queue.bind(virtualHost.getExchangeRegistry().getDefaultExchange(), queue.getName(), null);
}
}
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 9ebb893362..72286cb387 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,6 +38,7 @@ 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;
diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeFactory.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeFactory.java
index 1a9dc6673a..cc6a6a3d5b 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeFactory.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeFactory.java
@@ -43,8 +43,8 @@ public class DefaultExchangeFactory implements ExchangeFactory
public DefaultExchangeFactory(VirtualHost host)
{
_host = host;
- registerExchangeType(DestNameExchange.TYPE);
- registerExchangeType(DestWildExchange.TYPE);
+ registerExchangeType(DirectExchange.TYPE);
+ registerExchangeType(TopicExchange.TYPE);
registerExchangeType(HeadersExchange.TYPE);
registerExchangeType(FanoutExchange.TYPE);
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeRegistry.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeRegistry.java
index 98abf7977a..0ab8208d88 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeRegistry.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/DefaultExchangeRegistry.java
@@ -25,6 +25,7 @@ import org.apache.qpid.AMQException;
import org.apache.qpid.framing.AMQShortString;
import org.apache.qpid.server.protocol.ExchangeInitialiser;
import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.queue.IncomingMessage;
import org.apache.qpid.server.store.MessageStore;
import org.apache.qpid.server.virtualhost.VirtualHost;
@@ -121,9 +122,9 @@ public class DefaultExchangeRegistry implements ExchangeRegistry
* @param payload
* @throws AMQException if something goes wrong delivering data
*/
- public void routeContent(AMQMessage payload) throws AMQException
+ public void routeContent(IncomingMessage payload) throws AMQException
{
- final AMQShortString exchange = payload.getMessagePublishInfo().getExchange();
+ final AMQShortString exchange = payload.getExchange();
final Exchange exch = getExchange(exchange);
// there is a small window of opportunity for the exchange to be deleted in between
// the BasicPublish being received (where the exchange is validated) and the final
diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/DestNameExchange.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/DirectExchange.java
index 12347c0278..5dcc2cf143 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/exchange/DestNameExchange.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/DirectExchange.java
@@ -38,23 +38,22 @@ import org.apache.qpid.protocol.AMQConstant;
import org.apache.qpid.exchange.ExchangeDefaults;
import org.apache.qpid.framing.AMQShortString;
import org.apache.qpid.framing.FieldTable;
-import org.apache.qpid.framing.abstraction.MessagePublishInfo;
import org.apache.qpid.server.management.MBeanConstructor;
import org.apache.qpid.server.management.MBeanDescription;
-import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.queue.IncomingMessage;
import org.apache.qpid.server.queue.AMQQueue;
import org.apache.qpid.server.virtualhost.VirtualHost;
-public class DestNameExchange extends AbstractExchange
+public class DirectExchange extends AbstractExchange
{
- private static final Logger _logger = Logger.getLogger(DestNameExchange.class);
+ private static final Logger _logger = Logger.getLogger(DirectExchange.class);
/**
* Maps from queue name to queue instances
*/
private final Index _index = new Index();
- public static final ExchangeType<DestNameExchange> TYPE = new ExchangeType<DestNameExchange>()
+ public static final ExchangeType<DirectExchange> TYPE = new ExchangeType<DirectExchange>()
{
public AMQShortString getName()
@@ -62,18 +61,18 @@ public class DestNameExchange extends AbstractExchange
return ExchangeDefaults.DIRECT_EXCHANGE_CLASS;
}
- public Class<DestNameExchange> getExchangeClass()
+ public Class<DirectExchange> getExchangeClass()
{
- return DestNameExchange.class;
+ return DirectExchange.class;
}
- public DestNameExchange newInstance(VirtualHost host,
+ public DirectExchange newInstance(VirtualHost host,
AMQShortString name,
boolean durable,
int ticket,
boolean autoDelete) throws AMQException
{
- DestNameExchange exch = new DestNameExchange();
+ DirectExchange exch = new DirectExchange();
exch.initialise(host,name,durable,ticket,autoDelete);
return exch;
}
@@ -88,10 +87,10 @@ public class DestNameExchange extends AbstractExchange
* MBean class implementing the management interfaces.
*/
@MBeanDescription("Management Bean for Direct Exchange")
- private final class DestNameExchangeMBean extends ExchangeMBean
+ private final class DirectExchangeMBean extends ExchangeMBean
{
@MBeanConstructor("Creates an MBean for AMQ direct exchange")
- public DestNameExchangeMBean() throws JMException
+ public DirectExchangeMBean() throws JMException
{
super();
_exchangeType = "direct";
@@ -132,7 +131,7 @@ public class DestNameExchange extends AbstractExchange
try
{
- queue.bind(new AMQShortString(binding), null, DestNameExchange.this);
+ queue.bind(DirectExchange.this, new AMQShortString(binding), null);
}
catch (AMQException ex)
{
@@ -147,7 +146,7 @@ public class DestNameExchange extends AbstractExchange
{
try
{
- return new DestNameExchangeMBean();
+ return new DirectExchangeMBean();
}
catch (JMException ex)
{
@@ -187,35 +186,21 @@ public class DestNameExchange extends AbstractExchange
}
}
- public void route(AMQMessage payload) throws AMQException
+ public void route(IncomingMessage payload) throws AMQException
{
- final MessagePublishInfo info = payload.getMessagePublishInfo();
- final AMQShortString routingKey = info.getRoutingKey() == null ? AMQShortString.EMPTY_STRING : info.getRoutingKey();
+
+ final AMQShortString routingKey = payload.getRoutingKey() == null ? AMQShortString.EMPTY_STRING : payload.getRoutingKey();
+
final List<AMQQueue> queues = (routingKey == null) ? null : _index.get(routingKey);
- if (queues == null || queues.isEmpty())
+
+ if (_logger.isDebugEnabled())
{
- String msg = "Routing key " + routingKey + " is not known to " + this;
- if (info.isMandatory() || info.isImmediate())
- {
- throw new NoRouteException(msg, payload);
- }
- else
- {
- _logger.error("MESSAGE LOSS: Message should be sent on a Dead Letter Queue");
- _logger.warn(msg);
- }
+ _logger.debug("Publishing message to queue " + queues);
}
- else
- {
- if (_logger.isDebugEnabled())
- {
- _logger.debug("Publishing message to queue " + queues);
- }
payload.enqueue(queues);
- }
}
public boolean isBound(AMQShortString routingKey, FieldTable arguments, AMQQueue queue)
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 37cd85a8f8..31c66694ec 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
@@ -23,7 +23,8 @@ package org.apache.qpid.server.exchange;
import org.apache.qpid.AMQException;
import org.apache.qpid.framing.AMQShortString;
import org.apache.qpid.framing.FieldTable;
-import org.apache.qpid.server.queue.AMQMessage;
+
+import org.apache.qpid.server.queue.IncomingMessage;
import org.apache.qpid.server.queue.AMQQueue;
import org.apache.qpid.server.virtualhost.VirtualHost;
@@ -53,7 +54,7 @@ public interface Exchange
void deregisterQueue(AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQException;
- void route(AMQMessage message) throws AMQException;
+ void route(IncomingMessage message) throws AMQException;
/**
diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java
index e7c887f306..e9fd4d548b 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/FanoutExchange.java
@@ -1,240 +1,224 @@
-/*
- *
- * 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.exchange;
-
-import org.apache.log4j.Logger;
-import org.apache.qpid.AMQException;
-import org.apache.qpid.protocol.AMQConstant;
-import org.apache.qpid.exchange.ExchangeDefaults;
-import org.apache.qpid.framing.AMQShortString;
-import org.apache.qpid.framing.FieldTable;
-import org.apache.qpid.framing.abstraction.MessagePublishInfo;
-import org.apache.qpid.server.management.MBeanConstructor;
-import org.apache.qpid.server.management.MBeanDescription;
-import org.apache.qpid.server.queue.AMQMessage;
-import org.apache.qpid.server.queue.AMQQueue;
-import org.apache.qpid.server.virtualhost.VirtualHost;
-
-import javax.management.JMException;
-import javax.management.MBeanException;
-import javax.management.openmbean.CompositeData;
-import javax.management.openmbean.CompositeDataSupport;
-import javax.management.openmbean.OpenDataException;
-import javax.management.openmbean.TabularData;
-import javax.management.openmbean.TabularDataSupport;
-import java.util.List;
-import java.util.Map;
-import java.util.ArrayList;
-import java.util.concurrent.CopyOnWriteArraySet;
-
-public class FanoutExchange extends AbstractExchange
-{
- private static final Logger _logger = Logger.getLogger(FanoutExchange.class);
-
- /**
- * Maps from queue name to queue instances
- */
- private final CopyOnWriteArraySet<AMQQueue> _queues = new CopyOnWriteArraySet<AMQQueue>();
-
- /**
- * MBean class implementing the management interfaces.
- */
- @MBeanDescription("Management Bean for Fanout Exchange")
- private final class FanoutExchangeMBean extends ExchangeMBean
- {
- @MBeanConstructor("Creates an MBean for AMQ fanout exchange")
- public FanoutExchangeMBean() throws JMException
- {
- super();
- _exchangeType = "fanout";
- init();
- }
-
- public TabularData bindings() throws OpenDataException
- {
-
- _bindingList = new TabularDataSupport(_bindinglistDataType);
-
- for (AMQQueue queue : _queues)
- {
- String queueName = queue.getName().toString();
-
- Object[] bindingItemValues = {queueName, new String[]{queueName}};
- CompositeData bindingData = new CompositeDataSupport(_bindingDataType, _bindingItemNames, bindingItemValues);
- _bindingList.put(bindingData);
- }
-
- return _bindingList;
- }
-
- public void createNewBinding(String queueName, String binding) throws JMException
- {
- AMQQueue queue = getQueueRegistry().getQueue(new AMQShortString(queueName));
- if (queue == null)
- {
- throw new JMException("Queue \"" + queueName + "\" is not registered with the exchange.");
- }
-
- try
- {
- queue.bind(new AMQShortString(binding), null, FanoutExchange.this);
- }
- catch (AMQException ex)
- {
- throw new MBeanException(ex);
- }
- }
-
- } // End of MBean class
-
- protected ExchangeMBean createMBean() throws AMQException
- {
- try
- {
- return new FanoutExchange.FanoutExchangeMBean();
- }
- catch (JMException ex)
- {
- _logger.error("Exception occured in creating the direct exchange mbean", ex);
- throw new AMQException("Exception occured in creating the direct exchange mbean", ex);
- }
- }
-
- public static final ExchangeType<FanoutExchange> TYPE = new ExchangeType<FanoutExchange>()
- {
-
- public AMQShortString getName()
- {
- return ExchangeDefaults.FANOUT_EXCHANGE_CLASS;
- }
-
- public Class<FanoutExchange> getExchangeClass()
- {
- return FanoutExchange.class;
- }
-
- public FanoutExchange newInstance(VirtualHost host,
- AMQShortString name,
- boolean durable,
- int ticket,
- boolean autoDelete) throws AMQException
- {
- FanoutExchange exch = new FanoutExchange();
- exch.initialise(host, name, durable, ticket, autoDelete);
- return exch;
- }
-
- public AMQShortString getDefaultExchangeName()
- {
- return ExchangeDefaults.FANOUT_EXCHANGE_NAME;
- }
- };
-
- public Map<AMQShortString, List<AMQQueue>> getBindings()
- {
- return null;
- }
-
- public AMQShortString getType()
- {
- return ExchangeDefaults.FANOUT_EXCHANGE_CLASS;
- }
-
- public void registerQueue(AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQException
- {
- assert queue != null;
-
- if (_queues.contains(queue))
- {
- _logger.debug("Queue " + queue + " is already registered");
- }
- else
- {
- _queues.add(queue);
- _logger.debug("Binding queue " + queue + " with routing key " + routingKey + " to exchange " + this);
- }
- }
-
- public void deregisterQueue(AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQException
- {
- assert queue != null;
-
- if (!_queues.remove(queue))
- {
- throw new AMQException(AMQConstant.NOT_FOUND, "Queue " + queue + " was not registered with exchange " + this.getName() + ". ");
- }
- }
-
- public void route(AMQMessage payload) throws AMQException
- {
- final MessagePublishInfo publishInfo = payload.getMessagePublishInfo();
- final AMQShortString routingKey = publishInfo.getRoutingKey();
- if ((_queues == null) || _queues.isEmpty())
- {
- String msg = "No queues bound to " + this;
- if (publishInfo.isMandatory() || publishInfo.isImmediate())
- {
- throw new NoRouteException(msg, payload);
- }
- else
- {
- _logger.warn(msg);
- }
- }
- else
- {
- if (_logger.isDebugEnabled())
- {
- _logger.debug("Publishing message to queue " + _queues);
- }
-
- payload.enqueue(new ArrayList(_queues));
-
- }
- }
-
- public boolean isBound(AMQShortString routingKey, FieldTable arguments, AMQQueue queue)
- {
- return isBound(routingKey, queue);
- }
-
- public boolean isBound(AMQShortString routingKey, AMQQueue queue)
- {
- return _queues.contains(queue);
- }
-
- public boolean isBound(AMQShortString routingKey)
- {
-
- return (_queues != null) && !_queues.isEmpty();
- }
-
- public boolean isBound(AMQQueue queue)
- {
-
- return _queues.contains(queue);
- }
-
- public boolean hasBindings()
- {
- return !_queues.isEmpty();
- }
-}
+/*
+ *
+ * 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.exchange;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.protocol.AMQConstant;
+import org.apache.qpid.exchange.ExchangeDefaults;
+import org.apache.qpid.framing.AMQShortString;
+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.IncomingMessage;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+
+import javax.management.JMException;
+import javax.management.MBeanException;
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.CompositeDataSupport;
+import javax.management.openmbean.OpenDataException;
+import javax.management.openmbean.TabularData;
+import javax.management.openmbean.TabularDataSupport;
+import java.util.List;
+import java.util.Map;
+import java.util.ArrayList;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+public class FanoutExchange extends AbstractExchange
+{
+ private static final Logger _logger = Logger.getLogger(FanoutExchange.class);
+
+ /**
+ * Maps from queue name to queue instances
+ */
+ private final CopyOnWriteArraySet<AMQQueue> _queues = new CopyOnWriteArraySet<AMQQueue>();
+
+ /**
+ * MBean class implementing the management interfaces.
+ */
+ @MBeanDescription("Management Bean for Fanout Exchange")
+ private final class FanoutExchangeMBean extends ExchangeMBean
+ {
+ @MBeanConstructor("Creates an MBean for AMQ fanout exchange")
+ public FanoutExchangeMBean() throws JMException
+ {
+ super();
+ _exchangeType = "fanout";
+ init();
+ }
+
+ public TabularData bindings() throws OpenDataException
+ {
+
+ _bindingList = new TabularDataSupport(_bindinglistDataType);
+
+ for (AMQQueue queue : _queues)
+ {
+ String queueName = queue.getName().toString();
+
+ Object[] bindingItemValues = {queueName, new String[]{queueName}};
+ CompositeData bindingData = new CompositeDataSupport(_bindingDataType, _bindingItemNames, bindingItemValues);
+ _bindingList.put(bindingData);
+ }
+
+ return _bindingList;
+ }
+
+ public void createNewBinding(String queueName, String binding) throws JMException
+ {
+ AMQQueue queue = getQueueRegistry().getQueue(new AMQShortString(queueName));
+ if (queue == null)
+ {
+ throw new JMException("Queue \"" + queueName + "\" is not registered with the exchange.");
+ }
+
+ try
+ {
+ queue.bind(FanoutExchange.this, new AMQShortString(binding), null);
+ }
+ catch (AMQException ex)
+ {
+ throw new MBeanException(ex);
+ }
+ }
+
+ } // End of MBean class
+
+ protected ExchangeMBean createMBean() throws AMQException
+ {
+ try
+ {
+ return new FanoutExchange.FanoutExchangeMBean();
+ }
+ catch (JMException ex)
+ {
+ _logger.error("Exception occured in creating the direct exchange mbean", ex);
+ throw new AMQException("Exception occured in creating the direct exchange mbean", ex);
+ }
+ }
+
+ public static final ExchangeType<FanoutExchange> TYPE = new ExchangeType<FanoutExchange>()
+ {
+
+ public AMQShortString getName()
+ {
+ return ExchangeDefaults.FANOUT_EXCHANGE_CLASS;
+ }
+
+ public Class<FanoutExchange> getExchangeClass()
+ {
+ return FanoutExchange.class;
+ }
+
+ public FanoutExchange newInstance(VirtualHost host,
+ AMQShortString name,
+ boolean durable,
+ int ticket,
+ boolean autoDelete) throws AMQException
+ {
+ FanoutExchange exch = new FanoutExchange();
+ exch.initialise(host, name, durable, ticket, autoDelete);
+ return exch;
+ }
+
+ public AMQShortString getDefaultExchangeName()
+ {
+ return ExchangeDefaults.FANOUT_EXCHANGE_NAME;
+ }
+ };
+
+ public Map<AMQShortString, List<AMQQueue>> getBindings()
+ {
+ return null;
+ }
+
+ public AMQShortString getType()
+ {
+ return ExchangeDefaults.FANOUT_EXCHANGE_CLASS;
+ }
+
+ public void registerQueue(AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQException
+ {
+ assert queue != null;
+
+ if (_queues.contains(queue))
+ {
+ _logger.debug("Queue " + queue + " is already registered");
+ }
+ else
+ {
+ _queues.add(queue);
+ _logger.debug("Binding queue " + queue + " with routing key " + routingKey + " to exchange " + this);
+ }
+ }
+
+ public void deregisterQueue(AMQShortString routingKey, AMQQueue queue, FieldTable args) throws AMQException
+ {
+ assert queue != null;
+
+ if (!_queues.remove(queue))
+ {
+ throw new AMQException(AMQConstant.NOT_FOUND, "Queue " + queue + " was not registered with exchange " + this.getName() + ". ");
+ }
+ }
+
+ public void route(IncomingMessage payload) throws AMQException
+ {
+
+
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Publishing message to queue " + _queues);
+ }
+
+ payload.enqueue(new ArrayList(_queues));
+
+ }
+
+ public boolean isBound(AMQShortString routingKey, FieldTable arguments, AMQQueue queue)
+ {
+ return isBound(routingKey, queue);
+ }
+
+ public boolean isBound(AMQShortString routingKey, AMQQueue queue)
+ {
+ return _queues.contains(queue);
+ }
+
+ public boolean isBound(AMQShortString routingKey)
+ {
+
+ return (_queues != null) && !_queues.isEmpty();
+ }
+
+ public boolean isBound(AMQQueue queue)
+ {
+
+ return _queues.contains(queue);
+ }
+
+ public boolean hasBindings()
+ {
+ return !_queues.isEmpty();
+ }
+}
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 68ad88c4cb..8d97e9ef25 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,8 @@ 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.AMQMessage;
+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;
@@ -240,7 +241,7 @@ public class HeadersExchange extends AbstractExchange
}
}
- public void route(AMQMessage payload) throws AMQException
+ public void route(IncomingMessage payload) throws AMQException
{
FieldTable headers = getHeaders(payload.getContentHeaderBody());
if (_logger.isDebugEnabled())
@@ -261,21 +262,6 @@ public class HeadersExchange extends AbstractExchange
routed = true;
}
}
- if (!routed)
- {
-
- String msg = "Exchange " + getName() + ": message not routable.";
-
- if (payload.getMessagePublishInfo().isMandatory() || payload.getMessagePublishInfo().isImmediate())
- {
- throw new NoRouteException(msg, payload);
- }
- else
- {
- _logger.warn(msg);
- }
-
- }
}
public boolean isBound(AMQShortString routingKey, FieldTable arguments, AMQQueue queue)
diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/Index.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/Index.java
index eacdad8a8e..4f1f550e94 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/exchange/Index.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/Index.java
@@ -32,7 +32,7 @@ import org.apache.qpid.server.queue.AMQQueue;
/**
* An index of queues against routing key. Allows multiple queues to be stored
- * against the same key. Used in the DestNameExchange.
+ * against the same key. Used in the DirectExchange.
*/
class Index
{
diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/MessageRouter.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/MessageRouter.java
index 7508e80f7f..db9beb6da7 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/exchange/MessageRouter.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/MessageRouter.java
@@ -22,6 +22,7 @@ package org.apache.qpid.server.exchange;
import org.apache.qpid.AMQException;
import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.queue.IncomingMessage;
/**
* Separated out from the ExchangeRegistry interface to allow components
@@ -36,5 +37,5 @@ public interface MessageRouter
*
* @throws org.apache.qpid.AMQException if something goes wrong delivering data
*/
- void routeContent(AMQMessage message) throws AMQException;
+ void routeContent(IncomingMessage message) throws AMQException;
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/NoRouteException.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/NoRouteException.java
index 1d6ab3842d..d18ad7ab14 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/exchange/NoRouteException.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/NoRouteException.java
@@ -23,6 +23,7 @@ package org.apache.qpid.server.exchange;
import org.apache.qpid.protocol.AMQConstant;
import org.apache.qpid.server.RequiredDeliveryException;
import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.queue.IncomingMessage;
/**
* NoRouteException is a {@link RequiredDeliveryException} that represents the failure case where a manadatory message
@@ -36,9 +37,9 @@ import org.apache.qpid.server.queue.AMQMessage;
*/
public class NoRouteException extends RequiredDeliveryException
{
- public NoRouteException(String msg, AMQMessage message)
+ public NoRouteException(String msg, AMQMessage amqMessage)
{
- super(msg, message);
+ super(msg, amqMessage);
}
public AMQConstant getReplyCode()
diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/DestWildExchange.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/TopicExchange.java
index 6fa3686152..3ade1ee7f0 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/exchange/DestWildExchange.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/TopicExchange.java
@@ -27,10 +27,9 @@ import org.apache.qpid.exchange.ExchangeDefaults;
import org.apache.qpid.framing.AMQShortString;
import org.apache.qpid.framing.FieldTable;
import org.apache.qpid.framing.AMQShortStringTokenizer;
-import org.apache.qpid.framing.abstraction.MessagePublishInfo;
import org.apache.qpid.server.management.MBeanConstructor;
import org.apache.qpid.server.management.MBeanDescription;
-import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.queue.IncomingMessage;
import org.apache.qpid.server.queue.AMQQueue;
import org.apache.qpid.server.virtualhost.VirtualHost;
@@ -45,10 +44,10 @@ import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
-public class DestWildExchange extends AbstractExchange
+public class TopicExchange extends AbstractExchange
{
- public static final ExchangeType<DestWildExchange> TYPE = new ExchangeType<DestWildExchange>()
+ public static final ExchangeType<TopicExchange> TYPE = new ExchangeType<TopicExchange>()
{
public AMQShortString getName()
@@ -56,18 +55,18 @@ public class DestWildExchange extends AbstractExchange
return ExchangeDefaults.TOPIC_EXCHANGE_CLASS;
}
- public Class<DestWildExchange> getExchangeClass()
+ public Class<TopicExchange> getExchangeClass()
{
- return DestWildExchange.class;
+ return TopicExchange.class;
}
- public DestWildExchange newInstance(VirtualHost host,
+ public TopicExchange newInstance(VirtualHost host,
AMQShortString name,
boolean durable,
int ticket,
boolean autoDelete) throws AMQException
{
- DestWildExchange exch = new DestWildExchange();
+ TopicExchange exch = new TopicExchange();
exch.initialise(host, name, durable, ticket, autoDelete);
return exch;
}
@@ -79,7 +78,7 @@ public class DestWildExchange extends AbstractExchange
};
- private static final Logger _logger = Logger.getLogger(DestWildExchange.class);
+ private static final Logger _logger = Logger.getLogger(TopicExchange.class);
private final ConcurrentHashMap<AMQShortString, List<AMQQueue>> _bindingKey2queues =
new ConcurrentHashMap<AMQShortString, List<AMQQueue>>();
@@ -87,7 +86,7 @@ public class DestWildExchange extends AbstractExchange
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("*");
@@ -97,12 +96,12 @@ public class DestWildExchange extends AbstractExchange
private static final byte HASH_BYTE = (byte)'#';
private static final byte STAR_BYTE = (byte)'*';
- /** DestWildExchangeMBean class implements the management interface for the Topic exchanges. */
+ /** TopicExchangeMBean class implements the management interface for the Topic exchanges. */
@MBeanDescription("Management Bean for Topic Exchange")
- private final class DestWildExchangeMBean extends ExchangeMBean
+ private final class TopicExchangeMBean extends ExchangeMBean
{
@MBeanConstructor("Creates an MBean for AMQ topic exchange")
- public DestWildExchangeMBean() throws JMException
+ public TopicExchangeMBean() throws JMException
{
super();
_exchangeType = "topic";
@@ -124,7 +123,7 @@ public class DestWildExchange extends AbstractExchange
queueList.add(q.getName().toString());
}
- Object[] bindingItemValues = {key.toString(), queueList.toArray(new String[0])};
+ Object[] bindingItemValues = {key.toString(), queueList.toArray(new String[queueList.size()])};
CompositeData bindingData = new CompositeDataSupport(_bindingDataType, _bindingItemNames, bindingItemValues);
_bindingList.put(bindingData);
}
@@ -142,7 +141,7 @@ public class DestWildExchange extends AbstractExchange
try
{
- queue.bind(new AMQShortString(binding), null, DestWildExchange.this);
+ queue.bind(TopicExchange.this, new AMQShortString(binding), null);
}
catch (AMQException ex)
{
@@ -280,33 +279,30 @@ public class DestWildExchange 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;
}
- public void route(AMQMessage payload) throws AMQException
+ public void route(IncomingMessage payload) throws AMQException
{
- MessagePublishInfo info = payload.getMessagePublishInfo();
- final AMQShortString routingKey = info.getRoutingKey();
+ final AMQShortString routingKey = payload.getRoutingKey();
List<AMQQueue> queues = getMatchedQueues(routingKey);
- // if we have no registered queues we have nothing to do
- // TODO: add support for the immediate flag
- if ((queues == null) || queues.isEmpty())
- {
- if (info.isMandatory() || info.isImmediate())
- {
- String msg = "Topic " + routingKey + " is not known to " + this;
- throw new NoRouteException(msg, payload);
- }
- else
- {
- _logger.warn("No queues found for routing key " + routingKey);
- _logger.warn("Routing map contains: " + _bindingKey2queues);
- return;
- }
+ if(queues == null || queues.isEmpty())
+ {
+ _logger.info("Message routing key: " + payload.getRoutingKey() + " No routes - " + _bindingKey2queues);
}
payload.enqueue(queues);
@@ -407,7 +403,7 @@ public class DestWildExchange extends AbstractExchange
{
try
{
- return new DestWildExchangeMBean();
+ return new TopicExchangeMBean();
}
catch (JMException ex)
{
@@ -450,10 +446,18 @@ public class DestWildExchange extends AbstractExchange
{
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())
{
diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeaderKey.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeaderKey.java
new file mode 100644
index 0000000000..8fdb91cbef
--- /dev/null
+++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeaderKey.java
@@ -0,0 +1,40 @@
+package org.apache.qpid.server.exchange.headers;
+
+import org.apache.qpid.framing.AMQShortString;
+
+/*
+*
+* 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 HeaderKey
+{
+ public static final HeaderKey UNKNOWN = new HeaderKey(new AMQShortString("<< UNKNOWN >>"));
+ private AMQShortString _key;
+
+ public HeaderKey(final AMQShortString key)
+ {
+ _key = key;
+ }
+
+ public String toString()
+ {
+ return _key.toString();
+ }
+
+}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeaderKeyDictionary.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeaderKeyDictionary.java
new file mode 100644
index 0000000000..7be99a88c9
--- /dev/null
+++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeaderKeyDictionary.java
@@ -0,0 +1,50 @@
+package org.apache.qpid.server.exchange.headers;
+
+import org.apache.qpid.framing.AMQShortString;
+
+import java.util.Map;
+import java.util.HashMap;
+
+/*
+*
+* 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 HeaderKeyDictionary
+{
+
+ private final Map<AMQShortString, HeaderKey> _dictionary = new HashMap<AMQShortString, HeaderKey>();
+
+
+ public HeaderKey get(final AMQShortString key)
+ {
+ HeaderKey headerKey = _dictionary.get(key);
+ return headerKey == null ? HeaderKey.UNKNOWN : headerKey;
+ }
+
+ public HeaderKey getOrCreate(final AMQShortString key)
+ {
+ HeaderKey headerKey = _dictionary.get(key);
+ if(headerKey == null)
+ {
+ headerKey = new HeaderKey(key);
+ _dictionary.put(key, headerKey);
+ }
+ return headerKey;
+ }
+}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeaderMatcherResult.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeaderMatcherResult.java
new file mode 100644
index 0000000000..518064bb29
--- /dev/null
+++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeaderMatcherResult.java
@@ -0,0 +1,25 @@
+package org.apache.qpid.server.exchange.headers;
+
+/*
+*
+* 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 HeaderMatcherResult
+{
+}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeadersMatcherDFAState.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeadersMatcherDFAState.java
new file mode 100644
index 0000000000..9da93d483a
--- /dev/null
+++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeadersMatcherDFAState.java
@@ -0,0 +1,339 @@
+package org.apache.qpid.server.exchange.headers;
+
+import org.apache.qpid.framing.AMQTypedValue;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.FieldTable;
+import org.apache.qpid.server.exchange.topic.TopicMatcherDFAState;
+import org.apache.qpid.server.exchange.topic.TopicWord;
+import org.apache.qpid.server.exchange.topic.TopicMatcherResult;
+
+import java.util.*;
+
+/*
+*
+* 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 HeadersMatcherDFAState
+{
+
+
+ private final Collection<HeaderMatcherResult> _results;
+ private final Map<HeaderKey, Map<AMQTypedValue,HeadersMatcherDFAState>> _nextStateMap;
+ private final HeaderKeyDictionary _dictionary;
+
+ public HeadersMatcherDFAState(Map<HeaderKey, Map<AMQTypedValue,HeadersMatcherDFAState>> nextStateMap,
+ Collection<HeaderMatcherResult> results,
+ HeaderKeyDictionary dictionary)
+ {
+ _nextStateMap = nextStateMap;
+ _results = results;
+ _dictionary = dictionary;
+ }
+
+
+ public Collection<HeaderMatcherResult> match(final FieldTable table)
+ {
+ return match(table.iterator());
+ }
+
+
+
+ public Collection<HeaderMatcherResult> match(Iterator<Map.Entry<AMQShortString,AMQTypedValue>> fieldTableIterator)
+ {
+
+ if(_nextStateMap.isEmpty())
+ {
+ return _results;
+ }
+
+ while(fieldTableIterator.hasNext())
+ {
+
+ Map.Entry<AMQShortString, AMQTypedValue> fieldTableEntry = fieldTableIterator.next();
+ HeaderKey key = _dictionary.get(fieldTableEntry.getKey());
+ if(key != HeaderKey.UNKNOWN)
+ {
+ Map<AMQTypedValue, HeadersMatcherDFAState> valueToStateMap = _nextStateMap.get(key);
+
+ if(valueToStateMap != null)
+ {
+ HeadersMatcherDFAState nextState = valueToStateMap.get(fieldTableEntry.getValue());
+
+ if(nextState == null)
+ {
+ nextState = valueToStateMap.get(null);
+ }
+ if(nextState != null && nextState != this)
+ {
+ return nextState.match(fieldTableIterator);
+ }
+ }
+
+ }
+ }
+
+ return _results;
+
+ }
+
+
+ HeadersMatcherDFAState mergeStateMachines(HeadersMatcherDFAState otherStateMachine)
+ {
+
+ assert(otherStateMachine._dictionary == _dictionary);
+
+ Map<Set<HeadersMatcherDFAState>, HeadersMatcherDFAState> newStateMap= new HashMap<Set<HeadersMatcherDFAState>, HeadersMatcherDFAState>();
+
+ Collection<HeaderMatcherResult> results;
+
+ if(_results.isEmpty())
+ {
+ results = otherStateMachine._results;
+ }
+ else if(otherStateMachine._results.isEmpty())
+ {
+ results = _results;
+ }
+ else
+ {
+ results = new HashSet<HeaderMatcherResult>(_results);
+ results.addAll(otherStateMachine._results);
+ }
+
+
+ final Map<HeaderKey, Map<AMQTypedValue, HeadersMatcherDFAState>> newNextStateMap = new HashMap<HeaderKey, Map<AMQTypedValue, HeadersMatcherDFAState>>();
+
+ HeadersMatcherDFAState newState = new HeadersMatcherDFAState(newNextStateMap, results, _dictionary);
+
+
+ Set<HeadersMatcherDFAState> oldStates = new HashSet<HeadersMatcherDFAState>();
+ oldStates.add(this);
+ oldStates.add(otherStateMachine);
+
+ newStateMap.put(oldStates, newState);
+
+ mergeStateMachines(oldStates, newNextStateMap, newStateMap);
+
+ return newState;
+
+
+ }
+
+ private void mergeStateMachines(final Set<HeadersMatcherDFAState> oldStates,
+ final Map<HeaderKey, Map<AMQTypedValue, HeadersMatcherDFAState>> newNextStateMap,
+ final Map<Set<HeadersMatcherDFAState>, HeadersMatcherDFAState> newStateMap)
+ {
+ Map<HeaderKey, Map<AMQTypedValue, Set<HeadersMatcherDFAState>>> nfaMap = new HashMap<HeaderKey, Map<AMQTypedValue, Set<HeadersMatcherDFAState>>>();
+
+ Set<HeaderKey> distinctKeys = new HashSet<HeaderKey>();
+
+ for(HeadersMatcherDFAState state : oldStates)
+ {
+ Map<HeaderKey, Map<AMQTypedValue, HeadersMatcherDFAState>> map = state._nextStateMap;
+
+ for(Map.Entry<HeaderKey, Map<AMQTypedValue, HeadersMatcherDFAState>> entry : map.entrySet())
+ {
+ Map<AMQTypedValue, Set<HeadersMatcherDFAState>> valueToStatesMap = nfaMap.get(entry.getKey());
+
+ if(valueToStatesMap == null)
+ {
+ valueToStatesMap = new HashMap<AMQTypedValue, Set<HeadersMatcherDFAState>>();
+ nfaMap.put(entry.getKey(), valueToStatesMap);
+ }
+
+ for(Map.Entry<AMQTypedValue, HeadersMatcherDFAState> valueToStateEntry : entry.getValue().entrySet())
+ {
+ Set<HeadersMatcherDFAState> states = valueToStatesMap.get(valueToStateEntry.getKey());
+ if(states == null)
+ {
+ states = new HashSet<HeadersMatcherDFAState>();
+ valueToStatesMap.put(valueToStateEntry.getKey(),states);
+ }
+ states.add(valueToStateEntry.getValue());
+ }
+
+ distinctKeys.add(entry.getKey());
+ }
+ }
+
+ Map<HeaderKey, Set<HeadersMatcherDFAState>> anyValueStates = new HashMap<HeaderKey, Set<HeadersMatcherDFAState>>();
+
+ for(HeaderKey distinctKey : distinctKeys)
+ {
+ Map<AMQTypedValue, Set<HeadersMatcherDFAState>> valueToStateMap = nfaMap.get(distinctKey);
+ if(valueToStateMap != null)
+ {
+ Set<HeadersMatcherDFAState> statesForKeyDefault = valueToStateMap.get(null);
+ if(statesForKeyDefault != null)
+ {
+ anyValueStates.put(distinctKey, statesForKeyDefault);
+ }
+ }
+ }
+
+ // add the defaults for "null" to all other specified values of a given header key
+
+ for( Map.Entry<HeaderKey,Map<AMQTypedValue,Set<HeadersMatcherDFAState>>> entry : nfaMap.entrySet())
+ {
+ Map<AMQTypedValue, Set<HeadersMatcherDFAState>> valueToStatesMap = entry.getValue();
+ for(Map.Entry<AMQTypedValue, Set<HeadersMatcherDFAState>> valueToStates : valueToStatesMap.entrySet())
+ {
+ if(valueToStates.getKey() != null)
+ {
+
+
+ Set<HeadersMatcherDFAState> defaults = anyValueStates.get(entry.getKey());
+ if(defaults != null)
+ {
+ valueToStates.getValue().addAll(defaults);
+ }
+ }
+ }
+ }
+
+ // if a given header key is not mentioned in the map of a machine; then that machine would stay at the same state
+ // for that key.
+ for(HeaderKey distinctKey : distinctKeys)
+ {
+ Map<AMQTypedValue, Set<HeadersMatcherDFAState>> valueToStatesMap = nfaMap.get(distinctKey);
+ for(HeadersMatcherDFAState oldState : oldStates)
+ {
+ if(!oldState._nextStateMap.containsKey(distinctKey))
+ {
+ for(Set<HeadersMatcherDFAState> endStates : valueToStatesMap.values())
+ {
+ endStates.add(oldState);
+ }
+ }
+ }
+ }
+
+
+
+
+ for(Map.Entry<HeaderKey,Map<AMQTypedValue,Set<HeadersMatcherDFAState>>> transitionClass : nfaMap.entrySet())
+ {
+ Map<AMQTypedValue, HeadersMatcherDFAState> valueToDFAState = newNextStateMap.get(transitionClass.getKey());
+ if(valueToDFAState == null)
+ {
+ valueToDFAState = new HashMap<AMQTypedValue, HeadersMatcherDFAState>();
+ newNextStateMap.put(transitionClass.getKey(), valueToDFAState);
+ }
+
+ for(Map.Entry<AMQTypedValue,Set<HeadersMatcherDFAState>> transition : transitionClass.getValue().entrySet())
+ {
+ Set<HeadersMatcherDFAState> destinations = transition.getValue();
+
+
+ HeadersMatcherDFAState nextState = newStateMap.get(destinations);
+
+ if(nextState == null)
+ {
+
+ if(destinations.size() == 1)
+ {
+ nextState = destinations.iterator().next();
+ newStateMap.put(destinations, nextState);
+ }
+ else
+ {
+ Collection<HeaderMatcherResult> results;
+
+ Set<Collection<HeaderMatcherResult>> resultSets = new HashSet<Collection<HeaderMatcherResult>>();
+ for(HeadersMatcherDFAState destination : destinations)
+ {
+ resultSets.add(destination._results);
+ }
+ resultSets.remove(Collections.EMPTY_SET);
+ if(resultSets.size() == 0)
+ {
+ results = Collections.EMPTY_SET;
+ }
+ else if(resultSets.size() == 1)
+ {
+ results = resultSets.iterator().next();
+ }
+ else
+ {
+ results = new HashSet<HeaderMatcherResult>();
+ for(Collection<HeaderMatcherResult> oldResult : resultSets)
+ {
+ results.addAll(oldResult);
+ }
+ }
+
+ final Map<HeaderKey, Map<AMQTypedValue, HeadersMatcherDFAState>> nextStateMap = new HashMap<HeaderKey, Map<AMQTypedValue, HeadersMatcherDFAState>>();
+
+ nextState = new HeadersMatcherDFAState(nextStateMap, results, _dictionary);
+ newStateMap.put(destinations, nextState);
+
+ mergeStateMachines(
+ destinations,
+ nextStateMap,
+ newStateMap);
+
+
+ }
+
+
+ }
+ valueToDFAState.put(transition.getKey(),nextState);
+ }
+ }
+
+
+
+ final ArrayList<HeaderKey> removeKeyList = new ArrayList<HeaderKey>();
+
+ for(Map.Entry<HeaderKey,Map<AMQTypedValue,HeadersMatcherDFAState>> entry : _nextStateMap.entrySet())
+ {
+ final ArrayList<AMQTypedValue> removeValueList = new ArrayList<AMQTypedValue>();
+
+ for(Map.Entry<AMQTypedValue,HeadersMatcherDFAState> valueToDFAState : entry.getValue().entrySet())
+ {
+ if(valueToDFAState.getValue() == this)
+ {
+ HeadersMatcherDFAState defaultState = entry.getValue().get(null);
+ if(defaultState == null || defaultState == this)
+ {
+ removeValueList.add(valueToDFAState.getKey());
+ }
+ }
+ }
+
+ for(AMQTypedValue removeValue : removeValueList)
+ {
+ entry.getValue().remove(removeValue);
+ }
+
+ if(entry.getValue().isEmpty())
+ {
+ removeKeyList.add(entry.getKey());
+ }
+
+ }
+
+ for(HeaderKey removeKey : removeKeyList)
+ {
+ _nextStateMap.remove(removeKey);
+ }
+
+ }
+
+}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeadersParser.java b/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeadersParser.java
new file mode 100644
index 0000000000..85e74122c3
--- /dev/null
+++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/headers/HeadersParser.java
@@ -0,0 +1,439 @@
+package org.apache.qpid.server.exchange.headers;
+
+import org.apache.qpid.framing.*;
+
+import java.util.*;
+
+/*
+*
+* 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 HeadersParser
+{
+
+ private final HeaderKeyDictionary _dictionary = new HeaderKeyDictionary();
+ private static final AMQShortString MATCHING_TYPE_KEY = new AMQShortString("x-match");
+ private static final String ANY_MATCHING = "any";
+ private static final AMQShortString RESERVED_KEY_PREFIX = new AMQShortString("x-");
+
+
+ HeadersMatcherDFAState createStateMachine(FieldTable bindingArguments, HeaderMatcherResult result)
+ {
+ String matchingType = bindingArguments.getString(MATCHING_TYPE_KEY);
+ boolean matchAny = matchingType.equalsIgnoreCase(ANY_MATCHING);
+ if(matchAny)
+ {
+ return createStateMachineForAnyMatch(bindingArguments, result);
+ }
+ else
+ {
+ return createStateMachineForAllMatch(bindingArguments, result);
+ }
+
+
+ }
+
+
+ private HeadersMatcherDFAState createStateMachineForAnyMatch(final FieldTable bindingArguments,
+ final HeaderMatcherResult result)
+ {
+
+ // DFAs for "any" matches have only two states, "not-matched" and "matched"... they start in the former
+ // and upon meeting any of the criteria they move to the latter
+
+ //noinspection unchecked
+ final HeadersMatcherDFAState successState =
+ new HeadersMatcherDFAState(Collections.EMPTY_MAP,Collections.singleton(result),_dictionary);
+
+ Map<HeaderKey, Map<AMQTypedValue, HeadersMatcherDFAState>> nextStateMap =
+ new HashMap<HeaderKey, Map<AMQTypedValue, HeadersMatcherDFAState>>();
+
+ Set<AMQShortString> seenKeys = new HashSet<AMQShortString>();
+
+ Iterator<Map.Entry<AMQShortString, AMQTypedValue>> tableIterator = bindingArguments.iterator();
+
+ while(tableIterator.hasNext())
+ {
+ final Map.Entry<AMQShortString, AMQTypedValue> entry = tableIterator.next();
+ final AMQShortString key = entry.getKey();
+ final AMQTypedValue value = entry.getValue();
+
+
+ if(seenKeys.add(key) && !key.startsWith(RESERVED_KEY_PREFIX))
+ {
+ final AMQType type = value.getType();
+
+ final HeaderKey headerKey = _dictionary.getOrCreate(key);
+ final Map<AMQTypedValue, HeadersMatcherDFAState> valueMap;
+
+ if(type == AMQType.VOID ||
+ ((type == AMQType.ASCII_STRING || type == AMQType.WIDE_STRING) && ((CharSequence)value.getValue()).length() == 0))
+ {
+ valueMap = Collections.singletonMap(null,successState);
+
+ }
+ else
+ {
+ valueMap = Collections.singletonMap(value,successState);
+ }
+ nextStateMap.put(headerKey,valueMap);
+
+ }
+
+ }
+
+ if(seenKeys.size() == 0)
+ {
+ return successState;
+ }
+ else
+ {
+ return new HeadersMatcherDFAState(nextStateMap,Collections.EMPTY_SET,_dictionary);
+ }
+
+
+ }
+
+
+ private HeadersMatcherDFAState createStateMachineForAllMatch(final FieldTable bindingArguments,
+ final HeaderMatcherResult result)
+ {
+ // DFAs for "all" matches have a "success" state, a "fail" state, and states for every subset of
+ // matches which are possible, starting with the empty subset. For example if we have a binding
+ // { x-match="all"
+ // a=1
+ // b=1
+ // c=1
+ // d=1 }
+ // Then we would have the following states
+ // (1) Seen none of a, b, c, or d
+ // (2) Seen a=1 ; none of b,c, or d
+ // (3) Seen b=1 ; none of a,c, or d
+ // (4) Seen c=1 ; none of a,b, or d
+ // (5) Seen d=1 ; none of a,b, or c
+ // (6) Seen a=1,b=1 ; none of c,d
+ // (7) Seen a=1,c=1 ; none of b,d
+ // (8) Seen a=1,d=1 ; none of b,c
+ // (9) Seen b=1,c=1 ; none of a,d
+ //(10) Seen b=1,d=1 ; none of c,d
+ //(11) Seen c=1,d=1 ; none of a,b
+ //(12) Seen a=1,b=1,c=1 ; not d
+ //(13) Seen a=1,b=1,d=1 ; not c
+ //(14) Seen a=1,c=1,d=1 ; not b
+ //(15) Seen b=1,c=1,d=1 ; not a
+ //(16) success
+ //(17) fail
+ //
+ // All states but (16) can transition to (17); additionally:
+ // (1) can transition to (2),(3),(4),(5)
+ // (2) can transition to (6),(7),(8)
+ // (3) can transition to (6),(9),(10)
+ // (4) can transition to (7),(9),(11)
+ // (5) can transition to (8),(10),(11)
+ // (6) can transition to (12),(13)
+ // (7) can transition to (12),(14)
+ // (8) can transition to (13),(14)
+ // (9) can transition to (12),(15)
+ //(10) can transition to (13),(15)
+ //(11) can transition to (14),(15)
+ //(12)-(15) can transition to (16)
+
+ Set<AMQShortString> seenKeys = new HashSet<AMQShortString>();
+ List<KeyValuePair> requiredTerms = new ArrayList<KeyValuePair>(bindingArguments.size());
+
+ Iterator<Map.Entry<AMQShortString, AMQTypedValue>> tableIterator = bindingArguments.iterator();
+
+
+
+ while(tableIterator.hasNext())
+ {
+ final Map.Entry<AMQShortString, AMQTypedValue> entry = tableIterator.next();
+ final AMQShortString key = entry.getKey();
+ final AMQTypedValue value = entry.getValue();
+
+
+ if(seenKeys.add(key) && !key.startsWith(RESERVED_KEY_PREFIX))
+ {
+ final AMQType type = value.getType();
+
+ if(type == AMQType.VOID ||
+ ((type == AMQType.ASCII_STRING || type == AMQType.WIDE_STRING) && ((CharSequence)value.getValue()).length() == 0))
+ {
+ requiredTerms.add(new KeyValuePair(_dictionary.getOrCreate(key),null));
+ }
+ else
+ {
+ requiredTerms.add(new KeyValuePair(_dictionary.getOrCreate(key),value));
+ }
+ }
+
+ }
+
+ final HeadersMatcherDFAState successState =
+ new HeadersMatcherDFAState(Collections.EMPTY_MAP,Collections.singleton(result),_dictionary);
+
+ final HeadersMatcherDFAState failState =
+ new HeadersMatcherDFAState(Collections.EMPTY_MAP,Collections.EMPTY_SET,_dictionary);
+
+ Map<Set<KeyValuePair>, HeadersMatcherDFAState> notSeenTermsToStateMap =
+ new HashMap<Set<KeyValuePair>, HeadersMatcherDFAState>();
+
+ notSeenTermsToStateMap.put(Collections.EMPTY_SET, successState);
+
+
+ final int numberOfTerms = requiredTerms.size();
+
+ for(int numMissingTerms = 1; numMissingTerms <= numberOfTerms; numMissingTerms++)
+ {
+ int[] pos = new int[numMissingTerms];
+ for(int i = 0; i < numMissingTerms; i++)
+ {
+ pos[i] = i;
+ }
+
+ final int maxTermValue = (numberOfTerms - (numMissingTerms - 1));
+
+ while(pos[0] < maxTermValue)
+ {
+
+ Set<KeyValuePair> stateSet = new HashSet<KeyValuePair>();
+ for(int posIndex = 0; posIndex < pos.length; posIndex++)
+ {
+ stateSet.add(requiredTerms.get(pos[posIndex]));
+ }
+
+ final Map<HeaderKey, Map<AMQTypedValue,HeadersMatcherDFAState>> nextStateMap =
+ new HashMap<HeaderKey, Map<AMQTypedValue,HeadersMatcherDFAState>>();
+
+
+ for(int posIndex = 0; posIndex < pos.length; posIndex++)
+ {
+ KeyValuePair nextTerm = requiredTerms.get(pos[posIndex]);
+ HashSet<KeyValuePair> nextStateSet =
+ new HashSet<KeyValuePair>(stateSet);
+ nextStateSet.remove(nextTerm);
+
+ Map<AMQTypedValue, HeadersMatcherDFAState> valueToStateMap =
+ new HashMap<AMQTypedValue, HeadersMatcherDFAState>();
+ nextStateMap.put(nextTerm._key, valueToStateMap);
+
+ valueToStateMap.put( nextTerm._value,notSeenTermsToStateMap.get(nextStateSet));
+ if(nextTerm._value != null)
+ {
+ valueToStateMap.put(null, failState);
+ }
+
+
+ }
+
+
+ HeadersMatcherDFAState newState = new HeadersMatcherDFAState(nextStateMap, Collections.EMPTY_SET, _dictionary);
+
+ notSeenTermsToStateMap.put(stateSet, newState);
+
+ int i = numMissingTerms;
+ while(i-- != 0)
+ {
+ if(++pos[i] <= numberOfTerms -(numMissingTerms-i))
+ {
+ int k = pos[i];
+ for(int j = i+1; j < numMissingTerms; j++)
+ {
+ pos[j] = ++k;
+ }
+ break;
+ }
+ }
+ }
+
+
+
+
+ }
+
+
+ return notSeenTermsToStateMap.get(new HashSet<KeyValuePair>(requiredTerms));
+
+
+
+ }
+
+ public static void main(String[] args) throws AMQFrameDecodingException
+ {
+
+ FieldTable bindingTable = new FieldTable();
+
+ bindingTable.setString(new AMQShortString("x-match"),"all");
+ bindingTable.setInteger("a",1);
+ bindingTable.setVoid(new AMQShortString("b"));
+ bindingTable.setString("c","");
+ bindingTable.setInteger("d",4);
+ bindingTable.setInteger("e",1);
+
+
+
+ FieldTable bindingTable2 = new FieldTable();
+ bindingTable2.setString(new AMQShortString("x-match"),"all");
+ bindingTable2.setInteger("a",1);
+ bindingTable2.setVoid(new AMQShortString("b"));
+ bindingTable2.setString("c","");
+ bindingTable2.setInteger("d",4);
+ bindingTable2.setInteger("e",1);
+ bindingTable2.setInteger("f",1);
+
+
+ FieldTable table = new FieldTable();
+ table.setInteger("a",1);
+ table.setInteger("b",2);
+ table.setString("c","");
+ table.setInteger("d",4);
+ table.setInteger("e",1);
+ table.setInteger("f",1);
+ table.setInteger("h",1);
+ table.setInteger("i",1);
+ table.setInteger("j",1);
+ table.setInteger("k",1);
+ table.setInteger("l",1);
+
+ org.apache.mina.common.ByteBuffer buffer = org.apache.mina.common.ByteBuffer.allocate( (int) table.getEncodedSize());
+ EncodingUtils.writeFieldTableBytes(buffer, table);
+ buffer.flip();
+
+ FieldTable table2 = EncodingUtils.readFieldTable(buffer);
+
+
+
+ FieldTable bindingTable3 = new FieldTable();
+ bindingTable3.setString(new AMQShortString("x-match"),"any");
+ bindingTable3.setInteger("a",1);
+ bindingTable3.setInteger("b",3);
+
+
+ FieldTable bindingTable4 = new FieldTable();
+ bindingTable4.setString(new AMQShortString("x-match"),"any");
+ bindingTable4.setVoid(new AMQShortString("a"));
+
+
+ FieldTable bindingTable5 = new FieldTable();
+ bindingTable5.setString(new AMQShortString("x-match"),"all");
+ bindingTable5.setString(new AMQShortString("h"),"hello");
+
+ for(int i = 0; i < 100; i++)
+ {
+ printMatches(new FieldTable[] {bindingTable5} , table2);
+ }
+
+
+
+ }
+
+
+
+ private static void printMatches(final FieldTable[] bindingKeys, final FieldTable routingKey)
+ {
+ HeadersMatcherDFAState sm = null;
+ Map<HeaderMatcherResult, String> resultMap = new HashMap<HeaderMatcherResult, String>();
+
+ HeadersParser parser = new HeadersParser();
+
+ for(int i = 0; i < bindingKeys.length; i++)
+ {
+ HeaderMatcherResult r = new HeaderMatcherResult();
+ resultMap.put(r, bindingKeys[i].toString());
+
+
+ if(i==0)
+ {
+ sm = parser.createStateMachine(bindingKeys[i], r);
+ }
+ else
+ {
+ sm = sm.mergeStateMachines(parser.createStateMachine(bindingKeys[i], r));
+ }
+ }
+
+ Collection<HeaderMatcherResult> results = null;
+ long beforeTime = System.currentTimeMillis();
+ for(int i = 0; i < 1000000; i++)
+ {
+ routingKey.size();
+
+ assert sm != null;
+ results = sm.match(routingKey);
+
+ }
+ long elapsed = System.currentTimeMillis() - beforeTime;
+ System.out.println("1000000 Iterations took: " + elapsed);
+ Collection<String> resultStrings = new ArrayList<String>();
+
+ assert results != null;
+ for(HeaderMatcherResult result : results)
+ {
+ resultStrings.add(resultMap.get(result));
+ }
+
+ final ArrayList<String> nonMatches = new ArrayList<String>();
+ for(FieldTable key : bindingKeys)
+ {
+ nonMatches.add(key.toString());
+ }
+ nonMatches.removeAll(resultStrings);
+ System.out.println("\""+routingKey+"\" matched with " + resultStrings + " DID NOT MATCH with " + nonMatches);
+
+
+ }
+
+
+ public final static class KeyValuePair
+ {
+ public final HeaderKey _key;
+ public final AMQTypedValue _value;
+ private final int _hashCode;
+
+ public KeyValuePair(final HeaderKey key, final AMQTypedValue value)
+ {
+ _key = key;
+ _value = value;
+ int hash = (1 + 31 * _key.hashCode());
+ if(_value != null)
+ {
+ hash+=_value.hashCode();
+ }
+ _hashCode = hash;
+ }
+
+ public int hashCode()
+ {
+ return _hashCode;
+ }
+
+ public boolean equals(Object o)
+ {
+ KeyValuePair other = (KeyValuePair)o;
+ return (_key == other._key) && (_value == null ? other._value == null : _value.equals(other._value));
+ }
+
+
+ public String toString()
+ {
+ return "{" + _key + " -> " + _value + "}";
+ }
+
+ }
+}
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
new file mode 100644
index 0000000000..9da1852f8e
--- /dev/null
+++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicMatcherDFAState.java
@@ -0,0 +1,236 @@
+package org.apache.qpid.server.exchange.topic;
+
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.AMQShortStringTokenizer;
+
+import java.util.*;
+
+/*
+*
+* 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 TopicMatcherDFAState
+{
+ private final Collection<TopicMatcherResult> _results;
+ private final Map<TopicWord, TopicMatcherDFAState> _nextStateMap;
+ private static final byte TOPIC_DELIMITTER = (byte)'.';
+
+
+ public TopicMatcherDFAState(Map<TopicWord, TopicMatcherDFAState> nextStateMap,
+ Collection<TopicMatcherResult> results )
+ {
+ _nextStateMap = nextStateMap;
+ _results = results;
+ }
+
+
+ public TopicMatcherDFAState nextState(TopicWord word)
+ {
+ final TopicMatcherDFAState nextState = _nextStateMap.get(word);
+ return nextState == null ? _nextStateMap.get(TopicWord.ANY_WORD) : nextState;
+ }
+
+ public Collection<TopicMatcherResult> terminate()
+ {
+ return _results;
+ }
+
+
+ public Collection<TopicMatcherResult> parse(TopicWordDictionary dictionary, AMQShortString routingKey)
+ {
+ return parse(dictionary, routingKey.tokenize(TOPIC_DELIMITTER));
+ }
+
+ private Collection<TopicMatcherResult> parse(final TopicWordDictionary dictionary,
+ final AMQShortStringTokenizer tokens)
+ {
+ if(!tokens.hasMoreTokens())
+ {
+ return _results;
+ }
+ TopicWord word = dictionary.getWord(tokens.nextToken());
+ TopicMatcherDFAState nextState = _nextStateMap.get(word);
+ if(nextState == null && word != TopicWord.ANY_WORD)
+ {
+ nextState = _nextStateMap.get(TopicWord.ANY_WORD);
+ }
+ if(nextState == null)
+ {
+ return Collections.EMPTY_SET;
+ }
+ // Shortcut if we are at a looping terminal state
+ if((nextState == this) && (_nextStateMap.size() == 1) && _nextStateMap.containsKey(TopicWord.ANY_WORD))
+ {
+ return _results;
+ }
+
+ return nextState.parse(dictionary, tokens);
+
+ }
+
+
+ public TopicMatcherDFAState mergeStateMachines(TopicMatcherDFAState otherStateMachine)
+ {
+ Map<Set<TopicMatcherDFAState>, TopicMatcherDFAState> newStateMap= new HashMap<Set<TopicMatcherDFAState>, TopicMatcherDFAState>();
+
+ Collection<TopicMatcherResult> results;
+
+ if(_results.isEmpty())
+ {
+ results = otherStateMachine._results;
+ }
+ else if(otherStateMachine._results.isEmpty())
+ {
+ results = _results;
+ }
+ else
+ {
+ results = new HashSet<TopicMatcherResult>(_results);
+ results.addAll(otherStateMachine._results);
+ }
+
+
+ final Map<TopicWord, TopicMatcherDFAState> newNextStateMap = new HashMap<TopicWord, TopicMatcherDFAState>();
+
+ TopicMatcherDFAState newState = new TopicMatcherDFAState(newNextStateMap, results);
+
+
+ Set<TopicMatcherDFAState> oldStates = new HashSet<TopicMatcherDFAState>();
+ oldStates.add(this);
+ oldStates.add(otherStateMachine);
+
+ newStateMap.put(oldStates, newState);
+
+ mergeStateMachines(oldStates, newNextStateMap, newStateMap);
+
+ return newState;
+
+ }
+
+ private static void mergeStateMachines(
+ final Set<TopicMatcherDFAState> oldStates,
+ final Map<TopicWord, TopicMatcherDFAState> newNextStateMap,
+ final Map<Set<TopicMatcherDFAState>, TopicMatcherDFAState> newStateMap)
+ {
+ Map<TopicWord, Set<TopicMatcherDFAState>> nfaMap = new HashMap<TopicWord, Set<TopicMatcherDFAState>>();
+
+ for(TopicMatcherDFAState state : oldStates)
+ {
+ Map<TopicWord, TopicMatcherDFAState> map = state._nextStateMap;
+ for(Map.Entry<TopicWord, TopicMatcherDFAState> entry : map.entrySet())
+ {
+ Set<TopicMatcherDFAState> states = nfaMap.get(entry.getKey());
+ if(states == null)
+ {
+ states = new HashSet<TopicMatcherDFAState>();
+ nfaMap.put(entry.getKey(), states);
+ }
+ states.add(entry.getValue());
+ }
+ }
+
+ Set<TopicMatcherDFAState> anyWordStates = nfaMap.get(TopicWord.ANY_WORD);
+
+ for(Map.Entry<TopicWord, Set<TopicMatcherDFAState>> transition : nfaMap.entrySet())
+ {
+ Set<TopicMatcherDFAState> destinations = transition.getValue();
+
+ if(anyWordStates != null)
+ {
+ destinations.addAll(anyWordStates);
+ }
+
+ TopicMatcherDFAState nextState = newStateMap.get(destinations);
+ if(nextState == null)
+ {
+
+ if(destinations.size() == 1)
+ {
+ nextState = destinations.iterator().next();
+ newStateMap.put(destinations, nextState);
+ }
+ else
+ {
+ Collection<TopicMatcherResult> results;
+
+ Set<Collection<TopicMatcherResult>> resultSets = new HashSet<Collection<TopicMatcherResult>>();
+ for(TopicMatcherDFAState destination : destinations)
+ {
+ resultSets.add(destination._results);
+ }
+ resultSets.remove(Collections.EMPTY_SET);
+ if(resultSets.size() == 0)
+ {
+ results = Collections.EMPTY_SET;
+ }
+ else if(resultSets.size() == 1)
+ {
+ results = resultSets.iterator().next();
+ }
+ else
+ {
+ results = new HashSet<TopicMatcherResult>();
+ for(Collection<TopicMatcherResult> oldResult : resultSets)
+ {
+ results.addAll(oldResult);
+ }
+ }
+
+ final Map<TopicWord, TopicMatcherDFAState> nextStateMap = new HashMap<TopicWord, TopicMatcherDFAState>();
+
+ nextState = new TopicMatcherDFAState(nextStateMap, results);
+ newStateMap.put(destinations, nextState);
+
+ mergeStateMachines(
+ destinations,
+ nextStateMap,
+ newStateMap);
+
+
+ }
+
+
+ }
+ newNextStateMap.put(transition.getKey(),nextState);
+ }
+
+ // Remove redundant transitions where defined tokenWord has same action as ANY_WORD
+ TopicMatcherDFAState anyWordState = newNextStateMap.get(TopicWord.ANY_WORD);
+ if(anyWordState != null)
+ {
+ List<TopicWord> removeList = new ArrayList<TopicWord>();
+ for(Map.Entry<TopicWord,TopicMatcherDFAState> entry : newNextStateMap.entrySet())
+ {
+ if(entry.getValue() == anyWordState && entry.getKey() != TopicWord.ANY_WORD)
+ {
+ removeList.add(entry.getKey());
+ }
+ }
+ for(TopicWord removeKey : removeList)
+ {
+ newNextStateMap.remove(removeKey);
+ }
+ }
+
+
+
+ }
+
+
+}
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
new file mode 100644
index 0000000000..7e3d1819f4
--- /dev/null
+++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicMatcherResult.java
@@ -0,0 +1,25 @@
+package org.apache.qpid.server.exchange.topic;
+
+/*
+*
+* 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 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
new file mode 100644
index 0000000000..f564bbb565
--- /dev/null
+++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicParser.java
@@ -0,0 +1,448 @@
+package org.apache.qpid.server.exchange.topic;
+
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.AMQShortStringTokenizer;
+
+import java.util.*;
+
+/*
+*
+* 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 TopicParser
+{
+ private static final byte TOPIC_DELIMITER = (byte)'.';
+
+ private final TopicWordDictionary _dictionary = new TopicWordDictionary();
+
+ private static class Position
+ {
+ private final TopicWord _word;
+ private final boolean _selfTransition;
+ private final int _position;
+ private final boolean _endState;
+
+
+ public Position(final int position, final TopicWord word, final boolean selfTransition, final boolean endState)
+ {
+ _position = position;
+ _word = word;
+ _selfTransition = selfTransition;
+ _endState = endState;
+ }
+
+
+ }
+
+ private static final Position ERROR_POSITION = new Position(Integer.MAX_VALUE,null, true, false);
+
+ private static class SimpleState
+ {
+ Set<Position> _positions;
+ Map<TopicWord, SimpleState> _nextState;
+ }
+
+
+ TopicMatcherDFAState createStateMachine(AMQShortString bindingKey, TopicMatcherResult result)
+ {
+ List<TopicWord> wordList = createTopicWordList(bindingKey);
+ int wildCards = 0;
+ for(TopicWord word : wordList)
+ {
+ if(word == TopicWord.WILDCARD_WORD)
+ {
+ wildCards++;
+ }
+ }
+ if(wildCards == 0)
+ {
+ TopicMatcherDFAState[] states = new TopicMatcherDFAState[wordList.size()+1];
+ states[states.length-1] = new TopicMatcherDFAState(Collections.EMPTY_MAP, Collections.singleton(result));
+ for(int i = states.length-2; i >= 0; i--)
+ {
+ states[i] = new TopicMatcherDFAState(Collections.singletonMap(wordList.get(i),states[i+1]),Collections.EMPTY_SET);
+
+ }
+ return states[0];
+ }
+ else if(wildCards == wordList.size())
+ {
+ Map<TopicWord,TopicMatcherDFAState> stateMap = new HashMap<TopicWord,TopicMatcherDFAState>();
+ TopicMatcherDFAState state = new TopicMatcherDFAState(stateMap, Collections.singleton(result));
+ stateMap.put(TopicWord.ANY_WORD, state);
+ return state;
+ }
+
+
+ int positionCount = wordList.size() - wildCards;
+
+ Position[] positions = new Position[positionCount+1];
+
+ int lastWord;
+
+ if(wordList.get(wordList.size()-1)== TopicWord.WILDCARD_WORD)
+ {
+ lastWord = wordList.size()-1;
+ positions[positionCount] = new Position(positionCount, TopicWord.ANY_WORD, true, true);
+ }
+ else
+ {
+ lastWord = wordList.size();
+ positions[positionCount] = new Position(positionCount, TopicWord.ANY_WORD, false, true);
+ }
+
+
+
+ int pos = 0;
+ int wordPos = 0;
+
+
+ while(wordPos < lastWord)
+ {
+ TopicWord word = wordList.get(wordPos++);
+
+ if(word == TopicWord.WILDCARD_WORD)
+ {
+ int nextWordPos = wordPos++;
+ word = wordList.get(nextWordPos);
+
+ positions[pos] = new Position(pos++,word,true,false);
+ }
+ else
+ {
+ positions[pos] = new Position(pos++,word,false,false);
+ }
+
+ }
+
+ // 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...
+
+ Map<Set<Position>,SimpleState> stateMap = new HashMap<Set<Position>,SimpleState>();
+
+
+ SimpleState state = new SimpleState();
+ state._positions = Collections.singleton( positions[0] );
+ stateMap.put(state._positions, state);
+
+ calculateNextStates(state, stateMap, positions);
+
+ SimpleState[] simpleStates = stateMap.values().toArray(new SimpleState[stateMap.size()]);
+ HashMap<TopicWord, TopicMatcherDFAState>[] dfaStateMaps = new HashMap[simpleStates.length];
+ Map<SimpleState, TopicMatcherDFAState> simple2DFAMap = new HashMap<SimpleState, TopicMatcherDFAState>();
+
+ for(int i = 0; i < simpleStates.length; i++)
+ {
+
+ Collection<TopicMatcherResult> results;
+ boolean endState = false;
+
+ for(Position p : simpleStates[i]._positions)
+ {
+ if(p._endState)
+ {
+ endState = true;
+ break;
+ }
+ }
+
+ if(endState)
+ {
+ results = Collections.singleton(result);
+ }
+ else
+ {
+ results = Collections.EMPTY_SET;
+ }
+
+ dfaStateMaps[i] = new HashMap<TopicWord, TopicMatcherDFAState>();
+ simple2DFAMap.put(simpleStates[i], new TopicMatcherDFAState(dfaStateMaps[i],results));
+
+ }
+ for(int i = 0; i < simpleStates.length; i++)
+ {
+ SimpleState simpleState = simpleStates[i];
+
+ Map<TopicWord, SimpleState> nextSimpleStateMap = simpleState._nextState;
+ for(Map.Entry<TopicWord, SimpleState> stateMapEntry : nextSimpleStateMap.entrySet())
+ {
+ dfaStateMaps[i].put(stateMapEntry.getKey(), simple2DFAMap.get(stateMapEntry.getValue()));
+ }
+
+ }
+
+ return simple2DFAMap.get(state);
+
+ }
+
+
+
+ private void calculateNextStates(final SimpleState state,
+ final Map<Set<Position>, SimpleState> stateMap,
+ final Position[] positions)
+ {
+ Map<TopicWord, Set<Position>> transitions = new HashMap<TopicWord,Set<Position>>();
+
+ for(Position pos : state._positions)
+ {
+ if(pos._selfTransition)
+ {
+ Set<Position> dest = transitions.get(TopicWord.ANY_WORD);
+ if(dest == null)
+ {
+ dest = new HashSet<Position>();
+ transitions.put(TopicWord.ANY_WORD,dest);
+ }
+ dest.add(pos);
+ }
+
+ final int nextPos = pos._position + 1;
+ Position nextPosition = nextPos == positions.length ? ERROR_POSITION : positions[nextPos];
+
+ Set<Position> dest = transitions.get(pos._word);
+ if(dest == null)
+ {
+ dest = new HashSet<Position>();
+ transitions.put(pos._word,dest);
+ }
+ dest.add(nextPosition);
+
+ }
+
+ Set<Position> anyWordTransitions = transitions.get(TopicWord.ANY_WORD);
+ if(anyWordTransitions != null)
+ {
+ for(Set<Position> dest : transitions.values())
+ {
+ dest.addAll(anyWordTransitions);
+ }
+ }
+
+ state._nextState = new HashMap<TopicWord, SimpleState>();
+
+ for(Map.Entry<TopicWord,Set<Position>> dest : transitions.entrySet())
+ {
+
+ if(dest.getValue().size()>1)
+ {
+ dest.getValue().remove(ERROR_POSITION);
+ }
+ Position loopingTerminal = null;
+ for(Position destPos : dest.getValue())
+ {
+ if(destPos._selfTransition && destPos._endState)
+ {
+ loopingTerminal = destPos;
+ break;
+ }
+ }
+
+ if(loopingTerminal!=null)
+ {
+ dest.setValue(Collections.singleton(loopingTerminal));
+ }
+
+ SimpleState stateForEntry = stateMap.get(dest.getValue());
+ if(stateForEntry == null)
+ {
+ stateForEntry = new SimpleState();
+ stateForEntry._positions = dest.getValue();
+ stateMap.put(dest.getValue(),stateForEntry);
+ calculateNextStates(stateForEntry,
+ stateMap,
+ positions);
+ }
+ state._nextState.put(dest.getKey(),stateForEntry);
+
+
+
+ }
+
+ // remove redundant transitions
+ SimpleState anyWordState = state._nextState.get(TopicWord.ANY_WORD);
+ if(anyWordState != null)
+ {
+ List<TopicWord> removeList = new ArrayList<TopicWord>();
+ for(Map.Entry<TopicWord,SimpleState> entry : state._nextState.entrySet())
+ {
+ if(entry.getValue() == anyWordState && entry.getKey() != TopicWord.ANY_WORD)
+ {
+ removeList.add(entry.getKey());
+ }
+ }
+ for(TopicWord removeKey : removeList)
+ {
+ state._nextState.remove(removeKey);
+ }
+ }
+
+
+ }
+
+ private List<TopicWord> createTopicWordList(final AMQShortString bindingKey)
+ {
+ AMQShortStringTokenizer tokens = bindingKey.tokenize(TOPIC_DELIMITER);
+ TopicWord previousWord = null;
+
+ List<TopicWord> wordList = new ArrayList<TopicWord>();
+
+ while(tokens.hasMoreTokens())
+ {
+ TopicWord nextWord = _dictionary.getOrCreateWord(tokens.nextToken());
+ if(previousWord == TopicWord.WILDCARD_WORD)
+ {
+
+ if(nextWord == TopicWord.WILDCARD_WORD)
+ {
+ // consecutive wildcards can be merged
+ // i.e. subsequent wildcards can be discarded
+ continue;
+ }
+ else if(nextWord == TopicWord.ANY_WORD)
+ {
+ // wildcard and anyword can be reordered to always put anyword first
+ wordList.set(wordList.size()-1,TopicWord.ANY_WORD);
+ nextWord = TopicWord.WILDCARD_WORD;
+ }
+ }
+ wordList.add(nextWord);
+ previousWord = nextWord;
+
+ }
+ return wordList;
+ }
+
+
+ public static void main(String[] args)
+ {
+ printMatches("a.#.b.#","a.b.b.b.b.b.b.b.c");
+
+
+ printMatches("","");
+ printMatches("a","a");
+ printMatches("a","");
+ printMatches("","a");
+ printMatches("a.b","a.b");
+ printMatches("a","a.b");
+ printMatches("a.b","a");
+ printMatches("*","a");
+ printMatches("*.b","a.b");
+ printMatches("*.*","a.b");
+ printMatches("a.*","a.b");
+ printMatches("a.*.#","a.b");
+ printMatches("a.#.b","a.b");
+
+ printMatches("#.b","a");
+ printMatches("#.b","a.b");
+ printMatches("#.a.b","a.b");
+
+
+ printMatches("#","");
+ printMatches("#","a");
+ printMatches("#","a.b");
+ printMatches("#.#","a.b");
+ printMatches("#.*","a.b");
+
+ printMatches("#.a.b","a.b");
+ printMatches("a.b.#","a.b");
+ printMatches("a.#","a.b");
+ printMatches("#.*.#","a.b");
+ printMatches("#.*.b.#","a.b");
+ printMatches("#.a.*.#","a.b");
+ printMatches("#.a.#.b.#","a.b");
+ printMatches("#.*.#.*.#","a.b");
+ printMatches("*.#.*.#","a.b");
+ printMatches("#.*.#.*","a.b");
+
+
+ printMatches(new String[]{"a.#.b.#","a.*.#.b.#"},"a.b.b.b.b.b.b.b.c");
+
+
+ printMatches(new String[]{"a.b", "a.c"},"a.b");
+ printMatches(new String[]{"a.#", "a.c", "#.b"},"a.b");
+ printMatches(new String[]{"a.#", "a.c", "#.b", "#", "*.*"},"a.b");
+
+ printMatches(new String[]{"a.b.c.d.e.#", "a.b.c.d.#", "a.b.c.d.*", "a.b.c.#", "#.e", "a.*.c.d.e","#.c.*.#.*.*"},"a.b.c.d.e");
+ printMatches(new String[]{"a.b.c.d.e.#", "a.b.c.d.#", "a.b.c.d.*", "a.b.c.#", "#.e", "a.*.c.d.e","#.c.*.#.*.*"},"a.b.c.d.f.g");
+
+
+
+
+ }
+
+ private static void printMatches(final String[] bindingKeys, final String routingKey)
+ {
+ TopicMatcherDFAState sm = null;
+ Map<TopicMatcherResult, String> resultMap = new HashMap<TopicMatcherResult, String>();
+
+ TopicParser parser = new TopicParser();
+
+ for(int i = 0; i < bindingKeys.length; i++)
+ {
+ TopicMatcherResult r = new TopicMatcherResult();
+ resultMap.put(r, bindingKeys[i]);
+ AMQShortString bindingKeyShortString = new AMQShortString(bindingKeys[i]);
+
+ if(i==0)
+ {
+ sm = parser.createStateMachine(bindingKeyShortString, r);
+ }
+ else
+ {
+ sm = sm.mergeStateMachines(parser.createStateMachine(bindingKeyShortString, r));
+ }
+ }
+ AMQShortString routingKeyShortString = new AMQShortString(routingKey);
+
+ Collection<TopicMatcherResult> results = sm.parse(parser._dictionary, routingKeyShortString);
+ Collection<String> resultStrings = new ArrayList<String>();
+
+ for(TopicMatcherResult result : results)
+ {
+ resultStrings.add(resultMap.get(result));
+ }
+
+ final ArrayList<String> nonMatches = new ArrayList<String>(Arrays.asList(bindingKeys));
+ nonMatches.removeAll(resultStrings);
+ System.out.println("\""+routingKeyShortString+"\" matched with " + resultStrings + " DID NOT MATCH with " + nonMatches);
+
+
+ }
+
+ private static void printMatches(String bindingKey, String routingKey)
+ {
+ printMatches(new String[] { bindingKey }, routingKey);
+ }
+
+
+ private static boolean matches(String bindingKey, String routingKey)
+ {
+ AMQShortString bindingKeyShortString = new AMQShortString(bindingKey);
+ AMQShortString routingKeyShortString = new AMQShortString(routingKey);
+ TopicParser parser = new TopicParser();
+
+ 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
new file mode 100644
index 0000000000..e86a726b4f
--- /dev/null
+++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicWord.java
@@ -0,0 +1,34 @@
+package org.apache.qpid.server.exchange.topic;
+
+import org.apache.qpid.framing.AMQShortString;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.concurrent.ConcurrentHashMap;
+
+/*
+*
+* 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 final class TopicWord
+{
+ public static final TopicWord ANY_WORD = new TopicWord();
+ public static final TopicWord WILDCARD_WORD = new TopicWord();
+
+}
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
new file mode 100644
index 0000000000..f69616cc85
--- /dev/null
+++ b/java/broker/src/main/java/org/apache/qpid/server/exchange/topic/TopicWordDictionary.java
@@ -0,0 +1,63 @@
+package org.apache.qpid.server.exchange.topic;
+
+import org.apache.qpid.framing.AMQShortString;
+
+import java.util.concurrent.ConcurrentHashMap;
+
+/*
+*
+* 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 TopicWordDictionary
+{
+ private final ConcurrentHashMap<AMQShortString,TopicWord> _dictionary =
+ new ConcurrentHashMap<AMQShortString,TopicWord>();
+
+
+
+ public TopicWordDictionary()
+ {
+ _dictionary.put(new AMQShortString("*"), TopicWord.ANY_WORD);
+ _dictionary.put(new AMQShortString("#"), TopicWord.WILDCARD_WORD);
+ }
+
+
+
+
+ public TopicWord getOrCreateWord(AMQShortString name)
+ {
+ TopicWord word = _dictionary.putIfAbsent(name, new TopicWord());
+ if(word == null)
+ {
+ word = _dictionary.get(name);
+ }
+ return word;
+ }
+
+
+ public TopicWord getWord(AMQShortString name)
+ {
+ TopicWord word = _dictionary.get(name);
+ if(word == null)
+ {
+ word = TopicWord.ANY_WORD;
+ }
+ return word;
+ }
+}
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
new file mode 100644
index 0000000000..af49e113f6
--- /dev/null
+++ b/java/broker/src/main/java/org/apache/qpid/server/flow/AbstractFlowCreditManager.java
@@ -0,0 +1,57 @@
+/*
+*
+* 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.flow;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.Set;
+import java.util.HashSet;
+
+public abstract class AbstractFlowCreditManager implements FlowCreditManager
+{
+ protected final AtomicBoolean _suspended = new AtomicBoolean(false);
+ private final Set<FlowCreditManagerListener> _listeners = new HashSet<FlowCreditManagerListener>();
+
+ public final void addStateListener(FlowCreditManagerListener listener)
+ {
+ _listeners.add(listener);
+ }
+
+ public final boolean removeListener(FlowCreditManagerListener listener)
+ {
+ return _listeners.remove(listener);
+ }
+
+ private void notifyListeners(final boolean suspended)
+ {
+ for(FlowCreditManagerListener listener : _listeners)
+ {
+ listener.creditStateChanged(!suspended);
+ }
+ }
+
+ protected final void setSuspended(final boolean suspended)
+ {
+ if(_suspended.compareAndSet(!suspended, suspended))
+ {
+ notifyListeners(suspended);
+ }
+ }
+}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/flow/BytesOnlyCreditManager.java b/java/broker/src/main/java/org/apache/qpid/server/flow/BytesOnlyCreditManager.java
new file mode 100644
index 0000000000..96a1071135
--- /dev/null
+++ b/java/broker/src/main/java/org/apache/qpid/server/flow/BytesOnlyCreditManager.java
@@ -0,0 +1,77 @@
+package org.apache.qpid.server.flow;
+
+import org.apache.qpid.server.queue.AMQMessage;
+
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.Set;
+import java.util.HashSet;
+
+/*
+*
+* 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 BytesOnlyCreditManager extends AbstractFlowCreditManager
+{
+ private final AtomicLong _bytesCredit;
+
+ public BytesOnlyCreditManager(long initialCredit)
+ {
+ _bytesCredit = new AtomicLong(initialCredit);
+ }
+
+ public void addCredit(long messageCredit, long bytesCredit)
+ {
+ _bytesCredit.addAndGet(bytesCredit);
+ setSuspended(false);
+ }
+
+ public void removeAllCredit()
+ {
+ _bytesCredit.set(0L);
+ }
+
+ public boolean hasCredit()
+ {
+ return _bytesCredit.get() > 0L;
+ }
+
+ public boolean useCreditForMessage(AMQMessage msg)
+ {
+ final long msgSize = msg.getSize();
+ if(hasCredit())
+ {
+ if(_bytesCredit.addAndGet(-msgSize) >= 0)
+ {
+ return true;
+ }
+ else
+ {
+ _bytesCredit.addAndGet(msgSize);
+ setSuspended(true);
+ return false;
+ }
+ }
+ else
+ {
+ return false;
+ }
+
+ }
+}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/flow/FlowCreditManager.java b/java/broker/src/main/java/org/apache/qpid/server/flow/FlowCreditManager.java
new file mode 100644
index 0000000000..a249a6e63a
--- /dev/null
+++ b/java/broker/src/main/java/org/apache/qpid/server/flow/FlowCreditManager.java
@@ -0,0 +1,44 @@
+package org.apache.qpid.server.flow;
+
+import org.apache.qpid.server.queue.AMQMessage;
+
+/*
+*
+* 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 interface FlowCreditManager
+{
+
+ public static interface FlowCreditManagerListener
+ {
+ void creditStateChanged(boolean hasCredit);
+ }
+
+ void addStateListener(FlowCreditManagerListener listener);
+
+ boolean removeListener(FlowCreditManagerListener listener);
+
+ public void addCredit(long messageCredit, long bytesCredit);
+
+ public void removeAllCredit();
+
+ public boolean hasCredit();
+
+ public boolean useCreditForMessage(AMQMessage msg);
+}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/flow/LimitlessCreditManager.java b/java/broker/src/main/java/org/apache/qpid/server/flow/LimitlessCreditManager.java
new file mode 100644
index 0000000000..d63431c3eb
--- /dev/null
+++ b/java/broker/src/main/java/org/apache/qpid/server/flow/LimitlessCreditManager.java
@@ -0,0 +1,44 @@
+package org.apache.qpid.server.flow;
+
+import org.apache.qpid.server.queue.AMQMessage;
+
+/*
+*
+* 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 LimitlessCreditManager extends AbstractFlowCreditManager implements FlowCreditManager
+{
+ public void addCredit(long messageCredit, long bytesCredit)
+ {
+ }
+
+ public void removeAllCredit()
+ {
+ }
+
+ public boolean hasCredit()
+ {
+ return true;
+ }
+
+ public boolean useCreditForMessage(AMQMessage msg)
+ {
+ return true;
+ }
+}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/flow/MessageAndBytesCreditManager.java b/java/broker/src/main/java/org/apache/qpid/server/flow/MessageAndBytesCreditManager.java
new file mode 100644
index 0000000000..9c377481de
--- /dev/null
+++ b/java/broker/src/main/java/org/apache/qpid/server/flow/MessageAndBytesCreditManager.java
@@ -0,0 +1,79 @@
+package org.apache.qpid.server.flow;
+
+import org.apache.qpid.server.queue.AMQMessage;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+/*
+*
+* 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 MessageAndBytesCreditManager extends AbstractFlowCreditManager implements FlowCreditManager
+{
+ private long _messageCredit;
+ private long _bytesCredit;
+
+ MessageAndBytesCreditManager(final long messageCredit, final long bytesCredit)
+ {
+ _messageCredit = messageCredit;
+ _bytesCredit = bytesCredit;
+ }
+
+ public synchronized void addCredit(long messageCredit, long bytesCredit)
+ {
+ _messageCredit += messageCredit;
+ _bytesCredit += bytesCredit;
+ setSuspended(hasCredit());
+ }
+
+ public synchronized void removeAllCredit()
+ {
+ _messageCredit = 0L;
+ _bytesCredit = 0L;
+ setSuspended(true);
+ }
+
+ public synchronized boolean hasCredit()
+ {
+ return (_messageCredit > 0L) && ( _bytesCredit > 0L );
+ }
+
+ public synchronized boolean useCreditForMessage(AMQMessage msg)
+ {
+ if(_messageCredit == 0L)
+ {
+ setSuspended(true);
+ return false;
+ }
+ else
+ {
+ final long msgSize = msg.getSize();
+ if(msgSize > _bytesCredit)
+ {
+ setSuspended(true);
+ return false;
+ }
+ _messageCredit--;
+ _bytesCredit -= msgSize;
+ setSuspended(false);
+ return true;
+ }
+
+ }
+}
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
new file mode 100644
index 0000000000..6cff2d8488
--- /dev/null
+++ b/java/broker/src/main/java/org/apache/qpid/server/flow/MessageOnlyCreditManager.java
@@ -0,0 +1,76 @@
+package org.apache.qpid.server.flow;
+
+import org.apache.qpid.server.queue.AMQMessage;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+/*
+*
+* 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 MessageOnlyCreditManager extends AbstractFlowCreditManager implements FlowCreditManager
+{
+ private final AtomicLong _messageCredit;
+
+ MessageOnlyCreditManager(final long initialCredit)
+ {
+ _messageCredit = new AtomicLong(initialCredit);
+ }
+
+ public void addCredit(long messageCredit, long bytesCredit)
+ {
+ setSuspended(false);
+ _messageCredit.addAndGet(messageCredit);
+ }
+
+ public void removeAllCredit()
+ {
+ setSuspended(true);
+ _messageCredit.set(0L);
+ }
+
+ public boolean hasCredit()
+ {
+ return _messageCredit.get() > 0L;
+ }
+
+ public boolean useCreditForMessage(AMQMessage msg)
+ {
+ if(hasCredit())
+ {
+ if(_messageCredit.addAndGet(-1L) >= 0)
+ {
+ setSuspended(false);
+ return true;
+ }
+ else
+ {
+ _messageCredit.addAndGet(1L);
+ setSuspended(true);
+ return false;
+ }
+ }
+ else
+ {
+ setSuspended(true);
+ return false;
+ }
+
+ }
+}
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
new file mode 100644
index 0000000000..d9752b1098
--- /dev/null
+++ b/java/broker/src/main/java/org/apache/qpid/server/flow/Pre0_10CreditManager.java
@@ -0,0 +1,177 @@
+/*
+*
+* 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.flow;
+
+import org.apache.qpid.server.queue.AMQMessage;
+
+public class Pre0_10CreditManager extends AbstractFlowCreditManager implements FlowCreditManager
+{
+
+ private volatile long _bytesCreditLimit;
+ private volatile long _messageCreditLimit;
+
+ private volatile long _bytesCredit;
+ private volatile long _messageCredit;
+
+ public Pre0_10CreditManager(long bytesCreditLimit, long messageCreditLimit)
+ {
+ _bytesCreditLimit = bytesCreditLimit;
+ _messageCreditLimit = messageCreditLimit;
+ _bytesCredit = bytesCreditLimit;
+ _messageCredit = messageCreditLimit;
+ }
+
+
+ public synchronized void setCreditLimits(final long bytesCreditLimit, final long messageCreditLimit)
+ {
+ long bytesCreditChange = bytesCreditLimit - _bytesCreditLimit;
+ long messageCreditChange = messageCreditLimit - _messageCreditLimit;
+
+
+
+ if(bytesCreditChange != 0L)
+ {
+ if(bytesCreditLimit == 0L)
+ {
+ _bytesCredit = 0;
+ }
+ else
+ {
+ _bytesCredit += bytesCreditChange;
+ }
+ }
+
+
+ if(messageCreditChange != 0L)
+ {
+ if(messageCreditLimit == 0L)
+ {
+ _messageCredit = 0;
+ }
+ else
+ {
+ _messageCredit += messageCreditChange;
+ }
+ }
+
+
+ _bytesCreditLimit = bytesCreditLimit;
+ _messageCreditLimit = messageCreditLimit;
+
+ setSuspended(!hasCredit());
+
+ }
+
+
+ public synchronized void addCredit(final long messageCredit, final long bytesCredit)
+ {
+ final long messageCreditLimit = _messageCreditLimit;
+ if(messageCreditLimit != 0L)
+ {
+ long newCredit = _messageCredit + messageCredit;
+ _messageCredit = newCredit > messageCreditLimit ? messageCreditLimit : newCredit;
+ }
+
+
+ final long bytesCreditLimit = _bytesCreditLimit;
+ if(bytesCreditLimit != 0L)
+ {
+ long newCredit = _bytesCredit + bytesCredit;
+ _bytesCredit = newCredit > bytesCreditLimit ? bytesCreditLimit : newCredit;
+ }
+
+ setSuspended(!hasCredit());
+
+ }
+
+ public synchronized void removeAllCredit()
+ {
+ _bytesCredit = 0L;
+ _messageCredit = 0L;
+ setSuspended(!hasCredit());
+ }
+
+ public synchronized boolean hasCredit()
+ {
+ return (_bytesCreditLimit == 0L || _bytesCredit > 0)
+ && (_messageCreditLimit == 0L || _messageCredit > 0);
+ }
+
+ public synchronized boolean useCreditForMessage(final AMQMessage msg)
+ {
+ if(_messageCreditLimit != 0L)
+ {
+ if(_messageCredit != 0L)
+ {
+ if(_bytesCreditLimit == 0L)
+ {
+ _messageCredit--;
+
+ return true;
+ }
+ else
+ {
+ if((_bytesCredit >= msg.getSize()) || (_bytesCredit == _bytesCreditLimit))
+ {
+ _messageCredit--;
+ _bytesCredit -= msg.getSize();
+
+ return true;
+ }
+ else
+ {
+ setSuspended(true);
+ return false;
+ }
+ }
+ }
+ else
+ {
+ setSuspended(true);
+ return false;
+ }
+ }
+ else
+ {
+ if(_bytesCreditLimit == 0L)
+ {
+
+ return true;
+ }
+ else
+ {
+ if((_bytesCredit >= msg.getSize()) || (_bytesCredit == _bytesCreditLimit))
+ {
+ _bytesCredit -= msg.getSize();
+
+ return true;
+ }
+ else
+ {
+ setSuspended(true);
+ return false;
+ }
+ }
+
+ }
+
+ }
+}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/AccessRequestHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/AccessRequestHandler.java
index 6ace626c28..c13a69b793 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/handler/AccessRequestHandler.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/handler/AccessRequestHandler.java
@@ -4,10 +4,6 @@ import org.apache.qpid.framing.*;
import org.apache.qpid.server.state.StateAwareMethodListener;
import org.apache.qpid.server.state.AMQStateManager;
import org.apache.qpid.server.protocol.AMQProtocolSession;
-import org.apache.qpid.server.virtualhost.VirtualHost;
-import org.apache.qpid.server.queue.QueueRegistry;
-import org.apache.qpid.server.queue.AMQQueue;
-import org.apache.qpid.server.exchange.Exchange;
import org.apache.qpid.AMQException;
/**
diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/BasicCancelMethodHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/BasicCancelMethodHandler.java
index bda1c16cf6..29054f55c1 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/handler/BasicCancelMethodHandler.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/handler/BasicCancelMethodHandler.java
@@ -21,11 +21,9 @@
package org.apache.qpid.server.handler;
import org.apache.qpid.AMQException;
-import org.apache.qpid.framing.AMQFrame;
import org.apache.qpid.framing.BasicCancelBody;
import org.apache.qpid.framing.BasicCancelOkBody;
import org.apache.qpid.framing.MethodRegistry;
-import org.apache.qpid.protocol.AMQMethodEvent;
import org.apache.qpid.server.AMQChannel;
import org.apache.qpid.server.protocol.AMQProtocolSession;
import org.apache.qpid.server.state.AMQStateManager;
@@ -65,7 +63,7 @@ public class BasicCancelMethodHandler implements StateAwareMethodListener<BasicC
" nowait:" + body.getNowait());
}
- channel.unsubscribeConsumer(session, body.getConsumerTag());
+ channel.unsubscribeConsumer(body.getConsumerTag());
if (!body.getNowait())
{
MethodRegistry methodRegistry = session.getMethodRegistry();
diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/BasicConsumeMethodHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/BasicConsumeMethodHandler.java
index 7cd4afdb77..5342a7f518 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/handler/BasicConsumeMethodHandler.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/handler/BasicConsumeMethodHandler.java
@@ -111,7 +111,7 @@ public class BasicConsumeMethodHandler implements StateAwareMethodListener<Basic
try
{
- AMQShortString consumerTag = channel.subscribeToQueue(consumerTagName, queue, session, !body.getNoAck(),
+ AMQShortString consumerTag = channel.subscribeToQueue(consumerTagName, queue, !body.getNoAck(),
body.getArguments(), body.getNoLocal(), body.getExclusive());
if (!body.getNowait())
{
@@ -121,8 +121,7 @@ public class BasicConsumeMethodHandler implements StateAwareMethodListener<Basic
}
- //now allow queue to start async processing of any backlog of messages
- queue.deliverAsync();
+
}
catch (org.apache.qpid.AMQInvalidArgumentException ise)
{
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 f8f9127809..b97831c3ce 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
@@ -29,6 +29,7 @@ import org.apache.qpid.framing.MethodRegistry;
import org.apache.qpid.protocol.AMQConstant;
import org.apache.qpid.server.AMQChannel;
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.security.access.Permission;
import org.apache.qpid.server.state.AMQStateManager;
@@ -65,7 +66,6 @@ public class BasicGetMethodHandler implements StateAwareMethodListener<BasicGetB
else
{
AMQQueue queue = body.getQueue() == null ? channel.getDefaultQueue() : vHost.getQueueRegistry().getQueue(body.getQueue());
-
if (queue == null)
{
_log.info("No queue for '" + body.getQueue() + "'");
diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/BasicPublishMethodHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/BasicPublishMethodHandler.java
index 0f99a21ee5..e8e42454de 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/handler/BasicPublishMethodHandler.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/handler/BasicPublishMethodHandler.java
@@ -91,7 +91,7 @@ public class BasicPublishMethodHandler implements StateAwareMethodListener<Basic
MessagePublishInfo info = session.getMethodRegistry().getProtocolVersionMethodConverter().convertToInfo(body);
info.setExchange(exchange);
- channel.setPublishFrame(info, session, e);
+ channel.setPublishFrame(info, e);
}
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/BasicQosHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/BasicQosHandler.java
index 3c95180dca..dd3281c65f 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/handler/BasicQosHandler.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/handler/BasicQosHandler.java
@@ -22,10 +22,8 @@ package org.apache.qpid.server.handler;
import org.apache.qpid.AMQException;
import org.apache.qpid.framing.BasicQosBody;
-import org.apache.qpid.framing.BasicQosOkBody;
import org.apache.qpid.framing.MethodRegistry;
import org.apache.qpid.framing.AMQMethodBody;
-import org.apache.qpid.protocol.AMQMethodEvent;
import org.apache.qpid.server.protocol.AMQProtocolSession;
import org.apache.qpid.server.state.AMQStateManager;
import org.apache.qpid.server.state.StateAwareMethodListener;
@@ -49,8 +47,8 @@ public class BasicQosHandler implements StateAwareMethodListener<BasicQosBody>
throw body.getChannelNotFoundException(channelId);
}
- channel.setPrefetchCount(body.getPrefetchCount());
- channel.setPrefetchSize(body.getPrefetchSize());
+ channel.setCredit(body.getPrefetchSize(), body.getPrefetchCount());
+
MethodRegistry methodRegistry = session.getMethodRegistry();
AMQMethodBody responseBody = methodRegistry.createBasicQosOkBody();
diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRejectMethodHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRejectMethodHandler.java
index 069cc6ea2c..f3cab10ed7 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRejectMethodHandler.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/handler/BasicRejectMethodHandler.java
@@ -22,9 +22,8 @@ package org.apache.qpid.server.handler;
import org.apache.qpid.AMQException;
import org.apache.qpid.framing.BasicRejectBody;
-import org.apache.qpid.protocol.AMQMethodEvent;
import org.apache.qpid.server.AMQChannel;
-import org.apache.qpid.server.ack.UnacknowledgedMessage;
+import org.apache.qpid.server.queue.QueueEntry;
import org.apache.qpid.server.protocol.AMQProtocolSession;
import org.apache.qpid.server.state.AMQStateManager;
import org.apache.qpid.server.state.StateAwareMethodListener;
@@ -49,16 +48,6 @@ public class BasicRejectMethodHandler implements StateAwareMethodListener<BasicR
{
AMQProtocolSession session = stateManager.getProtocolSession();
-
-
-// if (_logger.isDebugEnabled())
-// {
-// _logger.debug("Rejecting:" + evt.getMethod().deliveryTag +
-// ": Requeue:" + evt.getMethod().requeue +
-//// ": Resend:" + evt.getMethod().resend +
-// " on channel:" + channelId);
-// }
-
AMQChannel channel = session.getChannel(channelId);
if (channel == null)
@@ -76,7 +65,7 @@ public class BasicRejectMethodHandler implements StateAwareMethodListener<BasicR
long deliveryTag = body.getDeliveryTag();
- UnacknowledgedMessage message = channel.getUnacknowledgedMessageMap().get(deliveryTag);
+ QueueEntry message = channel.getUnacknowledgedMessageMap().get(deliveryTag);
if (message == null)
{
@@ -85,11 +74,16 @@ public class BasicRejectMethodHandler implements StateAwareMethodListener<BasicR
}
else
{
- if (message.isQueueDeleted() || message.getQueue().isDeleted())
+ if (message.isQueueDeleted())
{
_logger.warn("Message's Queue as already been purged, unable to Reject. " +
"Dropping message should use Dead Letter Queue");
- //sendtoDeadLetterQueue(msg)
+ message = channel.getUnacknowledgedMessageMap().remove(deliveryTag);
+ if(message != null)
+ {
+ message.discard(channel.getStoreContext());
+ }
+ //sendtoDeadLetterQueue(msg)
return;
}
@@ -111,7 +105,7 @@ public class BasicRejectMethodHandler implements StateAwareMethodListener<BasicR
// If we haven't requested message to be resent to this consumer then reject it from ever getting it.
//if (!evt.getMethod().resend)
{
- message.entry.reject();
+ message.reject();
}
if (body.getRequeue())
@@ -121,6 +115,7 @@ public class BasicRejectMethodHandler implements StateAwareMethodListener<BasicR
else
{
_logger.warn("Dropping message as requeue not required and there is no dead letter queue");
+ message = channel.getUnacknowledgedMessageMap().remove(deliveryTag);
//sendtoDeadLetterQueue(AMQMessage message)
// message.queue = channel.getDefaultDeadLetterQueue();
// channel.requeue(deliveryTag);
diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeBoundHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeBoundHandler.java
index 491a2f80db..ccd42204d9 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeBoundHandler.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/handler/ExchangeBoundHandler.java
@@ -22,12 +22,10 @@ package org.apache.qpid.server.handler;
import org.apache.qpid.AMQException;
import org.apache.qpid.framing.*;
-import org.apache.qpid.framing.amqp_8_0.MethodRegistry_8_0;
-import org.apache.qpid.protocol.AMQMethodEvent;
import org.apache.qpid.server.exchange.Exchange;
import org.apache.qpid.server.protocol.AMQProtocolSession;
-import org.apache.qpid.server.queue.AMQQueue;
import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.queue.AMQQueue;
import org.apache.qpid.server.state.AMQStateManager;
import org.apache.qpid.server.state.StateAwareMethodListener;
import org.apache.qpid.server.virtualhost.VirtualHost;
diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/QueueBindHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/QueueBindHandler.java
index 0f6dc7a19d..46182e8c00 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/handler/QueueBindHandler.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/handler/QueueBindHandler.java
@@ -112,7 +112,7 @@ public class QueueBindHandler implements StateAwareMethodListener<QueueBindBody>
if (!exch.isBound(routingKey, body.getArguments(), queue))
{
- queue.bind(routingKey, body.getArguments(), exch);
+ queue.bind(exch, routingKey, body.getArguments());
}
}
catch (AMQInvalidRoutingKeyException rke)
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 7df864f189..2241281be1 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
@@ -34,9 +34,10 @@ import org.apache.qpid.server.configuration.VirtualHostConfiguration;
import org.apache.qpid.server.exchange.Exchange;
import org.apache.qpid.server.exchange.ExchangeRegistry;
import org.apache.qpid.server.protocol.AMQProtocolSession;
-import org.apache.qpid.server.queue.AMQQueue;
import org.apache.qpid.server.queue.QueueRegistry;
import org.apache.qpid.server.security.access.Permission;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.queue.AMQQueueFactory;
import org.apache.qpid.server.state.AMQStateManager;
import org.apache.qpid.server.state.StateAwareMethodListener;
import org.apache.qpid.server.store.MessageStore;
@@ -123,7 +124,7 @@ public class QueueDeclareHandler implements StateAwareMethodListener<QueueDeclar
{
Exchange defaultExchange = exchangeRegistry.getDefaultExchange();
- queue.bind(queueName, null, defaultExchange);
+ queue.bind(defaultExchange, queueName, null);
_logger.info("Queue " + queueName + " bound to default exchange(" + defaultExchange.getName() + ")");
}
}
@@ -173,7 +174,7 @@ public class QueueDeclareHandler implements StateAwareMethodListener<QueueDeclar
{
final QueueRegistry registry = virtualHost.getQueueRegistry();
AMQShortString owner = body.getExclusive() ? session.getContextKey() : null;
- final AMQQueue queue = new AMQQueue(queueName, body.getDurable(), owner, body.getAutoDelete(), virtualHost);
+ final AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(queueName, body.getDurable(), owner, body.getAutoDelete(), virtualHost);
if (body.getExclusive() && !body.getDurable())
diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeleteHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeleteHandler.java
index 310a73ffeb..37969de795 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeleteHandler.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/handler/QueueDeleteHandler.java
@@ -24,11 +24,10 @@ import org.apache.qpid.AMQException;
import org.apache.qpid.framing.QueueDeleteBody;
import org.apache.qpid.framing.QueueDeleteOkBody;
import org.apache.qpid.framing.MethodRegistry;
-import org.apache.qpid.protocol.AMQMethodEvent;
import org.apache.qpid.protocol.AMQConstant;
import org.apache.qpid.server.protocol.AMQProtocolSession;
-import org.apache.qpid.server.queue.AMQQueue;
import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.queue.AMQQueue;
import org.apache.qpid.server.state.AMQStateManager;
import org.apache.qpid.server.state.StateAwareMethodListener;
import org.apache.qpid.server.store.MessageStore;
@@ -104,11 +103,12 @@ public class QueueDeleteHandler implements StateAwareMethodListener<QueueDeleteB
}
else
{
-
+
//Perform ACLs
virtualHost.getAccessManager().authorise(session, Permission.DELETE, body, queue);
- int purged = queue.delete(body.getIfUnused(), body.getIfEmpty());
+ int purged = queue.delete();
+
if (queue.isDurable())
{
diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/QueuePurgeHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/QueuePurgeHandler.java
index cce49f13c7..7377862875 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/handler/QueuePurgeHandler.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/handler/QueuePurgeHandler.java
@@ -23,14 +23,12 @@ package org.apache.qpid.server.handler;
import org.apache.qpid.AMQException;
import org.apache.qpid.framing.QueuePurgeBody;
-import org.apache.qpid.framing.QueuePurgeOkBody;
import org.apache.qpid.framing.MethodRegistry;
import org.apache.qpid.framing.AMQMethodBody;
import org.apache.qpid.protocol.AMQConstant;
-import org.apache.qpid.protocol.AMQMethodEvent;
import org.apache.qpid.server.protocol.AMQProtocolSession;
-import org.apache.qpid.server.queue.AMQQueue;
import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.queue.AMQQueue;
import org.apache.qpid.server.state.AMQStateManager;
import org.apache.qpid.server.state.StateAwareMethodListener;
import org.apache.qpid.server.virtualhost.VirtualHost;
diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/QueueUnbindHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/QueueUnbindHandler.java
index e758e315aa..d73e33d6c8 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/handler/QueueUnbindHandler.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/handler/QueueUnbindHandler.java
@@ -42,7 +42,7 @@ public class QueueUnbindHandler implements StateAwareMethodListener<QueueUnbindB
final AMQQueue queue;
- final AMQShortString routingKey;
+ final AMQShortString routingKey;
if (body.getQueue() == null)
{
@@ -84,7 +84,7 @@ public class QueueUnbindHandler implements StateAwareMethodListener<QueueUnbindB
try
{
- queue.unBind(routingKey, body.getArguments(), exch);
+ queue.unBind(exch, routingKey, body.getArguments());
}
catch (AMQInvalidRoutingKeyException rke)
{
diff --git a/java/broker/src/main/java/org/apache/qpid/server/handler/TxCommitHandler.java b/java/broker/src/main/java/org/apache/qpid/server/handler/TxCommitHandler.java
index 79cc722e0e..9b23d88838 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/handler/TxCommitHandler.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/handler/TxCommitHandler.java
@@ -23,10 +23,8 @@ package org.apache.qpid.server.handler;
import org.apache.log4j.Logger;
import org.apache.qpid.AMQException;
import org.apache.qpid.framing.TxCommitBody;
-import org.apache.qpid.framing.TxCommitOkBody;
import org.apache.qpid.framing.MethodRegistry;
import org.apache.qpid.framing.AMQMethodBody;
-import org.apache.qpid.protocol.AMQMethodEvent;
import org.apache.qpid.server.AMQChannel;
import org.apache.qpid.server.protocol.AMQProtocolSession;
import org.apache.qpid.server.state.AMQStateManager;
@@ -70,7 +68,7 @@ public class TxCommitHandler implements StateAwareMethodListener<TxCommitBody>
AMQMethodBody responseBody = methodRegistry.createTxCommitOkBody();
session.writeFrame(responseBody.generateFrame(channelId));
- channel.processReturns(session);
+ channel.processReturns();
}
catch (AMQException e)
{
diff --git a/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_8/ProtocolOutputConverterImpl.java b/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_8/ProtocolOutputConverterImpl.java
index d7a879180a..2b55d294b5 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_8/ProtocolOutputConverterImpl.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_8/ProtocolOutputConverterImpl.java
@@ -78,9 +78,9 @@ public class ProtocolOutputConverterImpl implements ProtocolOutputConverter
final AMQMessageHandle messageHandle = message.getMessageHandle();
final StoreContext storeContext = message.getStoreContext();
- final Long messageId = message.getMessageId();
- final int bodyCount = messageHandle.getBodyCount(storeContext,messageId);
+
+ final int bodyCount = messageHandle.getBodyCount(storeContext);
if(bodyCount == 0)
{
@@ -97,7 +97,7 @@ public class ProtocolOutputConverterImpl implements ProtocolOutputConverter
// Optimise the case where we have a single content body. In that case we create a composite block
// so that we can writeDeliver out the deliver, header and body with a single network writeDeliver.
//
- ContentChunk cb = messageHandle.getContentChunk(storeContext,messageId, 0);
+ ContentChunk cb = messageHandle.getContentChunk(storeContext, 0);
AMQDataBlock firstContentBody = new AMQFrame(channelId, getProtocolSession().getMethodRegistry().getProtocolVersionMethodConverter().convertToBody(cb));
AMQDataBlock[] blocks = new AMQDataBlock[]{deliver, contentHeader, firstContentBody};
@@ -109,7 +109,7 @@ public class ProtocolOutputConverterImpl implements ProtocolOutputConverter
//
for(int i = 1; i < bodyCount; i++)
{
- cb = messageHandle.getContentChunk(storeContext,messageId, i);
+ cb = messageHandle.getContentChunk(storeContext, i);
writeFrame(new AMQFrame(channelId, getProtocolSession().getMethodRegistry().getProtocolVersionMethodConverter().convertToBody(cb)));
}
@@ -125,7 +125,6 @@ public class ProtocolOutputConverterImpl implements ProtocolOutputConverter
final AMQMessageHandle messageHandle = message.getMessageHandle();
final StoreContext storeContext = message.getStoreContext();
- final long messageId = message.getMessageId();
AMQDataBlock deliver = createEncodedGetOkFrame(message, channelId, deliveryTag, queueSize);
@@ -133,7 +132,7 @@ public class ProtocolOutputConverterImpl implements ProtocolOutputConverter
AMQDataBlock contentHeader = ContentHeaderBody.createAMQFrame(channelId,
message.getContentHeaderBody());
- final int bodyCount = messageHandle.getBodyCount(storeContext,messageId);
+ final int bodyCount = messageHandle.getBodyCount(storeContext);
if(bodyCount == 0)
{
SmallCompositeAMQDataBlock compositeBlock = new SmallCompositeAMQDataBlock(deliver,
@@ -148,7 +147,7 @@ public class ProtocolOutputConverterImpl implements ProtocolOutputConverter
// Optimise the case where we have a single content body. In that case we create a composite block
// so that we can writeDeliver out the deliver, header and body with a single network writeDeliver.
//
- ContentChunk cb = messageHandle.getContentChunk(storeContext,messageId, 0);
+ ContentChunk cb = messageHandle.getContentChunk(storeContext, 0);
AMQDataBlock firstContentBody = new AMQFrame(channelId, getProtocolSession().getMethodRegistry().getProtocolVersionMethodConverter().convertToBody(cb));
AMQDataBlock[] blocks = new AMQDataBlock[]{deliver, contentHeader, firstContentBody};
@@ -160,7 +159,7 @@ public class ProtocolOutputConverterImpl implements ProtocolOutputConverter
//
for(int i = 1; i < bodyCount; i++)
{
- cb = messageHandle.getContentChunk(storeContext, messageId, i);
+ cb = messageHandle.getContentChunk(storeContext, i);
writeFrame(new AMQFrame(channelId, getProtocolSession().getMethodRegistry().getProtocolVersionMethodConverter().convertToBody(cb)));
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_9/ProtocolOutputConverterImpl.java b/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_9/ProtocolOutputConverterImpl.java
index 646ef43826..c76c262edd 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_9/ProtocolOutputConverterImpl.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/output/amqp0_9/ProtocolOutputConverterImpl.java
@@ -56,9 +56,9 @@ public class ProtocolOutputConverterImpl implements ProtocolOutputConverter
final AMQMessageHandle messageHandle = message.getMessageHandle();
final StoreContext storeContext = message.getStoreContext();
- final Long messageId = message.getMessageId();
- final int bodyCount = messageHandle.getBodyCount(storeContext,messageId);
+
+ final int bodyCount = messageHandle.getBodyCount(storeContext);
if(bodyCount == 0)
{
@@ -75,7 +75,7 @@ public class ProtocolOutputConverterImpl implements ProtocolOutputConverter
// Optimise the case where we have a single content body. In that case we create a composite block
// so that we can writeDeliver out the deliver, header and body with a single network writeDeliver.
//
- ContentChunk cb = messageHandle.getContentChunk(storeContext,messageId, 0);
+ ContentChunk cb = messageHandle.getContentChunk(storeContext, 0);
AMQBody firstContentBody = PROTOCOL_METHOD_CONVERTER.convertToBody(cb);
@@ -87,14 +87,13 @@ public class ProtocolOutputConverterImpl implements ProtocolOutputConverter
//
for(int i = 1; i < bodyCount; i++)
{
- cb = messageHandle.getContentChunk(storeContext,messageId, i);
+ cb = messageHandle.getContentChunk(storeContext, i);
writeFrame(new AMQFrame(channelId, PROTOCOL_METHOD_CONVERTER.convertToBody(cb)));
}
}
-
-
+
}
private AMQDataBlock createContentHeaderBlock(final int channelId, final ContentHeaderBody contentHeaderBody)
@@ -111,14 +110,13 @@ public class ProtocolOutputConverterImpl implements ProtocolOutputConverter
final AMQMessageHandle messageHandle = message.getMessageHandle();
final StoreContext storeContext = message.getStoreContext();
- final long messageId = message.getMessageId();
AMQFrame deliver = createEncodedGetOkFrame(message, channelId, deliveryTag, queueSize);
AMQDataBlock contentHeader = createContentHeaderBlock(channelId, message.getContentHeaderBody());
- final int bodyCount = messageHandle.getBodyCount(storeContext,messageId);
+ final int bodyCount = messageHandle.getBodyCount(storeContext);
if(bodyCount == 0)
{
SmallCompositeAMQDataBlock compositeBlock = new SmallCompositeAMQDataBlock(deliver,
@@ -133,7 +131,7 @@ public class ProtocolOutputConverterImpl implements ProtocolOutputConverter
// Optimise the case where we have a single content body. In that case we create a composite block
// so that we can writeDeliver out the deliver, header and body with a single network writeDeliver.
//
- ContentChunk cb = messageHandle.getContentChunk(storeContext,messageId, 0);
+ ContentChunk cb = messageHandle.getContentChunk(storeContext, 0);
AMQDataBlock firstContentBody = new AMQFrame(channelId, PROTOCOL_METHOD_CONVERTER.convertToBody(cb));
AMQDataBlock[] blocks = new AMQDataBlock[]{deliver, contentHeader, firstContentBody};
@@ -145,7 +143,7 @@ public class ProtocolOutputConverterImpl implements ProtocolOutputConverter
//
for(int i = 1; i < bodyCount; i++)
{
- cb = messageHandle.getContentChunk(storeContext, messageId, i);
+ cb = messageHandle.getContentChunk(storeContext, i);
writeFrame(new AMQFrame(channelId, PROTOCOL_METHOD_CONVERTER.convertToBody(cb)));
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQMinaProtocolSession.java b/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQMinaProtocolSession.java
index 4267642b14..c38e65fb0c 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQMinaProtocolSession.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/protocol/AMQMinaProtocolSession.java
@@ -198,7 +198,7 @@ public class AMQMinaProtocolSession implements AMQProtocolSession, Managable
}
private void frameReceived(AMQFrame frame) throws AMQException
- {
+ {
int channelId = frame.getChannel();
AMQBody body = frame.getBodyFrame();
@@ -373,7 +373,7 @@ public class AMQMinaProtocolSession implements AMQProtocolSession, Managable
AMQChannel channel = getAndAssertChannel(channelId);
- channel.publishContentHeader(body, this);
+ channel.publishContentHeader(body);
}
@@ -381,7 +381,7 @@ public class AMQMinaProtocolSession implements AMQProtocolSession, Managable
{
AMQChannel channel = getAndAssertChannel(channelId);
- channel.publishContentBody(body, this);
+ channel.publishContentBody(body);
}
public void heartbeatBodyReceived(int channelId, HeartbeatBody body)
@@ -536,7 +536,7 @@ public class AMQMinaProtocolSession implements AMQProtocolSession, Managable
{
try
{
- channel.close(this);
+ channel.close();
markChannelAwaitingCloseOk(channelId);
}
finally
@@ -602,7 +602,7 @@ public class AMQMinaProtocolSession implements AMQProtocolSession, Managable
{
for (AMQChannel channel : _channelMap.values())
{
- channel.close(this);
+ channel.close();
}
_channelMap.clear();
@@ -633,7 +633,7 @@ public class AMQMinaProtocolSession implements AMQProtocolSession, Managable
public String toString()
{
- return "AMQProtocolSession(" + _minaProtocolSession.getRemoteAddress() + ")";
+ return _minaProtocolSession.getRemoteAddress() + "("+(getAuthorizedID() == null ? "?" : getAuthorizedID().getName()+")");
}
public String dump()
@@ -739,7 +739,7 @@ public class AMQMinaProtocolSession implements AMQProtocolSession, Managable
public Object getClientIdentifier()
{
- return _minaProtocolSession.getRemoteAddress();
+ return (_minaProtocolSession != null) ? _minaProtocolSession.getRemoteAddress() : null;
}
public VirtualHost getVirtualHost()
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 f501bc27d1..cf3d07160f 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
@@ -25,25 +25,18 @@ import org.apache.qpid.AMQException;
import org.apache.qpid.framing.AMQBody;
import org.apache.qpid.framing.AMQDataBlock;
import org.apache.qpid.framing.AMQFrame;
-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;
import org.apache.qpid.framing.abstraction.ProtocolVersionMethodConverter;
import org.apache.qpid.server.protocol.AMQProtocolSession;
-import org.apache.qpid.server.registry.ApplicationRegistry;
import org.apache.qpid.server.store.MessageStore;
import org.apache.qpid.server.store.StoreContext;
import org.apache.qpid.server.txn.TransactionalContext;
import org.apache.qpid.server.exchange.Exchange;
+
-import java.util.HashMap;
-import java.util.HashSet;
import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
/**
@@ -54,20 +47,12 @@ public class AMQMessage
/** Used for debugging purposes. */
private static final Logger _log = Logger.getLogger(AMQMessage.class);
- /** Used in clustering. @todo What for? */
- private Set<Object> _tokens;
-
- /** Only use in clustering. @todo What for? */
- private AMQProtocolSession _publisher;
-
- private final Long _messageId;
-
private final AtomicInteger _referenceCount = new AtomicInteger(1);
- private AMQMessageHandle _messageHandle;
+ private final AMQMessageHandle _messageHandle;
/** Holds the transactional context in which this message is being processed. */
- private TransactionalContext _txnContext;
+ private StoreContext _storeContext;
/**
* Flag to indicate whether this message has been delivered to a consumer. Used in implementing return functionality
@@ -78,74 +63,13 @@ public class AMQMessage
/** Flag to indicate that this message requires 'immediate' delivery. */
private boolean _immediate;
- private TransientMessageData _transientMessageData = new TransientMessageData();
-
private long _expiration;
+ private Object _publisherClientInstance;
+ private Object _publisherIdentifier;
+ private final long _size;
-
- private Exchange _exchange;
- private static final boolean SYNCED_CLOCKS =
- ApplicationRegistry.getInstance().getConfiguration().getBoolean("advanced.synced-clocks", false);
-
-
- public String debugIdentity()
- {
- return "(HC:" + System.identityHashCode(this) + " ID:" + _messageId + " Ref:" + _referenceCount.get() + ")";
- }
-
- public void setExpiration()
- {
- long expiration =
- ((BasicContentHeaderProperties) _transientMessageData.getContentHeaderBody().properties).getExpiration();
- long timestamp =
- ((BasicContentHeaderProperties) _transientMessageData.getContentHeaderBody().properties).getTimestamp();
-
- if (SYNCED_CLOCKS)
- {
- _expiration = expiration;
- }
- else
- {
- // Update TTL to be in broker time.
- if (expiration != 0L)
- {
- if (timestamp != 0L)
- {
- // todo perhaps use arrival time
- long diff = (System.currentTimeMillis() - timestamp);
-
- if ((diff > 1000L) || (diff < 1000L))
- {
- _expiration = expiration + diff;
- }
- }
- }
- }
-
- }
-
- public boolean isReferenced()
- {
- return _referenceCount.get() > 0;
- }
-
- public void setExchange(final Exchange exchange)
- {
- _exchange = exchange;
- }
-
- public void route() throws AMQException
- {
- _exchange.route(this);
- }
-
- public void enqueue(final List<AMQQueue> queues)
- {
- _transientMessageData.setDestinationQueues(queues);
- }
-
/**
* Used to iterate through all the body frames associated with this message. Will not keep all the data in memory
* therefore is memory-efficient.
@@ -167,7 +91,7 @@ public class AMQMessage
{
try
{
- return _index < (_messageHandle.getBodyCount(getStoreContext(), _messageId) - 1);
+ return _index < (_messageHandle.getBodyCount(getStoreContext()) - 1);
}
catch (AMQException e)
{
@@ -184,7 +108,7 @@ public class AMQMessage
AMQBody cb =
getProtocolVersionMethodConverter().convertToBody(_messageHandle.getContentChunk(getStoreContext(),
- _messageId, ++_index));
+ ++_index));
return new AMQFrame(_channel, cb);
}
@@ -209,7 +133,7 @@ public class AMQMessage
public StoreContext getStoreContext()
{
- return _txnContext.getStoreContext();
+ return _storeContext;
}
private class BodyContentIterator implements Iterator<ContentChunk>
@@ -221,7 +145,7 @@ public class AMQMessage
{
try
{
- return _index < (_messageHandle.getBodyCount(getStoreContext(), _messageId) - 1);
+ return _index < (_messageHandle.getBodyCount(getStoreContext()) - 1);
}
catch (AMQException e)
{
@@ -235,7 +159,7 @@ public class AMQMessage
{
try
{
- return _messageHandle.getContentChunk(getStoreContext(), _messageId, ++_index);
+ return _messageHandle.getContentChunk(getStoreContext(), ++_index);
}
catch (AMQException e)
{
@@ -249,14 +173,7 @@ public class AMQMessage
}
}
- public AMQMessage(Long messageId, MessagePublishInfo info, TransactionalContext txnContext)
- {
- _messageId = messageId;
- _txnContext = txnContext;
- _immediate = info.isImmediate();
- _transientMessageData.setMessagePublishInfo(info);
- }
/**
* Used when recovering, i.e. when the message store is creating references to messages. In that case, the normal
@@ -272,137 +189,81 @@ public class AMQMessage
public AMQMessage(Long messageId, MessageStore store, MessageHandleFactory factory, TransactionalContext txnConext)
throws AMQException
{
- _messageId = messageId;
_messageHandle = factory.createMessageHandle(messageId, store, true);
- _txnContext = txnConext;
- _transientMessageData = null;
+ _storeContext = txnConext.getStoreContext();
+ _size = _messageHandle.getBodySize(txnConext.getStoreContext());
}
- /**
- * Used in testing only. This allows the passing of the content header immediately on construction.
+ /**
+ * Used when recovering, i.e. when the message store is creating references to messages. In that case, the normal
+ * enqueue/routingComplete is not done since the recovery process is responsible for routing the messages to
+ * queues.
*
- * @param messageId
- * @param info
- * @param txnContext
- * @param contentHeader
+ * @param messageHandle
+ *
+ * @throws AMQException
*/
- public AMQMessage(Long messageId, MessagePublishInfo info, TransactionalContext txnContext,
- ContentHeaderBody contentHeader) throws AMQException
+ public AMQMessage(
+ AMQMessageHandle messageHandle,
+ StoreContext storeConext,
+ MessagePublishInfo info)
+ throws AMQException
{
- this(messageId, info, txnContext);
- setContentHeaderBody(contentHeader);
- }
+ _messageHandle = messageHandle;
+ _storeContext = storeConext;
+ _immediate = info.isImmediate();
+ _size = messageHandle.getBodySize(storeConext);
- /* *
- * Used in testing only. This allows the passing of the content header and some body fragments on construction.
- *
- * @param messageId
- * @param info
- * @param txnContext
- * @param contentHeader
- * @param destinationQueues
- * @param contentBodies
- *
- * @throws AMQException
- */ /*
- public AMQMessage(Long messageId, MessagePublishInfo info, TransactionalContext txnContext,
- ContentHeaderBody contentHeader, List<AMQQueue> destinationQueues, List<ContentChunk> contentBodies,
- MessageStore messageStore, StoreContext storeContext, MessageHandleFactory messageHandleFactory) throws AMQException
- {
- this(messageId, info, txnContext, contentHeader);
- _transientMessageData.setDestinationQueues(destinationQueues);
- routingComplete(messageStore, storeContext, messageHandleFactory);
- for (ContentChunk cb : contentBodies)
- {
- addContentBodyFrame(storeContext, cb);
- }
}
- */
+
+
protected AMQMessage(AMQMessage msg) throws AMQException
{
- _messageId = msg._messageId;
_messageHandle = msg._messageHandle;
- _txnContext = msg._txnContext;
+ _storeContext = msg._storeContext;
_deliveredToConsumer = msg._deliveredToConsumer;
- _transientMessageData = msg._transientMessageData;
- }
+ _size = msg._size;
- public Iterator<AMQDataBlock> getBodyFrameIterator(AMQProtocolSession protocolSession, int channel)
- {
- return new BodyFrameIterator(protocolSession, channel);
}
- public Iterator<ContentChunk> getContentBodyIterator()
+
+ public String debugIdentity()
{
- return new BodyContentIterator();
+ return "(HC:" + System.identityHashCode(this) + " ID:" + getMessageId() + " Ref:" + _referenceCount.get() + ")";
}
- public ContentHeaderBody getContentHeaderBody() throws AMQException
+ public void setExpiration(final long expiration)
{
- if (_transientMessageData != null)
- {
- return _transientMessageData.getContentHeaderBody();
- }
- else
- {
- return _messageHandle.getContentHeaderBody(getStoreContext(), _messageId);
- }
+
+ _expiration = expiration;
+
}
- public void setContentHeaderBody(ContentHeaderBody contentHeaderBody) throws AMQException
+ public boolean isReferenced()
{
- _transientMessageData.setContentHeaderBody(contentHeaderBody);
+ return _referenceCount.get() > 0;
}
- public void routingComplete(MessageStore store, StoreContext storeContext, MessageHandleFactory factory)
- throws AMQException
+ public Iterator<AMQDataBlock> getBodyFrameIterator(AMQProtocolSession protocolSession, int channel)
{
- final boolean persistent = isPersistent();
- _messageHandle = factory.createMessageHandle(_messageId, store, persistent);
- if (persistent)
- {
- _txnContext.beginTranIfNecessary();
- }
-
- // enqueuing the messages ensure that if required the destinations are recorded to a
- // persistent store
-
- for (AMQQueue q : _transientMessageData.getDestinationQueues())
- {
- _messageHandle.enqueue(storeContext, _messageId, q);
- }
-
- if (_transientMessageData.getContentHeaderBody().bodySize == 0)
- {
- deliver(storeContext);
- }
+ return new BodyFrameIterator(protocolSession, channel);
}
- public boolean addContentBodyFrame(StoreContext storeContext, ContentChunk contentChunk) throws AMQException
+ public Iterator<ContentChunk> getContentBodyIterator()
{
- _transientMessageData.addBodyLength(contentChunk.getSize());
- final boolean allContentReceived = isAllContentReceived();
- _messageHandle.addContentBodyFrame(storeContext, _messageId, contentChunk, allContentReceived);
- if (allContentReceived)
- {
- deliver(storeContext);
-
- return true;
- }
- else
- {
- return false;
- }
+ return new BodyContentIterator();
}
- public boolean isAllContentReceived() throws AMQException
+ public ContentHeaderBody getContentHeaderBody() throws AMQException
{
- return _transientMessageData.isAllContentReceived();
+ return _messageHandle.getContentHeaderBody(getStoreContext());
}
+
+
public Long getMessageId()
{
- return _messageId;
+ return _messageHandle.getMessageId();
}
/**
@@ -456,14 +317,14 @@ public class AMQMessage
// and the handle has not yet been constructed
if (_messageHandle != null)
{
- _messageHandle.removeMessage(storeContext, _messageId);
+ _messageHandle.removeMessage(storeContext);
}
}
catch (AMQException e)
{
// to maintain consistency, we revert the count
incrementReference();
- throw new MessageCleanupException(_messageId, e);
+ throw new MessageCleanupException(getMessageId(), e);
}
}
else
@@ -476,15 +337,6 @@ public class AMQMessage
}
}
- public void setPublisher(AMQProtocolSession publisher)
- {
- _publisher = publisher;
- }
-
- public AMQProtocolSession getPublisher()
- {
- return _publisher;
- }
/**
* Called selectors to determin if the message has already been sent
@@ -496,98 +348,27 @@ public class AMQMessage
return _deliveredToConsumer;
}
-
- public boolean checkToken(Object token)
- {
-
- if (_tokens == null)
- {
- _tokens = new HashSet<Object>();
- }
-
- if (_tokens.contains(token))
- {
- return true;
- }
- else
- {
- _tokens.add(token);
-
- return false;
- }
- }
-
- /**
- * Registers a queue to which this message is to be delivered. This is called from the exchange when it is routing
- * the message. This will be called before any content bodies have been received so that the choice of
- * AMQMessageHandle implementation can be picked based on various criteria.
- *
- * @param queue the queue
- *
- * @throws org.apache.qpid.AMQException if there is an error enqueuing the message
- */
- public void enqueue(AMQQueue queue) throws AMQException
- {
- _transientMessageData.addDestinationQueue(queue);
- }
-
- /**
- * NOTE: Think about why you are using this method. Normal usages would want to do
- * AMQQueue.dequeue(StoreContext, AMQMessage)
- * This will keep the queue statistics up-to-date.
- * Currently this method is only called _correctly_ from AMQQueue dequeue.
- * Ideally we would have a better way for the queue to dequeue the message.
- * Especially since enqueue isn't the recipriocal of this method.
- * @deprecated
- * @param storeContext
- * @param queue
- * @throws AMQException
- */
- void dequeue(StoreContext storeContext, AMQQueue queue) throws AMQException
- {
- _messageHandle.dequeue(storeContext, _messageId, queue);
- }
-
public boolean isPersistent() throws AMQException
{
- if (_transientMessageData != null)
- {
- return _transientMessageData.isPersistent();
- }
- else
- {
- return _messageHandle.isPersistent(getStoreContext(), _messageId);
- }
+ return _messageHandle.isPersistent(getStoreContext());
}
/**
* Called to enforce the 'immediate' flag.
*
- * @throws NoConsumersException if the message is marked for immediate delivery but has not been marked as delivered
+ * @returns true if the message is marked for immediate delivery but has not been marked as delivered
* to a consumer
*/
- public void checkDeliveredToConsumer() throws NoConsumersException
+ public boolean immediateAndNotDelivered()
{
- if (_immediate && !_deliveredToConsumer)
- {
- throw new NoConsumersException(this);
- }
+ return (_immediate && !_deliveredToConsumer);
+
}
public MessagePublishInfo getMessagePublishInfo() throws AMQException
{
- MessagePublishInfo pb;
- if (_transientMessageData != null)
- {
- pb = _transientMessageData.getMessagePublishInfo();
- }
- else
- {
- pb = _messageHandle.getMessagePublishInfo(getStoreContext(), _messageId);
- }
-
- return pb;
+ return _messageHandle.getMessagePublishInfo(getStoreContext());
}
public boolean isRedelivered()
@@ -636,42 +417,6 @@ public class AMQMessage
_deliveredToConsumer = true;
}
- private void deliver(StoreContext storeContext) throws AMQException
- {
- // 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 = _transientMessageData.getDestinationQueues();
- if (_log.isDebugEnabled())
- {
- _log.debug("Delivering message " + debugIdentity() + " to " + destinationQueues);
- }
-
- try
- {
- // first we allow the handle to know that the message has been fully received. This is useful if it is
- // maintaining any calculated values based on content chunks
- _messageHandle.setPublishAndContentHeaderBody(storeContext, _messageId,
- _transientMessageData.getMessagePublishInfo(), _transientMessageData.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());
-
- for (AMQQueue q : destinationQueues)
- {
- // Increment the references to this message for each queue delivery.
- incrementReference();
- // normal deliver so add this message at the end.
- _txnContext.deliver(q.createEntry(this), false);
- }
- }
- finally
- {
-
- // Remove refence for routing process . Reference count should now == delivered queue count
- decrementReference(storeContext);
- }
- }
public AMQMessageHandle getMessageHandle()
@@ -681,37 +426,36 @@ public class AMQMessage
public long getSize()
{
- try
- {
- long size = getContentHeaderBody().bodySize;
+ return _size;
- return size;
- }
- catch (AMQException e)
- {
- _log.error(e.toString(), e);
-
- return 0;
- }
+ }
+ public void setPublisherClientInstance(final Object publisherClientInstance)
+ {
+ _publisherClientInstance = publisherClientInstance;
}
- public void restoreTransientMessageData() throws AMQException
+ public Object getPublisherClientInstance()
+ {
+ return _publisherClientInstance;
+ }
+
+ public Object getPublisherIdentifier()
{
- TransientMessageData transientMessageData = new TransientMessageData();
- transientMessageData.setMessagePublishInfo(getMessagePublishInfo());
- transientMessageData.setContentHeaderBody(getContentHeaderBody());
- transientMessageData.addBodyLength(getContentHeaderBody().getSize());
- _transientMessageData = transientMessageData;
+ return _publisherIdentifier;
}
+ public void setPublisherIdentifier(final Object publisherIdentifier)
+ {
+ _publisherIdentifier = publisherIdentifier;
+ }
public String toString()
{
// return "Message[" + debugIdentity() + "]: " + _messageId + "; ref count: " + _referenceCount + "; taken : " +
// _taken + " by :" + _takenBySubcription;
- return "Message[" + debugIdentity() + "]: " + _messageId + "; ref count: " + _referenceCount;
+ return "Message[" + debugIdentity() + "]: " + getMessageId() + "; ref count: " + _referenceCount;
}
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQMessageHandle.java b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQMessageHandle.java
index ede55b3bbf..a0db4ba833 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQMessageHandle.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQMessageHandle.java
@@ -29,23 +29,27 @@ import org.apache.qpid.framing.abstraction.MessagePublishInfo;
/**
* A pluggable way of getting message data. Implementations can provide intelligent caching for example or
* even no caching at all to minimise the broker memory footprint.
- *
- * The method all take a messageId to avoid having to store it in the instance - the AMQMessage container
- * must already keen the messageId so it is pointless storing it twice.
*/
public interface AMQMessageHandle
{
- ContentHeaderBody getContentHeaderBody(StoreContext context, Long messageId) throws AMQException;
+ ContentHeaderBody getContentHeaderBody(StoreContext context) throws AMQException;
+
+ /**
+ *
+ * @return the messageId for the message associated with this handle
+ */
+ Long getMessageId();
+
/**
* @return the number of body frames associated with this message
*/
- int getBodyCount(StoreContext context, Long messageId) throws AMQException;
+ int getBodyCount(StoreContext context) throws AMQException;
/**
* @return the size of the body
*/
- long getBodySize(StoreContext context, Long messageId) throws AMQException;
+ long getBodySize(StoreContext context) throws AMQException;
/**
* Get a particular content body
@@ -53,27 +57,23 @@ public interface AMQMessageHandle
* @return a content body
* @throws IllegalArgumentException if the index is invalid
*/
- ContentChunk getContentChunk(StoreContext context, Long messageId, int index) throws IllegalArgumentException, AMQException;
+ ContentChunk getContentChunk(StoreContext context, int index) throws IllegalArgumentException, AMQException;
- void addContentBodyFrame(StoreContext storeContext, Long messageId, ContentChunk contentBody, boolean isLastContentBody) throws AMQException;
+ void addContentBodyFrame(StoreContext storeContext, ContentChunk contentBody, boolean isLastContentBody) throws AMQException;
- MessagePublishInfo getMessagePublishInfo(StoreContext context, Long messageId) throws AMQException;
+ MessagePublishInfo getMessagePublishInfo(StoreContext context) throws AMQException;
boolean isRedelivered();
void setRedelivered(boolean redelivered);
- boolean isPersistent(StoreContext context, Long messageId) throws AMQException;
+ boolean isPersistent(StoreContext context) throws AMQException;
- void setPublishAndContentHeaderBody(StoreContext storeContext, Long messageId, MessagePublishInfo messagePublishInfo,
+ void setPublishAndContentHeaderBody(StoreContext storeContext, MessagePublishInfo messagePublishInfo,
ContentHeaderBody contentHeaderBody)
throws AMQException;
- void removeMessage(StoreContext storeContext, Long messageId) throws AMQException;
-
- void enqueue(StoreContext storeContext, Long messageId, AMQQueue queue) throws AMQException;
-
- void dequeue(StoreContext storeContext, Long messageId, AMQQueue queue) throws AMQException;
+ void removeMessage(StoreContext storeContext) throws AMQException;
long getArrivalTime();
}
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 7c6db0b4b3..fcfe26c4bd 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
@@ -20,1005 +20,192 @@
*/
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.registry.ApplicationRegistry;
-import org.apache.qpid.server.exchange.Exchange;
import org.apache.qpid.server.management.Managable;
-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.exchange.Exchange;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
import org.apache.qpid.server.virtualhost.VirtualHost;
+import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.subscription.Subscription;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.FieldTable;
+import org.apache.qpid.AMQException;
-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 AMQQueue implements Managable, Comparable
+import java.util.List;
+import java.util.Set;
+
+public interface AMQQueue extends Managable, Comparable
{
- /**
- * ExistingExclusiveSubscription signals a failure to create a subscription, because an exclusive subscription
- * already exists.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Represent failure to create a subscription, because an exclusive subscription already exists.
- * </table>
- *
- * @todo Not an AMQP exception as no status code.
- *
- * @todo Move to top level, used outside this class.
- */
- public static final class ExistingExclusiveSubscription extends AMQException
- {
+ AMQShortString getName();
- public ExistingExclusiveSubscription()
- {
- super("");
- }
- }
+ boolean isDurable();
- /**
- * ExistingSubscriptionPreventsExclusive signals a failure to create an exclusize subscription, as a subscription
- * already exists.
- *
- * <p/><table id="crc"><caption>CRC Card</caption>
- * <tr><th> Responsibilities <th> Collaborations
- * <tr><td> Represent failure to create an exclusize subscription, as a subscription already exists.
- * </table>
- *
- * @todo Not an AMQP exception as no status code.
- *
- * @todo Move to top level, used outside this class.
- */
- public static final class ExistingSubscriptionPreventsExclusive extends AMQException
- {
- public ExistingSubscriptionPreventsExclusive()
- {
- super("");
- }
- }
+ boolean isAutoDelete();
- private static final Logger _logger = Logger.getLogger(AMQQueue.class);
+ AMQShortString getOwner();
- private final AMQShortString _name;
+ VirtualHost getVirtualHost();
- /** null means shared */
- private final AMQShortString _owner;
- private final boolean _durable;
+ void bind(Exchange exchange, AMQShortString routingKey, FieldTable arguments) throws AMQException;
- /** If true, this queue is deleted when the last subscriber is removed */
- private final boolean _autoDelete;
+ void unBind(Exchange exchange, AMQShortString routingKey, FieldTable arguments) throws AMQException;
- /** Holds subscribers to the queue. */
- private final SubscriptionSet _subscribers;
- private final SubscriptionFactory _subscriptionFactory;
- private final AtomicInteger _subscriberCount = new AtomicInteger();
+ void registerSubscription(final Subscription subscription, final boolean exclusive) throws AMQException;
- private final AtomicBoolean _isExclusive = new AtomicBoolean();
+ void unregisterSubscription(final Subscription subscription) throws AMQException;
- private final AtomicBoolean _deleted = new AtomicBoolean(false);
+ int getConsumerCount();
- private List<Task> _deleteTaskList = new CopyOnWriteArrayList<Task>();
+ int getActiveConsumerCount();
- /** Manages message delivery. */
- private final DeliveryManager _deliveryMgr;
+ boolean isUnused();
- /** 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;
+ boolean isEmpty();
- /** max allowed size(KB) of a single message */
- @Configured(path = "maximumMessageSize", defaultValue = "0")
- public long _maximumMessageSize;
+ int getMessageCount();
- /** max allowed number of messages on a queue. */
- @Configured(path = "maximumMessageCount", defaultValue = "0")
- public long _maximumMessageCount;
+ int getUndeliveredMessageCount();
- /** 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;
+ long getQueueDepth();
- /** the minimum interval between sending out consequetive alerts of the same type */
- @Configured(path = "minimumAlertRepeatGap", defaultValue = "0")
- public long _minimumAlertRepeatGap;
+ long getReceivedMessageCount();
- /** total messages received by the queue since startup. */
- public AtomicLong _totalMessagesReceived = new AtomicLong();
+ long getOldestMessageArrivalTime();
- private final Set<NotificationCheck> _notificationChecks = EnumSet.noneOf(NotificationCheck.class);
+ boolean isDeleted();
- public AMQQueue(AMQShortString name, boolean durable, AMQShortString owner, boolean autoDelete, VirtualHost virtualHost)
- throws AMQException
- {
- this(name, durable, owner, autoDelete, virtualHost, AsyncDeliveryConfig.getAsyncDeliveryExecutor(),
- new SubscriptionSet(), new SubscriptionImpl.Factory());
- }
+ List<QueueEntry> getMessagesOnTheQueue();
- protected AMQQueue(AMQShortString name, boolean durable, AMQShortString owner, boolean autoDelete,
- VirtualHost virtualHost, SubscriptionSet subscribers) throws AMQException
- {
- this(name, durable, owner, autoDelete, virtualHost, AsyncDeliveryConfig.getAsyncDeliveryExecutor(), subscribers,
- new SubscriptionImpl.Factory());
- }
+ List<QueueEntry> getMessagesOnTheQueue(long fromMessageId, long toMessageId);
- protected AMQQueue(AMQShortString name, boolean durable, AMQShortString owner, boolean autoDelete,
- VirtualHost virtualHost, Executor asyncDelivery, SubscriptionSet subscribers,
- SubscriptionFactory subscriptionFactory) throws AMQException
- {
- if (name == null)
- {
- throw new IllegalArgumentException("Queue name must not be null");
- }
+ QueueEntry getMessageOnTheQueue(long messageId);
- if (virtualHost == null)
- {
- throw new IllegalArgumentException("Virtual Host must not be null");
- }
- _name = name;
- _durable = durable;
- _owner = owner;
- _autoDelete = autoDelete;
- _virtualHost = virtualHost;
- _asyncDelivery = asyncDelivery;
- _managedObject = createMBean();
- _managedObject.register();
+ void moveMessagesToAnotherQueue(long fromMessageId, long toMessageId, String queueName,
+ StoreContext storeContext);
- _subscribers = subscribers;
- _subscriptionFactory = subscriptionFactory;
- _deliveryMgr = new ConcurrentSelectorDeliveryManager(_subscribers, this);
+ void copyMessagesToAnotherQueue(long fromMessageId, long toMessageId, String queueName, StoreContext storeContext);
- // This ensure that the notification checks for the configured alerts are created.
- setMaximumMessageAge(_maximumMessageAge);
- setMaximumMessageCount(_maximumMessageCount);
- setMaximumMessageSize(_maximumMessageSize);
- setMaximumQueueDepth(_maximumQueueDepth);
+ void removeMessagesFromQueue(long fromMessageId, long toMessageId, StoreContext storeContext);
- }
+ void quiesce();
- private AMQQueueMBean createMBean() throws AMQException
- {
- try
- {
- return new AMQQueueMBean(this);
- }
- catch (JMException ex)
- {
- throw new AMQException("AMQQueue MBean creation has failed ", ex);
- }
- }
+ void start();
- public final AMQShortString getName()
- {
- return _name;
- }
+ void enqueueMovedMessages(final StoreContext storeContext, final List<QueueEntry> foundMessagesList);
- public boolean isShared()
- {
- return _owner == null;
- }
- public boolean isDurable()
- {
- return _durable;
- }
- public AMQShortString getOwner()
- {
- return _owner;
- }
+ long getMaximumMessageSize();
- public boolean isAutoDelete()
- {
- return _autoDelete;
- }
+ void setMaximumMessageSize(long value);
- public boolean isDeleted()
- {
- return _deleted.get();
- }
- /** @return no of messages(undelivered) on the queue. */
- public int getMessageCount()
- {
- return _deliveryMgr.getQueueMessageCount();
- }
+ long getMaximumMessageCount();
- /** @return List of messages(undelivered) on the queue. */
- public List<QueueEntry> getMessagesOnTheQueue()
- {
- return _deliveryMgr.getMessages();
- }
+ void setMaximumMessageCount(long value);
- /**
- * 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();
- }
+ long getMaximumQueueDepth();
- /**
- * @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;
- }
+ void setMaximumQueueDepth(long value);
- 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.
- startMovingMessages();
- toQueue.startMovingMessages();
-
- // 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._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);
- }
-
- // 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.stopMovingMessages();
- stopMovingMessages();
- }
- }
-
- /**
- * 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.
- startMovingMessages();
- toQueue.startMovingMessages();
-
- // 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._name, 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.stopMovingMessages();
- stopMovingMessages();
- }
- }
-
- /**
- * 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.
- startMovingMessages();
-
- // 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
- {
- stopMovingMessages();
- }
- }
-
- public void startMovingMessages()
- {
- _deliveryMgr.startMovingMessages();
- }
-
- private void enqueueMovedMessages(StoreContext storeContext, List<QueueEntry> messageList)
- {
- _deliveryMgr.enqueueMovedMessages(storeContext, messageList);
- _totalMessagesReceived.addAndGet(messageList.size());
- }
+ long getMaximumMessageAge();
- public void stopMovingMessages()
- {
- _deliveryMgr.stopMovingMessages();
- _deliveryMgr.processAsync(_asyncDelivery);
- }
+ void setMaximumMessageAge(final long maximumMessageAge);
- /** @return MBean object associated with this Queue */
- public ManagedObject getManagedObject()
- {
- return _managedObject;
- }
- public long getMaximumMessageSize()
- {
- return _maximumMessageSize;
- }
+ long getMinimumAlertRepeatGap();
- 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 _subscribers.size();
- }
+ void deleteMessageFromTop(StoreContext storeContext) throws AMQException;
- public int getActiveConsumerCount()
- {
- return _subscribers.getWeight();
- }
+ long clearQueue(StoreContext storeContext) throws AMQException;
- 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);
- }
+ int delete() throws AMQException;
+ QueueEntry enqueue(StoreContext storeContext, AMQMessage message) throws AMQException;
- }
+ void requeue(StoreContext storeContext, QueueEntry entry) throws AMQException;
- public long getMaximumQueueDepth()
- {
- return _maximumQueueDepth;
- }
+ void dequeue(StoreContext storeContext, QueueEntry entry) throws FailedDequeueException;
- // 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);
- }
+ void deliverAsync();
- }
- public long getOldestMessageArrivalTime()
- {
- return _deliveryMgr.getOldestMessageArrival();
+ boolean performGet(final AMQProtocolSession session, final AMQChannel channel, final boolean b) throws AMQException;
- }
- /** 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);
- }
+ void addQueueDeleteTask(final Task task);
- public void bind(AMQShortString routingKey, FieldTable arguments, Exchange exchange) throws AMQException
- {
- exchange.registerQueue(routingKey, this, arguments);
- if (isDurable() && exchange.isDurable())
- {
- _virtualHost.getMessageStore().bindQueue(exchange, routingKey, this, arguments);
- }
+ boolean resend(final QueueEntry entry, final Subscription subscription) throws AMQException;
- _bindings.addBinding(routingKey, arguments, exchange);
- }
+ void removeExpiredIfNoSubscribers() throws AMQException;
- public void unBind(AMQShortString routingKey, FieldTable arguments, Exchange exchange) throws AMQException
- {
- exchange.deregisterQueue(routingKey, this, arguments);
- if (isDurable() && exchange.isDurable())
- {
- _virtualHost.getMessageStore().unbindQueue(exchange, routingKey, this, arguments);
- }
+ Set<NotificationCheck> getNotificationChecks();
- _bindings.remove(routingKey, arguments, exchange);
- }
- public void registerProtocolSession(AMQProtocolSession ps, int channel, AMQShortString consumerTag, boolean acks,
- FieldTable filters, boolean noLocal, 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 session {0} with channel {1} and "
- + "consumer tag {2} with {3}", ps, channel, consumerTag, this));
- }
-
- Subscription subscription =
- _subscriptionFactory.createSubscription(channel, ps, consumerTag, acks, filters, noLocal, this);
-
- if (subscription.filtersMessages())
- {
- if (_deliveryMgr.hasQueuedMessages())
- {
- _deliveryMgr.populatePreDeliveryQueue(subscription);
- }
- }
-
- _subscribers.addSubscriber(subscription);
- if(exclusive)
- {
- _subscribers.setExclusive(true);
- }
-
- subscription.start();
- }
-
- 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 unregisterProtocolSession(AMQProtocolSession ps, int channel, AMQShortString consumerTag) throws AMQException
- {
- if (_logger.isDebugEnabled())
- {
- _logger.debug(MessageFormat.format(
- "Unregistering protocol session {0} with channel {1} and consumer tag {2} from {3}",
- ps, channel, consumerTag, this));
- }
-
- _subscribers.setExclusive(false);
- Subscription removedSubscription;
- if ((removedSubscription = _subscribers.removeSubscriber(_subscriptionFactory.createSubscription(channel, ps,
- consumerTag)))
- == null)
- {
- throw new AMQException("Protocol session with channel " + channel + " and consumer tag " + consumerTag
- + " and protocol session key " + ps.getKey() + " not registered with queue " + this);
- }
-
- removedSubscription.close();
- setExclusive(false);
- decrementSubscriberCount();
-
- // if we are eligible for auto deletion, unregister from the queue registry
- if (_autoDelete && _subscribers.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
- removedSubscription.queueDeleted(this);
- }
- }
-
- public boolean isUnused()
- {
- return _subscribers.isEmpty();
- }
-
- public boolean isEmpty()
- {
- return !_deliveryMgr.hasQueuedMessages();
- }
-
- public int delete(boolean checkUnused, boolean checkEmpty) throws AMQException
- {
- if (checkUnused && !_subscribers.isEmpty())
- {
- _logger.info("Will not delete " + this + " as it is in use.");
-
- return 0;
- }
- else if (checkEmpty && _deliveryMgr.hasQueuedMessages())
- {
- _logger.info("Will not delete " + this + " as it is not empty.");
-
- return 0;
- }
- else
- {
- delete();
-
- return _deliveryMgr.getQueueMessageCount();
- }
- }
-
- public void delete() throws AMQException
- {
- if (!_deleted.getAndSet(true))
- {
- _subscribers.queueDeleted(this);
- _bindings.deregister();
- _virtualHost.getQueueRegistry().unregisterQueue(_name);
- _managedObject.unregister();
- for (Task task : _deleteTaskList)
- {
- task.doTask(this);
- }
-
- _deleteTaskList.clear();
- }
- }
-
- 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.checkDeliveredToConsumer();
- updateReceivedMessageCount(msg);
- }
- catch (NoConsumersException e)
- {
- // as this message will be returned, it should be removed
- // from the queue:
- dequeue(storeContext, msg);
- }
- }*/
-
- // 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);
- try
- {
- msg.checkDeliveredToConsumer();
- updateReceivedMessageCount(entry);
- }
- catch (NoConsumersException e)
- {
- // as this message will be returned, it should be removed
- // from the queue:
- dequeue(storeContext, entry);
- }
- }
-
- public void dequeue(StoreContext storeContext, QueueEntry entry) throws FailedDequeueException
- {
- try
- {
- entry.getMessage().dequeue(storeContext, this);
- }
- 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);
- }
-
- protected SubscriptionManager getSubscribers()
- {
- return _subscribers;
- }
-
- protected void updateReceivedMessageCount(QueueEntry entry) throws AMQException
+ /**
+ * ExistingExclusiveSubscription signals a failure to create a subscription, because an exclusive subscription
+ * already exists.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represent failure to create a subscription, because an exclusive subscription already exists.
+ * </table>
+ *
+ * @todo Not an AMQP exception as no status code.
+ *
+ * @todo Move to top level, used outside this class.
+ */
+ static final class ExistingExclusiveSubscription extends AMQException
{
- AMQMessage msg = entry.getMessage();
-
- if (!msg.isRedelivered())
- {
- _totalMessagesReceived.incrementAndGet();
- }
- try
- {
- _managedObject.checkForNotification(msg);
- }
- catch (JMException e)
+ public ExistingExclusiveSubscription()
{
- throw new AMQException("Unable to get notification from manage queue: " + e, e);
+ super("");
}
}
- public boolean equals(Object o)
+ /**
+ * ExistingSubscriptionPreventsExclusive signals a failure to create an exclusize subscription, as a subscription
+ * already exists.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Represent failure to create an exclusize subscription, as a subscription already exists.
+ * </table>
+ *
+ * @todo Not an AMQP exception as no status code.
+ *
+ * @todo Move to top level, used outside this class.
+ */
+ static final class ExistingSubscriptionPreventsExclusive extends AMQException
{
- if (this == o)
- {
- return true;
- }
-
- if ((o == null) || (getClass() != o.getClass()))
+ public ExistingSubscriptionPreventsExclusive()
{
- return false;
+ super("");
}
-
- final AMQQueue amqQueue = (AMQQueue) o;
-
- return (_name.equals(amqQueue._name));
- }
-
- public int hashCode()
- {
- return _name.hashCode();
- }
-
- public String toString()
- {
- return "Queue(" + _name + ")@" + System.identityHashCode(this);
- }
-
- 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 static interface Task
+ static interface Task
{
public void doTask(AMQQueue queue) throws AMQException;
}
-
- public void addQueueDeleteTask(Task task)
- {
- _deleteTaskList.add(task);
- }
-
- 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, SubscriptionImpl subscription, QueueEntry entry)
- {
- _deliveryMgr.subscriberHasPendingResend(hasContent, subscription, entry);
- }
-
- public QueueEntry createEntry(AMQMessage amqMessage)
- {
- return new QueueEntry(this, amqMessage);
- }
-
- public int compareTo(Object o)
- {
- return _name.compareTo(((AMQQueue) o).getName());
- }
-
-
- public void removeExpiredIfNoSubscribers() throws AMQException
- {
- synchronized(_subscribers.getChangeLock())
- {
- if(_subscribers.isEmpty())
- {
- _deliveryMgr.removeExpired();
- }
- }
- }
-
- public final Set<NotificationCheck> getNotificationChecks()
- {
- return _notificationChecks;
- }
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/WeightedSubscriptionManager.java b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueFactory.java
index 6c71571807..c2e53ea3c7 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/queue/WeightedSubscriptionManager.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueFactory.java
@@ -7,9 +7,9 @@
* 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
@@ -20,7 +20,21 @@
*/
package org.apache.qpid.server.queue;
-public interface WeightedSubscriptionManager extends SubscriptionManager
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.server.virtualhost.VirtualHost;
+import org.apache.qpid.AMQException;
+
+
+public class AMQQueueFactory
{
- public int getWeight();
+ public static AMQQueue createAMQQueueImpl(AMQShortString name,
+ boolean durable,
+ AMQShortString owner,
+ boolean autoDelete,
+ VirtualHost virtualHost)
+ throws AMQException
+ {
+ return new AMQQueueImpl(name, durable, owner, autoDelete, virtualHost);
+ //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
new file mode 100644
index 0000000000..d8d0e599ed
--- /dev/null
+++ b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueImpl.java
@@ -0,0 +1,1003 @@
+/*
+ *
+ * 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/AMQQueueMBean.java b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java
index 348a136f9d..2ed6be77c6 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/queue/AMQQueueMBean.java
@@ -292,7 +292,7 @@ public class AMQQueueMBean extends AMQManagedObject implements ManagedQueue, Que
}
/**
- * @see org.apache.qpid.server.queue.AMQQueue#deleteMessageFromTop
+ * @see AMQQueue#deleteMessageFromTop
*/
public void deleteMessageFromTop() throws JMException
{
@@ -307,7 +307,7 @@ public class AMQQueueMBean extends AMQManagedObject implements ManagedQueue, Que
}
/**
- * @see org.apache.qpid.server.queue.AMQQueue#clearQueue
+ * @see AMQQueue#clearQueue
*/
public void clearQueue() throws JMException
{
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
index 7dfcae95c3..becd57752e 100644
--- 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
@@ -26,6 +26,7 @@ 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;
@@ -47,7 +48,7 @@ import java.util.concurrent.locks.ReentrantLock;
/** Manages delivery of messages on behalf of a queue */
-public class ConcurrentSelectorDeliveryManager implements DeliveryManager
+public class ConcurrentSelectorDeliveryManager
{
private static final Logger _log = Logger.getLogger(ConcurrentSelectorDeliveryManager.class);
@@ -60,13 +61,13 @@ public class ConcurrentSelectorDeliveryManager implements DeliveryManager
/** 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 SubscriptionManager _subscriptions;
+ 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 AMQQueue _queue;
+ private final AMQQueueImpl _queue;
/**
* Flag used while moving messages from this queue to another. For moving messages the async delivery should also
@@ -83,7 +84,7 @@ public class ConcurrentSelectorDeliveryManager implements DeliveryManager
private ReentrantLock _lock = new ReentrantLock();
private AtomicLong _totalMessageSize = new AtomicLong();
private AtomicInteger _extraMessages = new AtomicInteger();
- private Set<Subscription> _hasContent = Collections.synchronizedSet(new HashSet<Subscription>());
+ private Set<DeliveryAgent> _hasContent = Collections.synchronizedSet(new HashSet<DeliveryAgent>());
private final Object _queueHeadLock = new Object();
private String _processingThreadName = "";
@@ -91,7 +92,7 @@ public class ConcurrentSelectorDeliveryManager implements DeliveryManager
/** Used by any reaping thread to purge messages */
private StoreContext _reapingStoreContext = new StoreContext();
- ConcurrentSelectorDeliveryManager(SubscriptionManager subscriptions, AMQQueue queue)
+ ConcurrentSelectorDeliveryManager(AMQQueueImpl queue)
{
//Set values from configuration
@@ -102,11 +103,16 @@ public class ConcurrentSelectorDeliveryManager implements DeliveryManager
_log.warn("Compressing Buffers on queue.");
}
- _subscriptions = subscriptions;
_queue = queue;
}
+ public SubscriptionSet getSubscribers()
+ {
+ return _subscriptions;
+ }
+
+
private boolean addMessageToQueue(QueueEntry entry, boolean deliverFirst)
{
AMQMessage msg = entry.getMessage();
@@ -182,13 +188,17 @@ public class ConcurrentSelectorDeliveryManager implements DeliveryManager
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(subscription);
+ _hasContent.add(deliveryAgent);
_totalMessageSize.addAndGet(entry.getSize());
_extraMessages.addAndGet(1);
}
@@ -197,7 +207,7 @@ public class ConcurrentSelectorDeliveryManager implements DeliveryManager
_log.debug("Queue has removing subscriber content");
if (entry == null)
{
- _hasContent.remove(subscription);
+ _hasContent.remove(deliveryAgent);
}
else
{
@@ -302,20 +312,21 @@ public class ConcurrentSelectorDeliveryManager implements DeliveryManager
public void populatePreDeliveryQueue(Subscription subscription)
{
- if (_log.isDebugEnabled())
- {
- _log.debug("Populating PreDeliveryQueue for Subscription(" + System.identityHashCode(subscription) + ")");
- }
+ populatePreDeliveryQueue(_subscriptions.getDeliveryAgent(subscription));
+ }
+ private void populatePreDeliveryQueue(DeliveryAgent deliveryAgent)
+ {
+
Iterator<QueueEntry> currentQueue = _messages.iterator();
while (currentQueue.hasNext())
{
QueueEntry entry = currentQueue.next();
- if (subscription.hasInterest(entry))
+ if (deliveryAgent != null && deliveryAgent.hasInterest(entry))
{
- subscription.enqueueForPreDelivery(entry, false);
+ deliveryAgent.enqueueForPreDelivery(entry, false);
}
}
@@ -348,7 +359,7 @@ public class ConcurrentSelectorDeliveryManager implements DeliveryManager
{
_log.debug("No ack mode so dequeuing message immediately: " + entry.getMessage().getMessageId());
}
- _queue.dequeue(channel.getStoreContext(), entry);
+ entry.dequeue(channel.getStoreContext());
}
synchronized (channel)
{
@@ -367,12 +378,12 @@ public class ConcurrentSelectorDeliveryManager implements DeliveryManager
if (!acks)
{
- entry.getMessage().decrementReference(channel.getStoreContext());
+ entry.dispose(channel.getStoreContext());
}
}
finally
{
- entry.setDeliveredToConsumer();
+ entry.setDeliveredToSubscription();
}
return true;
@@ -414,9 +425,10 @@ public class ConcurrentSelectorDeliveryManager implements DeliveryManager
{
for (Subscription sub : _subscriptions.getSubscriptions())
{
- if (!sub.isSuspended() && sub.filtersMessages())
+ DeliveryAgent deliveryAgent = _subscriptions.getDeliveryAgent(sub);
+ if (deliveryAgent != null && !sub.isSuspended() && sub.filtersMessages())
{
- Queue<QueueEntry> preDeliveryQueue = sub.getPreDeliveryQueue();
+ Queue<QueueEntry> preDeliveryQueue = deliveryAgent.getPreDeliveryQueue();
for (QueueEntry entry : messageList)
{
preDeliveryQueue.remove(entry);
@@ -449,12 +461,12 @@ public class ConcurrentSelectorDeliveryManager implements DeliveryManager
if (entry != null)
{
- queue.dequeue(storeContext, entry);
+ 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.getMessage().decrementReference(storeContext);
+ entry.dispose(storeContext);
}
@@ -474,9 +486,9 @@ public class ConcurrentSelectorDeliveryManager implements DeliveryManager
//and remove it
_messages.poll();
- _queue.dequeue(storeContext, entry);
+ entry.dequeue(storeContext);
- entry.getMessage().decrementReference(_reapingStoreContext);
+ entry.dispose(storeContext);
entry = getNextMessage();
count++;
@@ -506,7 +518,6 @@ public class ConcurrentSelectorDeliveryManager implements DeliveryManager
//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))
{
- AMQMessage message = entry.getMessage();
//remove the already taken message or expired
QueueEntry removed = messages.poll();
@@ -514,14 +525,13 @@ public class ConcurrentSelectorDeliveryManager implements DeliveryManager
assert removed == entry;
// if the message expired then the _totalMessageSize needs adjusting
- if (message.expired(_queue) && !entry.taken(sub))
+ 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.
- _queue.dequeue(_reapingStoreContext, entry);
-
- message.decrementReference(_reapingStoreContext);
+ entry.dequeue(_reapingStoreContext);
+ entry.dispose(_reapingStoreContext);
if (_log.isInfoEnabled())
{
@@ -534,7 +544,7 @@ public class ConcurrentSelectorDeliveryManager implements DeliveryManager
if (_log.isDebugEnabled())
{
- _log.debug("Removed taken message:" + message.debugIdentity());
+ _log.debug("Removed taken message:" + entry.debugIdentity());
}
// try the next message
@@ -546,24 +556,6 @@ public class ConcurrentSelectorDeliveryManager implements DeliveryManager
/**
* This method will return true if the message is to be purged from the queue.
- *
- *
- * SIDE-EFFECT: The message will be taken by the Subscription(sub) for the current Queue(_queue)
- *
- * @param message
- * @param sub
- *
- * @return
- *
- * @throws AMQException
- */
- private boolean purgeMessage(QueueEntry message, Subscription sub) throws AMQException
- {
- return purgeMessage(message, sub, false);
- }
-
- /**
- * 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
*
@@ -599,13 +591,13 @@ public class ConcurrentSelectorDeliveryManager implements DeliveryManager
if (sub != null)
{
// if we have a queue browser(we don't purge) so check mark the message as taken
- purge = ((!sub.isBrowser() || message.isTaken()));
+ purge = ((!sub.isBrowser() || message.isAcquired()));
}
else
{
// if there is no subscription we are doing
// a get or purging so mark message as taken.
- message.isTaken();
+ message.isAcquired();
// and then ensure that it gets purged
purge = true;
}
@@ -615,17 +607,17 @@ public class ConcurrentSelectorDeliveryManager implements DeliveryManager
{
// If we are simply purging the queue don't take the message
// just purge up to the next non-taken msg.
- return purge && message.isTaken();
+ 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.taken(sub);
+ return purge && message.acquire(sub);
}
}
- public void sendNextMessage(Subscription sub, AMQQueue queue)
+ public void sendNextMessage(DeliveryAgent sub)
{
Queue<QueueEntry> messageQueue = sub.getNextQueue(_messages);
@@ -634,7 +626,7 @@ public class ConcurrentSelectorDeliveryManager implements DeliveryManager
{
_log.debug(debugIdentity() + "Async sendNextMessage for sub (" + System.identityHashCode(sub) +
") from queue (" + System.identityHashCode(messageQueue) +
- ") AMQQueue (" + System.identityHashCode(queue) + ")");
+ ") AMQQueue (" + System.identityHashCode(_queue) + ")");
}
if (messageQueue == null)
@@ -642,18 +634,18 @@ public class ConcurrentSelectorDeliveryManager implements DeliveryManager
// 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);
+ _log.info(debugIdentity() + sub + ": asked to send messages but has none on given queue:" + _queue);
}
return;
}
QueueEntry entry = null;
- QueueEntry removed = null;
+ QueueEntry removed;
try
{
synchronized (_queueHeadLock)
{
- entry = getNextMessage(messageQueue, sub, false);
+ entry = getNextMessage(messageQueue, sub.getSubscription(), false);
// message will be null if we have no messages in the messageQueue.
if (entry == null)
@@ -677,7 +669,7 @@ public class ConcurrentSelectorDeliveryManager implements DeliveryManager
_totalMessageSize.addAndGet(-entry.getSize());
}
- sub.send(entry, _queue);
+ sub.send(entry);
//remove sent message from our queue.
removed = messageQueue.poll();
@@ -739,11 +731,11 @@ public class ConcurrentSelectorDeliveryManager implements DeliveryManager
}
}
- private void cleanMainQueue(Subscription sub)
+ private void cleanMainQueue(DeliveryAgent sub)
{
try
{
- getNextMessage(_messages, sub, true);
+ getNextMessage(_messages, sub.getSubscription(), true);
}
catch (AMQException e)
{
@@ -773,7 +765,11 @@ public class ConcurrentSelectorDeliveryManager implements DeliveryManager
// Only give the message to those that want them.
if (sub.hasInterest(entry))
{
- sub.enqueueForPreDelivery(entry, true);
+ DeliveryAgent deliveryAgent = _subscriptions.getDeliveryAgent(sub);
+ if(deliveryAgent != null)
+ {
+ deliveryAgent.enqueueForPreDelivery(entry, true);
+ }
}
}
}
@@ -806,13 +802,17 @@ public class ConcurrentSelectorDeliveryManager implements DeliveryManager
for (Subscription sub : _subscriptions.getSubscriptions())
{
- synchronized (sub.getSendLock())
+ DeliveryAgent da = _subscriptions.getDeliveryAgent(sub);
+ if(da != null)
{
- if (!sub.isSuspended())
+ synchronized (da.getSendLock())
{
- sendNextMessage(sub, _queue);
+ if (!sub.isSuspended())
+ {
+ sendNextMessage(da);
- hasSubscribers = true;
+ hasSubscribers = true;
+ }
}
}
}
@@ -839,6 +839,8 @@ public class ConcurrentSelectorDeliveryManager implements DeliveryManager
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
{
@@ -861,16 +863,16 @@ public class ConcurrentSelectorDeliveryManager implements DeliveryManager
}
for (Subscription sub : _subscriptions.getSubscriptions())
{
-
+ DeliveryAgent deliveryAgent = (s==null) ? null : _subscriptions.getDeliveryAgent(sub);
// Only give the message to those that want them.
- if (sub.hasInterest(entry))
+ if (deliveryAgent != null && sub.hasInterest(entry))
{
if (debugEnabled)
{
_log.debug(debugIdentity() + "Queuing message(" + System.identityHashCode(entry) +
") for PreDelivery for subscriber(" + System.identityHashCode(sub) + ")");
}
- sub.enqueueForPreDelivery(entry, deliverFirst);
+ deliveryAgent.enqueueForPreDelivery(entry, deliverFirst);
}
}
@@ -887,9 +889,9 @@ public class ConcurrentSelectorDeliveryManager implements DeliveryManager
if (s.filtersMessages())
{
- if (s.getPreDeliveryQueue().size() > 0)
+ if (da.getPreDeliveryQueue().size() > 0)
{
- _log.error("Direct delivery from PDQ with queued msgs:" + s.getPreDeliveryQueue().size());
+ _log.error("Direct delivery from PDQ with queued msgs:" + da.getPreDeliveryQueue().size());
}
}
else if (_messages.size() > 0)
@@ -899,7 +901,8 @@ public class ConcurrentSelectorDeliveryManager implements DeliveryManager
//release lock now
_lock.unlock();
- synchronized (s.getSendLock())
+ Object sendLock = (da == null) ? new Object() : da.getSendLock();
+ synchronized (sendLock)
{
if (!s.isSuspended())
{
@@ -909,7 +912,7 @@ public class ConcurrentSelectorDeliveryManager implements DeliveryManager
System.identityHashCode(s) + ") :" + s);
}
- if (entry.taken(s))
+ if (entry.acquire(s))
{
//Message has been delivered so don't redeliver.
// This can currently occur because of the recursive call below
@@ -939,7 +942,7 @@ public class ConcurrentSelectorDeliveryManager implements DeliveryManager
return;
}
//Deliver the message
- s.send(entry, _queue);
+ da.send(entry);
}
else
{
@@ -955,7 +958,7 @@ public class ConcurrentSelectorDeliveryManager implements DeliveryManager
// 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.isTaken())
+ if (!entry.isAcquired())
{
if (debugEnabled)
{
@@ -996,6 +999,23 @@ public class ConcurrentSelectorDeliveryManager implements DeliveryManager
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()
@@ -1047,6 +1067,21 @@ public class ConcurrentSelectorDeliveryManager implements DeliveryManager
}
}
+ 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)") +
@@ -1058,4 +1093,6 @@ public class ConcurrentSelectorDeliveryManager implements DeliveryManager
" 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
new file mode 100644
index 0000000000..a43e552cef
--- /dev/null
+++ b/java/broker/src/main/java/org/apache/qpid/server/queue/DeliveryAgent.java
@@ -0,0 +1,344 @@
+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/DeliveryManager.java b/java/broker/src/main/java/org/apache/qpid/server/queue/DeliveryManager.java
deleted file mode 100644
index 1568f58e2e..0000000000
--- a/java/broker/src/main/java/org/apache/qpid/server/queue/DeliveryManager.java
+++ /dev/null
@@ -1,102 +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.concurrent.Executor;
-
-import org.apache.qpid.AMQException;
-import org.apache.qpid.framing.AMQShortString;
-import org.apache.qpid.server.AMQChannel;
-import org.apache.qpid.server.protocol.AMQProtocolSession;
-import org.apache.qpid.server.store.StoreContext;
-
-interface DeliveryManager
-{
- /**
- * Determines whether there are queued messages. Sets _queueing to false if there are no queued messages. This needs
- * to be atomic.
- *
- * @return true if there are queued messages
- */
- boolean hasQueuedMessages();
-
- /**
- * This method should not be used to determin if there are messages in the queue.
- *
- * @return int The number of messages in the queue
- *
- * @use hasQueuedMessages() for all controls relating to having messages on the queue.
- */
- int getQueueMessageCount();
-
- /**
- * Requests that the delivery manager start processing the queue asynchronously if there is work that can be done
- * (i.e. there are messages queued up and subscribers that can receive them. <p/> This should be called when
- * subscribers are added, but only after the consume-ok message has been returned as message delivery may start
- * immediately. It should also be called after unsuspending a client. <p/>
- *
- * @param executor the executor on which the delivery should take place
- */
- void processAsync(Executor executor);
-
- /**
- * Handles message delivery. The delivery manager is always in one of two modes; it is either queueing messages for
- * asynchronous delivery or delivering directly.
- *
- * @param storeContext
- * @param name the name of the entity on whose behalf we are delivering the message
- * @param entry the message to deliver
- * @param deliverFirst
- *
- * @throws org.apache.qpid.server.queue.FailedDequeueException
- * if the message could not be dequeued
- */
- void deliver(StoreContext storeContext, AMQShortString name, QueueEntry entry, boolean deliverFirst) throws FailedDequeueException, AMQException;
-
- void removeAMessageFromTop(StoreContext storeContext, AMQQueue queue) throws AMQException;
-
- long clearAllMessages(StoreContext storeContext) throws AMQException;
-
- void startMovingMessages();
-
- void enqueueMovedMessages(StoreContext context, List<QueueEntry> messageList);
-
- void stopMovingMessages();
-
- void removeMovedMessages(List<QueueEntry> messageListToRemove);
-
- List<QueueEntry> getMessages();
-
- List<QueueEntry> getMessages(long fromMessageId, long toMessageId);
-
- void populatePreDeliveryQueue(Subscription subscription);
-
- boolean performGet(AMQProtocolSession session, AMQChannel channel, boolean acks) throws AMQException;
-
- long getTotalMessageSize();
-
- long getOldestMessageArrival();
-
- void subscriberHasPendingResend(boolean hasContent, Subscription subscription, QueueEntry msg);
-
- void removeExpired() throws AMQException;
-}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/ExchangeBindings.java b/java/broker/src/main/java/org/apache/qpid/server/queue/ExchangeBindings.java
index e6377b33da..d2e5a02508 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/queue/ExchangeBindings.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/queue/ExchangeBindings.java
@@ -109,9 +109,9 @@ class ExchangeBindings
}
- public void remove(AMQShortString routingKey, FieldTable arguments, Exchange exchange)
+ public boolean remove(AMQShortString routingKey, FieldTable arguments, Exchange exchange)
{
- _bindings.remove(new ExchangeBinding(routingKey, exchange, arguments));
+ return _bindings.remove(new ExchangeBinding(routingKey, exchange, arguments));
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/InMemoryMessageHandle.java b/java/broker/src/main/java/org/apache/qpid/server/queue/InMemoryMessageHandle.java
index 630186991b..e7a99ac668 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/queue/InMemoryMessageHandle.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/queue/InMemoryMessageHandle.java
@@ -46,26 +46,34 @@ public class InMemoryMessageHandle implements AMQMessageHandle
private long _arrivalTime;
- public InMemoryMessageHandle()
+ private final Long _messageId;
+
+ public InMemoryMessageHandle(final Long messageId)
{
+ _messageId = messageId;
}
- public ContentHeaderBody getContentHeaderBody(StoreContext context, Long messageId) throws AMQException
+ public ContentHeaderBody getContentHeaderBody(StoreContext context) throws AMQException
{
return _contentHeaderBody;
}
- public int getBodyCount(StoreContext context, Long messageId)
+ public Long getMessageId()
+ {
+ return _messageId;
+ }
+
+ public int getBodyCount(StoreContext context)
{
return _contentBodies.size();
}
- public long getBodySize(StoreContext context, Long messageId) throws AMQException
+ public long getBodySize(StoreContext context) throws AMQException
{
- return getContentHeaderBody(context, messageId).bodySize;
+ return getContentHeaderBody(context).bodySize;
}
- public ContentChunk getContentChunk(StoreContext context, Long messageId, int index) throws AMQException, IllegalArgumentException
+ public ContentChunk getContentChunk(StoreContext context, int index) throws AMQException, IllegalArgumentException
{
if (index > _contentBodies.size() - 1)
{
@@ -75,13 +83,13 @@ public class InMemoryMessageHandle implements AMQMessageHandle
return _contentBodies.get(index);
}
- public void addContentBodyFrame(StoreContext storeContext, Long messageId, ContentChunk contentBody, boolean isLastContentBody)
+ public void addContentBodyFrame(StoreContext storeContext, ContentChunk contentBody, boolean isLastContentBody)
throws AMQException
{
_contentBodies.add(contentBody);
}
- public MessagePublishInfo getMessagePublishInfo(StoreContext context, Long messageId) throws AMQException
+ public MessagePublishInfo getMessagePublishInfo(StoreContext context) throws AMQException
{
return _messagePublishInfo;
}
@@ -97,10 +105,10 @@ public class InMemoryMessageHandle implements AMQMessageHandle
_redelivered = redelivered;
}
- public boolean isPersistent(StoreContext context, Long messageId) throws AMQException
+ public boolean isPersistent(StoreContext context) throws AMQException
{
//todo remove literal values to a constant file such as AMQConstants in common
- ContentHeaderBody chb = getContentHeaderBody(context, messageId);
+ ContentHeaderBody chb = getContentHeaderBody(context);
return chb.properties instanceof BasicContentHeaderProperties &&
((BasicContentHeaderProperties) chb.properties).getDeliveryMode() == 2;
}
@@ -111,7 +119,7 @@ public class InMemoryMessageHandle implements AMQMessageHandle
* @param contentHeaderBody
* @throws AMQException
*/
- public void setPublishAndContentHeaderBody(StoreContext storeContext, Long messageId, MessagePublishInfo messagePublishInfo,
+ public void setPublishAndContentHeaderBody(StoreContext storeContext, MessagePublishInfo messagePublishInfo,
ContentHeaderBody contentHeaderBody)
throws AMQException
{
@@ -120,17 +128,7 @@ public class InMemoryMessageHandle implements AMQMessageHandle
_arrivalTime = System.currentTimeMillis();
}
- public void removeMessage(StoreContext storeContext, Long messageId) throws AMQException
- {
- // NO OP
- }
-
- public void enqueue(StoreContext storeContext, Long messageId, AMQQueue queue) throws AMQException
- {
- // NO OP
- }
-
- public void dequeue(StoreContext storeContext, Long messageId, AMQQueue queue) throws AMQException
+ public void removeMessage(StoreContext storeContext) throws AMQException
{
// NO OP
}
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
new file mode 100644
index 0000000000..2dd9f14574
--- /dev/null
+++ b/java/broker/src/main/java/org/apache/qpid/server/queue/IncomingMessage.java
@@ -0,0 +1,310 @@
+/*
+ *
+ * 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.abstraction.MessagePublishInfo;
+import org.apache.qpid.framing.abstraction.ContentChunk;
+import org.apache.qpid.framing.ContentHeaderBody;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.framing.BasicContentHeaderProperties;
+import org.apache.qpid.server.txn.TransactionalContext;
+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.registry.ApplicationRegistry;
+import org.apache.qpid.server.exchange.NoRouteException;
+import org.apache.qpid.server.exchange.Exchange;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.common.ClientProperties;
+import org.apache.log4j.Logger;
+
+import java.util.List;
+import java.util.ArrayList;
+
+public class IncomingMessage
+{
+
+ /** Used for debugging purposes. */
+ private static final Logger _logger = Logger.getLogger(IncomingMessage.class);
+
+ private static final boolean SYNCHED_CLOCKS =
+ ApplicationRegistry.getInstance().getConfiguration().getBoolean("advanced.synced-clocks", false);
+
+ private final MessagePublishInfo _messagePublishInfo;
+ private ContentHeaderBody _contentHeaderBody;
+ private AMQMessageHandle _messageHandle;
+ private final Long _messageId;
+ private final TransactionalContext _txnContext;
+
+
+
+ /**
+ * Keeps a track of how many bytes we have received in body frames
+ */
+ private long _bodyLengthReceived = 0;
+
+ /**
+ * This is stored during routing, to know the queues to which this message should immediately be
+ * 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 AMQProtocolSession _publisher;
+ private MessageStore _messageStore;
+ private long _expiration;
+
+ private Exchange _exchange;
+
+
+ public IncomingMessage(final Long messageId,
+ final MessagePublishInfo info,
+ final TransactionalContext txnContext,
+ final AMQProtocolSession publisher)
+ {
+ _messageId = messageId;
+ _messagePublishInfo = info;
+ _txnContext = txnContext;
+ _publisher = publisher;
+
+ }
+
+ public void setContentHeaderBody(final ContentHeaderBody contentHeaderBody) throws AMQException
+ {
+ _contentHeaderBody = contentHeaderBody;
+ }
+
+ public void setExpiration()
+ {
+ long expiration =
+ ((BasicContentHeaderProperties) _contentHeaderBody.properties).getExpiration();
+ long timestamp =
+ ((BasicContentHeaderProperties) _contentHeaderBody.properties).getTimestamp();
+
+ if (SYNCHED_CLOCKS)
+ {
+ _expiration = expiration;
+ }
+ else
+ {
+ // Update TTL to be in broker time.
+ if (expiration != 0L)
+ {
+ if (timestamp != 0L)
+ {
+ // todo perhaps use arrival time
+ long diff = (System.currentTimeMillis() - timestamp);
+
+ if ((diff > 1000L) || (diff < 1000L))
+ {
+ _expiration = expiration + diff;
+ }
+ }
+ }
+ }
+
+ }
+
+ public void routingComplete(final MessageStore store,
+ final MessageHandleFactory factory) throws AMQException
+ {
+
+ final boolean persistent = isPersistent();
+ _messageHandle = factory.createMessageHandle(_messageId, store, persistent);
+ if (persistent)
+ {
+ _txnContext.beginTranIfNecessary();
+ // enqueuing the messages ensure that if required the destinations are recorded to a
+ // persistent store
+
+ if(_destinationQueues != null)
+ {
+ for (AMQQueue q : _destinationQueues)
+ {
+ if(q.isDurable())
+ {
+
+ _messageStore.enqueueMessage(_txnContext.getStoreContext(), q.getName(), _messageId);
+ }
+ }
+ }
+
+ }
+
+
+
+
+ }
+
+ public AMQMessage deliverToQueues()
+ throws AMQException
+ {
+
+ // 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;
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Delivering message " + _messageId + " to " + destinationQueues);
+ }
+
+ AMQMessage message = null;
+
+ try
+ {
+ // first we allow the handle to know that the message has been fully received. This is useful if it is
+ // maintaining any calculated values based on content chunks
+ _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());
+ message.setExpiration(_expiration);
+
+ if (_publisher.getClientProperties() != null)
+ {
+ message.setPublisherClientInstance(_publisher.getClientProperties().getObject(ClientProperties.instance.toAMQShortString()));
+ }
+
+
+
+ if ((destinationQueues == null) || destinationQueues.isEmpty())
+ {
+
+ if (isMandatory() || isImmediate())
+ {
+ throw new NoRouteException("No Route for message", message);
+
+ }
+ else
+ {
+ _logger.warn("MESSAGE DISCARDED: No routes for message - " + message);
+ }
+ }
+ else
+ {
+ for (AMQQueue q : destinationQueues)
+ {
+ // Increment the references to this message for each queue delivery.
+ message.incrementReference();
+ // normal deliver so add this message at the end.
+ _txnContext.deliver(q, message);
+ }
+ }
+
+ return message;
+ }
+ finally
+ {
+ // Remove refence for routing process . Reference count should now == delivered queue count
+ if(message != null) message.decrementReference(_txnContext.getStoreContext());
+ }
+
+ }
+
+ public void addContentBodyFrame(final ContentChunk contentChunk)
+ throws AMQException
+ {
+
+ _bodyLengthReceived += contentChunk.getSize();
+
+ _messageHandle.addContentBodyFrame(_txnContext.getStoreContext(), contentChunk, allContentReceived());
+
+ }
+
+ public boolean allContentReceived()
+ {
+ return (_bodyLengthReceived == getContentHeaderBody().bodySize);
+ }
+
+ public AMQShortString getExchange() throws AMQException
+ {
+ return _messagePublishInfo.getExchange();
+ }
+
+ public AMQShortString getRoutingKey() throws AMQException
+ {
+ return _messagePublishInfo.getRoutingKey();
+ }
+
+ public boolean isMandatory() throws AMQException
+ {
+ return _messagePublishInfo.isMandatory();
+ }
+
+
+ public boolean isImmediate() throws AMQException
+ {
+ return _messagePublishInfo.isImmediate();
+ }
+
+
+ public void enqueue(final AMQQueue q) throws AMQException
+ {
+ if(_destinationQueues == null)
+ {
+ _destinationQueues = new ArrayList<AMQQueue>();
+ }
+ _destinationQueues.add(q);
+ }
+
+ public ContentHeaderBody getContentHeaderBody()
+ {
+ return _contentHeaderBody;
+ }
+
+
+ public boolean isPersistent()
+ {
+ //todo remove literal values to a constant file such as AMQConstants in common
+ return getContentHeaderBody().properties instanceof BasicContentHeaderProperties &&
+ ((BasicContentHeaderProperties) getContentHeaderBody().properties).getDeliveryMode() == 2;
+ }
+
+ public void setMessageStore(final MessageStore messageStore)
+ {
+ _messageStore = messageStore;
+ }
+
+ public Long getMessageId()
+ {
+ return _messageId;
+ }
+
+ public void setExchange(final Exchange e)
+ {
+ _exchange = e;
+ }
+
+ public void route() throws AMQException
+ {
+ _exchange.route(this);
+ }
+
+ public void enqueue(final List<AMQQueue> queues)
+ {
+ _destinationQueues = queues;
+ }
+}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/MessageHandleFactory.java b/java/broker/src/main/java/org/apache/qpid/server/queue/MessageHandleFactory.java
index 94ab935115..0b214ca336 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/queue/MessageHandleFactory.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/queue/MessageHandleFactory.java
@@ -36,11 +36,11 @@ public class MessageHandleFactory
// just hardcoded for now
if (persistent)
{
- return new WeakReferenceMessageHandle(store);
+ return new WeakReferenceMessageHandle(messageId, store);
}
else
{
- return new InMemoryMessageHandle();
+ return new InMemoryMessageHandle(messageId);
}
}
}
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 8553db3e09..08dcd0e329 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
@@ -1,173 +1,74 @@
-/*
- *
- * 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.server.store.StoreContext;
-import org.apache.log4j.Logger;
+import org.apache.qpid.server.subscription.Subscription;
+
+/*
+*
+* 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 interface QueueEntry extends Comparable<QueueEntry>
+{
+ AMQQueue getQueue();
-import java.util.Set;
-import java.util.HashSet;
-import java.util.concurrent.atomic.AtomicReference;
+ AMQMessage getMessage();
+ long getSize();
-public class QueueEntry
-{
+ boolean getDeliveredToConsumer();
+
+ boolean expired() throws AMQException;
+
+ boolean isAcquired();
+
+ boolean acquire(Subscription sub);
+
+ boolean acquiredBySubscription();
+
+ void setDeliveredToSubscription();
+
+ void release();
+
+ String debugIdentity();
+
+ boolean immediateAndNotDelivered();
+
+ void setRedelivered(boolean b);
+
+ Subscription getDeliveredSubscription();
+
+ void reject();
+
+ void reject(Subscription subscription);
+
+ boolean isRejectedBy(Subscription subscription);
+
+ void requeue(StoreContext storeContext) throws AMQException;
+
+ void dequeue(final StoreContext storeContext) throws FailedDequeueException;
+
+ void dispose(final StoreContext storeContext) throws MessageCleanupException;
- /**
- * Used for debugging purposes.
- */
- private static final Logger _log = Logger.getLogger(QueueEntry.class);
-
- private final AMQQueue _queue;
- private final AMQMessage _message;
-
- private Set<Subscription> _rejectedBy = null;
-
- private AtomicReference<Object> _owner = new AtomicReference<Object>();
-
-
- public QueueEntry(AMQQueue queue, AMQMessage message)
- {
- _queue = queue;
- _message = message;
- }
-
-
- public AMQQueue getQueue()
- {
- return _queue;
- }
-
- public AMQMessage getMessage()
- {
- return _message;
- }
-
- public long getSize()
- {
- return getMessage().getSize();
- }
-
- public boolean getDeliveredToConsumer()
- {
- return getMessage().getDeliveredToConsumer();
- }
-
- public boolean expired() throws AMQException
- {
- return getMessage().expired(_queue);
- }
-
- public boolean isTaken()
- {
- return _owner.get() != null;
- }
-
- public boolean taken(Subscription sub)
- {
- return !(_owner.compareAndSet(null, sub == null ? this : sub));
- }
-
- public void setDeliveredToConsumer()
- {
- getMessage().setDeliveredToConsumer();
- }
-
- public void release()
- {
- _owner.set(null);
- }
-
- public String debugIdentity()
- {
- return getMessage().debugIdentity();
- }
-
- public void process(StoreContext storeContext, boolean deliverFirst) throws AMQException
- {
- _queue.process(storeContext, this, deliverFirst);
- }
-
- public void checkDeliveredToConsumer() throws NoConsumersException
- {
- _message.checkDeliveredToConsumer();
- }
-
- public void setRedelivered(boolean b)
- {
- getMessage().setRedelivered(b);
- }
-
- public Subscription getDeliveredSubscription()
- {
- synchronized (this)
- {
- Object owner = _owner.get();
- if (owner instanceof Subscription)
- {
- return (Subscription) owner;
- }
- else
- {
- return null;
- }
- }
- }
-
- public void reject()
- {
- reject(getDeliveredSubscription());
- }
-
- public void reject(Subscription subscription)
- {
- if (subscription != null)
- {
- if (_rejectedBy == null)
- {
- _rejectedBy = new HashSet<Subscription>();
- }
-
- _rejectedBy.add(subscription);
- }
- else
- {
- _log.warn("Requesting rejection by null subscriber:" + debugIdentity());
- }
- }
-
- public boolean isRejectedBy(Subscription subscription)
- {
- boolean rejected = _rejectedBy != null;
-
- if (rejected) // We have subscriptions that rejected this message
- {
- return _rejectedBy.contains(subscription);
- }
- else // This messasge hasn't been rejected yet.
- {
- return rejected;
- }
- }
+ void restoreCredit();
+ void discard(StoreContext storeContext) throws AMQException;
+ boolean isQueueDeleted();
}
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
new file mode 100644
index 0000000000..54404e23e7
--- /dev/null
+++ b/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryImpl.java
@@ -0,0 +1,240 @@
+/*
+ *
+ * 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.server.store.StoreContext;
+import org.apache.qpid.server.subscription.Subscription;
+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;
+
+
+public class QueueEntryImpl implements QueueEntry
+{
+
+ /**
+ * Used for debugging purposes.
+ */
+ private static final Logger _log = Logger.getLogger(QueueEntryImpl.class);
+
+ private final AMQQueue _queue;
+ private final AMQMessage _message;
+
+
+ private Set<Subscription> _rejectedBy = null;
+
+ private final AtomicReference<Object> _owner = new AtomicReference<Object>();
+ private final AtomicLong _entryId = new AtomicLong();
+
+
+ public QueueEntryImpl(AMQQueue queue, AMQMessage message, final long entryId)
+ {
+ _queue = queue;
+ _message = message;
+ _entryId.set(entryId);
+ }
+
+ public QueueEntryImpl(AMQQueue queue, AMQMessage message)
+ {
+ _queue = queue;
+ _message = message;
+ }
+
+ protected void setEntryId(long entryId)
+ {
+ _entryId.set(entryId);
+ }
+
+ protected long getEntryId()
+ {
+ return _entryId.get();
+ }
+
+ public AMQQueue getQueue()
+ {
+ return _queue;
+ }
+
+ public AMQMessage getMessage()
+ {
+ return _message;
+ }
+
+ public long getSize()
+ {
+ return getMessage().getSize();
+ }
+
+ public boolean getDeliveredToConsumer()
+ {
+ return getMessage().getDeliveredToConsumer();
+ }
+
+ public boolean expired() throws AMQException
+ {
+ return getMessage().expired(_queue);
+ }
+
+ public boolean isAcquired()
+ {
+ return _owner.get() != null;
+ }
+
+ public boolean acquire(Subscription sub)
+ {
+ return !(_owner.compareAndSet(null, sub == null ? this : sub));
+ }
+
+ public boolean acquiredBySubscription()
+ {
+ Object owner = _owner.get();
+ return (owner != null) && (owner != this);
+ }
+
+ public void setDeliveredToSubscription()
+ {
+ getMessage().setDeliveredToConsumer();
+ }
+
+ public void release()
+ {
+ _owner.set(null);
+ }
+
+ public String debugIdentity()
+ {
+ return getMessage().debugIdentity();
+ }
+
+
+ public boolean immediateAndNotDelivered()
+ {
+ return _message.immediateAndNotDelivered();
+ }
+
+ public void setRedelivered(boolean b)
+ {
+ getMessage().setRedelivered(b);
+ }
+
+ public Subscription getDeliveredSubscription()
+ {
+ synchronized (this)
+ {
+ Object owner = _owner.get();
+ if (owner instanceof Subscription)
+ {
+ return (Subscription) owner;
+ }
+ else
+ {
+ return null;
+ }
+ }
+ }
+
+ public void reject()
+ {
+ reject(getDeliveredSubscription());
+ }
+
+ public void reject(Subscription subscription)
+ {
+ if (subscription != null)
+ {
+ if (_rejectedBy == null)
+ {
+ _rejectedBy = new HashSet<Subscription>();
+ }
+
+ _rejectedBy.add(subscription);
+ }
+ else
+ {
+ _log.warn("Requesting rejection by null subscriber:" + debugIdentity());
+ }
+ }
+
+ public boolean isRejectedBy(Subscription subscription)
+ {
+
+ if (_rejectedBy != null) // We have subscriptions that rejected this message
+ {
+ return _rejectedBy.contains(subscription);
+ }
+ else // This messasge hasn't been rejected yet.
+ {
+ return false;
+ }
+ }
+
+
+ public void requeue(final StoreContext storeContext) throws AMQException
+ {
+ _queue.requeue(storeContext, this);
+ }
+
+ public void dequeue(final StoreContext storeContext) throws FailedDequeueException
+ {
+ getQueue().dequeue(storeContext, this);
+ }
+
+ public void dispose(final StoreContext storeContext) throws MessageCleanupException
+ {
+ getMessage().decrementReference(storeContext);
+ }
+
+ public void restoreCredit()
+ {
+ Object owner = _owner.get();
+ if(owner instanceof Subscription)
+ {
+ Subscription s = (Subscription) owner;
+ s.restoreCredit(this);
+ }
+ }
+
+ public void discard(StoreContext storeContext) throws AMQException
+ {
+ //if the queue is null then the message is waiting to be acked, but has been removed.
+ if (getQueue() != null)
+ {
+ dequeue(storeContext);
+ }
+
+ dispose(storeContext);
+ }
+
+ public boolean isQueueDeleted()
+ {
+ return getQueue().isDeleted();
+ }
+
+ public int compareTo(final QueueEntry o)
+ {
+ QueueEntryImpl other = (QueueEntryImpl)o;
+ return getEntryId() > other.getEntryId() ? 1 : getEntryId() < other.getEntryId() ? -1 : 0;
+ }
+}
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
new file mode 100644
index 0000000000..fc32909079
--- /dev/null
+++ b/java/broker/src/main/java/org/apache/qpid/server/queue/QueueEntryList.java
@@ -0,0 +1,220 @@
+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
+* 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 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);
+ }
+
+
+ public QueueEntryNode getHead()
+ {
+ return _head;
+ }
+
+
+
+
+}
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
new file mode 100644
index 0000000000..b028d60b19
--- /dev/null
+++ b/java/broker/src/main/java/org/apache/qpid/server/queue/SimpleAMQQueue.java
@@ -0,0 +1,1174 @@
+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.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;
+import org.apache.qpid.server.management.ManagedObject;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.configuration.Configured;
+import org.apache.log4j.Logger;
+
+import javax.management.JMException;
+import java.util.List;
+import java.util.Set;
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.Executor;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
+
+/*
+*
+* 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 SimpleAMQQueue implements AMQQueue, Subscription.StateListener
+{
+ private static final Logger _logger = Logger.getLogger(SimpleAMQQueue.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 VirtualHost _virtualHost;
+
+ /** Used to track bindings to exchanges so that on deletion they can easily be cancelled. */
+ private final ExchangeBindings _bindings = new ExchangeBindings(this);
+
+ private final AtomicBoolean _deleted = new AtomicBoolean(false);
+
+ private final List<Task> _deleteTaskList = new CopyOnWriteArrayList<Task>();
+
+ private final AtomicInteger _messageCount = new AtomicInteger(0);
+
+ private final AtomicLong _depth = new AtomicLong(0L);
+
+
+ private final AtomicInteger _activeSubscriberCount = new AtomicInteger();
+
+ private final AtomicBoolean _quiesced = new AtomicBoolean(false);
+
+ /**
+ * the _enqueueLock is used to control the entry of new messages onto the queue. In normal operation many threads
+ * may concurrently enqueue messages. However while certain operations are being carried out (e.g. clearing the
+ * queue), it is important to prevent new messages being added to the queue. To obtain this behaviour we use the
+ * readLock for shared "enqueue" access and the write lock for the exclusive access.
+ */
+ // private final ReadWriteLock _enqueueLock = new ReentrantReadWriteLock();
+
+
+ private final Lock _subscriberLock = new ReentrantLock();
+
+// private final List<Subscription> _subscriberList = new CopyOnWriteArrayList<Subscription>();
+
+
+ private final SubscriptionList _subscriptionList = new SubscriptionList(this);
+ private final AtomicReference<SubscriptionList.SubscriptionNode> _lastSubscriptionNode = new AtomicReference<SubscriptionList.SubscriptionNode>(_subscriptionList.getHead());
+
+ private boolean _exclusiveSubscriber;
+
+
+ private final QueueEntryList _entries;
+
+
+ private final AMQQueueMBean _managedObject;
+ private final Executor _asyncDelivery;
+ private final AtomicLong _totalMessagesReceived = new AtomicLong();
+
+
+
+ /** 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;
+
+ private final Set<NotificationCheck> _notificationChecks = EnumSet.noneOf(NotificationCheck.class);
+
+
+ private final AtomicLong _stateChangeCount = new AtomicLong(Long.MIN_VALUE);
+ private AtomicReference _asynchronousRunner = new AtomicReference(null);
+ private AtomicInteger _deliveredMessages = new AtomicInteger();
+
+
+ SimpleAMQQueue(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;
+ _entries = new QueueEntryList(this);
+
+ _asyncDelivery = AsyncDeliveryConfig.getAsyncDeliveryExecutor();
+
+ try
+ {
+ _managedObject = new AMQQueueMBean(this);
+ _managedObject.register();
+ }
+ catch (JMException e)
+ {
+ throw new AMQException("AMQQueue MBean creation has failed ", e);
+ }
+
+
+ // This ensure that the notification checks for the configured alerts are created.
+ setMaximumMessageAge(_maximumMessageAge);
+ setMaximumMessageCount(_maximumMessageCount);
+ setMaximumMessageSize(_maximumMessageSize);
+ setMaximumQueueDepth(_maximumQueueDepth);
+
+ }
+
+ // ------ Getters and Setters
+
+ public AMQShortString getName()
+ {
+ return _name;
+ }
+
+ public boolean isDurable()
+ {
+ return _durable;
+ }
+
+ public boolean isAutoDelete()
+ {
+ return _autoDelete;
+ }
+
+ public AMQShortString getOwner()
+ {
+ return _owner;
+ }
+
+ public VirtualHost getVirtualHost()
+ {
+ return _virtualHost;
+ }
+
+
+ // ------ bind and unbind
+
+ 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");
+ }
+ }
+
+
+ // ------ Manage Subscriptions
+
+ public void registerSubscription(final Subscription subscription, final boolean exclusive) throws AMQException
+ {
+
+
+ if(_exclusiveSubscriber)
+ {
+ throw new ExistingExclusiveSubscription();
+ }
+
+ if(exclusive && getConsumerCount() != 0)
+ {
+ throw new ExistingSubscriptionPreventsExclusive();
+ }
+
+ _exclusiveSubscriber = exclusive;
+
+ _activeSubscriberCount.incrementAndGet();
+ subscription.setStateListener(this);
+ subscription.setQueueContext(null,_entries.getHead());
+
+ if(!_deleted.get())
+ {
+ subscription.setQueue(this);
+ _subscriptionList.add(subscription);
+ if(_deleted.get())
+ {
+ subscription.queueDeleted(this);
+ }
+ }
+ else
+ {
+ // TODO
+ }
+
+
+ deliverAsync();
+
+ }
+
+ public void unregisterSubscription(final Subscription subscription) throws AMQException
+ {
+ if(subscription == null)
+ {
+ throw new NullPointerException("subscription argument is null");
+ }
+
+
+
+
+ boolean removed = _subscriptionList.remove(subscription);
+
+
+
+ if(removed)
+ {
+ subscription.close();
+ // No longer can the queue have an exclusive consumer
+ _exclusiveSubscriber = false;
+
+
+
+
+
+ // auto-delete queues must be deleted if there are no remaining subscribers
+
+ if (_autoDelete && getConsumerCount() == 0)
+ {
+ if (_logger.isInfoEnabled())
+ {
+ _logger.info("Auto-deleteing queue:" + this);
+ }
+
+ delete();
+
+ // 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);
+ }
+ }
+
+
+ }
+
+
+ // ------ Enqueue / Dequeue
+
+ public QueueEntry enqueue(StoreContext storeContext, AMQMessage message) throws AMQException
+ {
+ // need to get the enqueue lock
+
+
+ _messageCount.incrementAndGet();
+
+ _totalMessagesReceived.incrementAndGet();
+ _depth.addAndGet(message.getSize());
+
+ QueueEntry entry = _entries.add(message);
+
+ /*
+
+ iterate over subscriptions and if any is at the end of the queue and can deliver this message, then deliver the message
+
+ */
+ SubscriptionList.SubscriptionNode node = _lastSubscriptionNode.get();
+ SubscriptionList.SubscriptionNode nextNode = node.getNext();
+ if(nextNode == null)
+ {
+ nextNode = _subscriptionList.getHead().getNext();
+ }
+ while(nextNode != null)
+ {
+ if(_lastSubscriptionNode.compareAndSet(node, nextNode))
+ {
+ break;
+ }
+ else
+ {
+ node = _lastSubscriptionNode.get();
+ nextNode = node.getNext();
+ if(nextNode == null)
+ {
+ nextNode = _subscriptionList.getHead().getNext();
+ }
+ }
+ }
+
+
+
+ int loops = 2;
+
+ while(!entry.isAcquired() && loops != 0)
+ {
+ if(nextNode == null)
+ {
+ loops--;
+ nextNode = _subscriptionList.getHead();
+ }
+ else
+ {
+ // if subscription at end, and active, offer
+ Subscription sub = nextNode.getSubscription();
+ synchronized(sub.getSendLock())
+ {
+ if(subscriptionReady(sub, entry)
+ && !sub.isSuspended()
+ && sub.isActive())
+ {
+
+ if( sub.hasInterest(entry) )
+ {
+ if( !sub.wouldSuspend(entry))
+ {
+ if(!sub.isBrowser() && entry.acquire(sub))
+ {
+ sub.restoreCredit(entry);
+ }
+ else
+ {
+ deliverMessage(sub, entry);
+ QueueEntryList.QueueEntryNode queueEntryNode = (QueueEntryList.QueueEntryNode) sub.getQueueContext();
+ if(queueEntryNode.getNext() == entry)
+ {
+ sub.setQueueContext(queueEntryNode,entry);
+ }
+
+ }
+ }
+ }
+ else
+ {
+ QueueEntryList.QueueEntryNode queueEntryNode = (QueueEntryList.QueueEntryNode) sub.getQueueContext();
+ if(queueEntryNode.getNext() == entry)
+ {
+ sub.setQueueContext(queueEntryNode,entry);
+ }
+ }
+ }
+ }
+ }
+ nextNode = nextNode.getNext();
+
+ }
+
+
+
+ if(entry.immediateAndNotDelivered())
+ {
+ dequeue(storeContext, entry);
+ }
+ else if(!entry.isAcquired())
+ {
+ deliverAsync();
+ }
+
+
+ try
+ {
+ _managedObject.checkForNotification(entry.getMessage());
+ }
+ catch (JMException e)
+ {
+ throw new AMQException("Unable to get notification from manage queue: " + e, e);
+ }
+
+
+ return entry;
+
+ }
+
+ private void deliverMessage(final Subscription sub, final QueueEntry entry)
+ throws AMQException
+ {
+ _deliveredMessages.incrementAndGet();
+ sub.send(entry);
+
+ }
+
+ private boolean subscriptionReady(final Subscription sub, final QueueEntry entry)
+ {
+
+ QueueEntryList.QueueEntryNode node = (QueueEntryList.QueueEntryNode) sub.getQueueContext();
+ while(node.isAcquired() || node.isDeleted() || !sub.hasInterest(node) )
+ {
+
+ QueueEntryList.QueueEntryNode newNode = node.getNext();
+ if(newNode != null)
+ {
+ sub.setQueueContext(node, newNode);
+ node = (QueueEntryList.QueueEntryNode) sub.getQueueContext();
+ }
+ else
+ {
+ break;
+ }
+
+ }
+
+ return node == entry;
+
+ }
+
+ public void requeue(StoreContext storeContext, QueueEntry entry) throws AMQException
+ {
+
+ SubscriptionList.SubscriptionNodeIterator subscriberIter = _subscriptionList.iterator();
+ // iterate over all the subscribers, and if they are in advance of this queue entry then move them backwards
+ while(subscriberIter.advance())
+ {
+ Subscription sub = subscriberIter.getNode().getSubscription();
+
+ // we don't make browsers send the same stuff twice
+ if(!sub.isBrowser())
+ {
+ QueueEntry subEntry = (QueueEntry) sub.getQueueContext();
+ while(entry.compareTo(subEntry)<0)
+ {
+ if(sub.setQueueContext(subEntry,entry))
+ {
+ break;
+ }
+ else
+ {
+ subEntry = (QueueEntry) sub.getQueueContext();
+ }
+ }
+ }
+ }
+
+
+ deliverAsync();
+
+
+ }
+
+ public void dequeue(StoreContext storeContext, QueueEntry entry) throws FailedDequeueException
+ {
+ _messageCount.decrementAndGet();
+ _depth.addAndGet(-entry.getMessage().getSize());
+ if(entry.acquiredBySubscription())
+ {
+ _deliveredMessages.decrementAndGet();
+ }
+
+ try
+ {
+ AMQMessage msg = entry.getMessage();
+ if(isDurable() && msg.isPersistent())
+ {
+ _virtualHost.getMessageStore().dequeueMessage(storeContext, getName(), msg.getMessageId());
+ }
+ ((QueueEntryList.QueueEntryNode)entry).delete();
+
+ }
+ 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 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
+ entry to resend and move back the subscription pointer. */
+
+ synchronized(subscription.getSendLock())
+ {
+ if(!subscription.isClosed())
+ {
+ deliverMessage(subscription, entry);
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ }
+
+
+
+
+
+ public int getConsumerCount()
+ {
+ return _subscriptionList.size();
+ }
+
+ public int getActiveConsumerCount()
+ {
+ return _activeSubscriberCount.get();
+ }
+
+ public boolean isUnused()
+ {
+ return getConsumerCount() == 0;
+ }
+
+ public boolean isEmpty()
+ {
+ return getMessageCount() == 0;
+ }
+
+ public int getMessageCount()
+ {
+ return _messageCount.get();
+ }
+
+ public long getQueueDepth()
+ {
+ return _depth.get();
+ }
+
+ public int getUndeliveredMessageCount()
+ {
+ int count = getMessageCount() - _deliveredMessages.get();
+ if(count < 0)
+ {
+ return 0;
+ }
+ else
+ {
+ return count;
+ }
+ }
+
+
+ public long getReceivedMessageCount()
+ {
+ return _totalMessagesReceived.get();
+ }
+
+ public long getOldestMessageArrivalTime()
+ {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean isDeleted()
+ {
+ return _deleted.get();
+ }
+
+
+
+ public List<QueueEntry> getMessagesOnTheQueue()
+ {
+ ArrayList<QueueEntry> entryList = new ArrayList<QueueEntry>();
+ QueueEntryList.QueueEntryIterator queueListIterator = _entries.iterator();
+ while(queueListIterator.advance())
+ {
+ QueueEntryList.QueueEntryNode node = queueListIterator.getNode();
+ if(node != null && !node.isDeleted())
+ {
+ entryList.add(node);
+ }
+ }
+ return entryList;
+
+ }
+
+ public void stateChange(Subscription sub, Subscription.State oldState, Subscription.State newState)
+ {
+ if(oldState == Subscription.State.ACTIVE && newState != Subscription.State.ACTIVE)
+ {
+ _activeSubscriberCount.decrementAndGet();
+
+ }
+ else if(newState == Subscription.State.ACTIVE && oldState != Subscription.State.ACTIVE)
+ {
+ _activeSubscriberCount.incrementAndGet();
+ deliverAsync();
+ }
+ }
+
+ public static interface QueueEntryFilter
+ {
+ public boolean accept(QueueEntry entry);
+
+ public boolean filterComplete();
+ }
+
+
+
+ public List<QueueEntry> getMessagesOnTheQueue(final long fromMessageId, final long toMessageId)
+ {
+ return getMessagesOnTheQueue(new QueueEntryFilter()
+ {
+
+ public boolean accept(QueueEntry entry)
+ {
+ final long messageId = entry.getMessage().getMessageId();
+ return messageId >= fromMessageId && messageId <= toMessageId;
+ }
+
+ public boolean filterComplete()
+ {
+ return false;
+ }
+ });
+ }
+
+ public QueueEntry getMessageOnTheQueue(final long messageId)
+ {
+ List<QueueEntry> entries = getMessagesOnTheQueue(new QueueEntryFilter()
+ {
+ private boolean _complete;
+
+ public boolean accept(QueueEntry entry)
+ {
+ _complete = entry.getMessage().getMessageId() == messageId;
+ return _complete;
+ }
+
+ public boolean filterComplete()
+ {
+ return _complete;
+ }
+ });
+ return entries.isEmpty() ? null : entries.get(0);
+ }
+
+
+ public List<QueueEntry> getMessagesOnTheQueue(QueueEntryFilter filter)
+ {
+ ArrayList<QueueEntry> entryList = new ArrayList<QueueEntry>();
+ QueueEntryList.QueueEntryIterator queueListIterator = _entries.iterator();
+ while(queueListIterator.advance() && !filter.filterComplete())
+ {
+ QueueEntryList.QueueEntryNode node = queueListIterator.getNode();
+ if(!node.isDeleted() && filter.accept(node))
+ {
+ entryList.add(node);
+ }
+ }
+ return entryList;
+
+ }
+
+
+ public void moveMessagesToAnotherQueue(long fromMessageId,
+ long toMessageId,
+ String queueName,
+ StoreContext storeContext)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void copyMessagesToAnotherQueue(long fromMessageId,
+ long toMessageId,
+ String queueName,
+ StoreContext storeContext)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void removeMessagesFromQueue(long fromMessageId, long toMessageId, StoreContext storeContext)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+
+ public void enqueueMovedMessages(final StoreContext storeContext, final List<QueueEntry> foundMessagesList)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+
+
+
+ public void quiesce()
+ {
+ _quiesced.set(true);
+ }
+
+ public void start()
+ {
+ if(_quiesced.compareAndSet(true,false))
+ {
+ deliverAsync();
+ }
+ }
+
+
+
+
+ // ------ Management functions
+
+
+ public void deleteMessageFromTop(StoreContext storeContext) throws AMQException
+ {
+ QueueEntryList.QueueEntryIterator queueListIterator = _entries.iterator();
+ boolean noDeletes = true;
+
+ while(noDeletes && queueListIterator.advance() )
+ {
+ QueueEntryList.QueueEntryNode node = queueListIterator.getNode();
+ if(!node.isDeleted() && !node.acquire(null))
+ {
+ node.dequeue(storeContext);
+
+ node.dispose(storeContext);
+
+ noDeletes = false;
+ }
+
+ }
+ }
+
+ public long clearQueue(StoreContext storeContext) throws AMQException
+ {
+
+ QueueEntryList.QueueEntryIterator queueListIterator = _entries.iterator();
+ long count = 0;
+
+ while(queueListIterator.advance())
+ {
+ QueueEntryList.QueueEntryNode node = queueListIterator.getNode();
+ if(!node.isDeleted() && !node.acquire(null))
+ {
+ node.dequeue(storeContext);
+
+ node.dispose(storeContext);
+
+ count++;
+ }
+
+ }
+ return count;
+
+ }
+
+
+ public void addQueueDeleteTask(final Task task)
+ {
+ _deleteTaskList.add(task);
+ }
+
+ public int delete() throws AMQException
+ {
+ if (!_deleted.getAndSet(true))
+ {
+
+ SubscriptionList.SubscriptionNodeIterator subscriptionIter = _subscriptionList.iterator();
+
+ while (subscriptionIter.advance())
+ {
+ Subscription s = subscriptionIter.getNode().getSubscription();
+ if(s != null)
+ {
+ s.queueDeleted(this);
+ }
+ }
+
+ _bindings.deregister();
+ _virtualHost.getQueueRegistry().unregisterQueue(_name);
+
+
+ _managedObject.unregister();
+ for (Task task : _deleteTaskList)
+ {
+ task.doTask(this);
+ }
+
+ _deleteTaskList.clear();
+ }
+ return getMessageCount();
+
+ }
+
+
+ public void deliverAsync()
+ {
+ _stateChangeCount.incrementAndGet();
+
+ if(_asynchronousRunner.get() == null)
+ {
+ _asyncDelivery.execute(new Runner());
+ }
+ }
+
+ private class Runner implements Runnable
+ {
+ public void run()
+ {
+ String startName = Thread.currentThread().getName();
+ Thread.currentThread().setName("async delivery (Q: " + getName() + " VH: " + _virtualHost.getName() +")");
+ try
+ {
+ processQueue(this);
+ }
+ catch (AMQException e)
+ {
+ _logger.error(e);
+ }
+ Thread.currentThread().setName(startName);
+ }
+ }
+
+
+ private void processQueue(Runnable runner) throws AMQException
+ {
+ long stateChangeCount;
+ long previousStateChangeCount = Long.MIN_VALUE;
+ boolean deliveryIncomplete = true;
+
+ int extraLoops = 1;
+
+ while(((previousStateChangeCount != (stateChangeCount = _stateChangeCount.get())) || deliveryIncomplete ) && _asynchronousRunner.compareAndSet(null,runner))
+ {
+ // we want to have one extra loop after the every subscription has reached the point where it cannot move
+ // further, just in case the advance of one subscription in the last loop allows a different subscription to
+ // move forward in the next iteration
+
+ if(previousStateChangeCount != stateChangeCount)
+ {
+ extraLoops = 1;
+ }
+
+ previousStateChangeCount = stateChangeCount;
+ deliveryIncomplete = _subscriptionList.size() != 0;
+ boolean done = true;
+
+
+ SubscriptionList.SubscriptionNodeIterator subscriptionIter = _subscriptionList.iterator();
+ //iterate over the subscribers and try to advance their pointer
+ while(subscriptionIter.advance())
+ {
+ Subscription sub = subscriptionIter.getNode().getSubscription();
+ if(sub != null)
+ {
+ synchronized(sub.getSendLock())
+ {
+ if(sub.isActive())
+ {
+ 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;
+ }
+
+ }
+ 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())
+ {
+ QueueEntryList.QueueEntryNode newNode = node.getNext();
+
+ if(newNode != null)
+ {
+ sub.setQueueContext(node, newNode);
+ node = (QueueEntryList.QueueEntryNode) sub.getQueueContext();
+ advanced = true;
+ }
+
+
+ }
+ }
+ done = false;
+ }
+ }
+ else
+ {
+ // this subscription is not interested in this node so we can skip over it
+ QueueEntryList.QueueEntryNode newNode = node.getNext();
+ if(newNode != null)
+ {
+ sub.setQueueContext(node, newNode);
+ }
+ }
+ }
+ }
+ final boolean atTail = (node.getNext() == null);
+
+ done = done && atTail;
+
+ if(atTail && !advanced && sub.isAutoClose())
+ {
+ unregisterSubscription(sub);
+
+ ProtocolOutputConverter converter = sub.getChannel().getProtocolSession().getProtocolOutputConverter();
+ converter.confirmConsumerAutoClose(sub.getChannel().getChannelId(), sub.getConumerTag());
+
+ }
+
+ }
+ }
+ }
+ if(done)
+ {
+ if(extraLoops == 0)
+ {
+ deliveryIncomplete = false;
+ }
+ else
+ {
+ extraLoops--;
+ }
+ }
+ else
+ {
+ extraLoops = 1;
+ }
+ }
+
+
+
+ _asynchronousRunner.set(null);
+ }
+
+
+ }
+
+ 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();
+
+ while(queueListIterator.advance())
+ {
+ QueueEntryList.QueueEntryNode node = queueListIterator.getNode();
+ if(!node.isDeleted() && node.expired() && node.acquire(null))
+ {
+
+ node.dequeue(storeContext);
+
+ node.dispose(storeContext);
+
+ }
+
+ }
+
+ }
+
+
+ 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 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 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 Set<NotificationCheck> getNotificationChecks()
+ {
+ return _notificationChecks;
+ }
+
+
+
+
+
+ public ManagedObject getManagedObject()
+ {
+ return _managedObject;
+ }
+
+ public int compareTo(final Object o)
+ {
+ return _name.compareTo(((AMQQueue) o).getName());
+ }
+} \ No newline at end of file
diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/Subscription.java b/java/broker/src/main/java/org/apache/qpid/server/queue/Subscription.java
deleted file mode 100644
index 96ce6743ec..0000000000
--- a/java/broker/src/main/java/org/apache/qpid/server/queue/Subscription.java
+++ /dev/null
@@ -1,63 +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.Queue;
-
-import org.apache.qpid.AMQException;
-import org.apache.qpid.server.AMQChannel;
-
-public interface Subscription
-{
- void send(QueueEntry msg, AMQQueue queue) throws AMQException;
-
- boolean isSuspended();
-
- void queueDeleted(AMQQueue queue) throws AMQException;
-
- boolean filtersMessages();
-
- boolean hasInterest(QueueEntry msg);
-
- Queue<QueueEntry> getPreDeliveryQueue();
-
- Queue<QueueEntry> getResendQueue();
-
- Queue<QueueEntry> getNextQueue(Queue<QueueEntry> messages);
-
- void enqueueForPreDelivery(QueueEntry msg, boolean deliverFirst);
-
- void close();
-
- boolean isClosed();
-
- boolean isBrowser();
-
- boolean wouldSuspend(QueueEntry msg);
-
- void addToResendQueue(QueueEntry msg);
-
- Object getSendLock();
-
- AMQChannel getChannel();
-
- void start();
-}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionImpl.java b/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionImpl.java
deleted file mode 100644
index bde3ad8ec9..0000000000
--- a/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionImpl.java
+++ /dev/null
@@ -1,689 +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.Queue;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-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;
-import org.apache.qpid.framing.FieldTable;
-import org.apache.qpid.server.AMQChannel;
-import org.apache.qpid.server.output.ProtocolOutputConverter;
-import org.apache.qpid.server.filter.FilterManager;
-import org.apache.qpid.server.filter.FilterManagerFactory;
-import org.apache.qpid.server.protocol.AMQProtocolSession;
-import org.apache.qpid.server.store.StoreContext;
-import org.apache.qpid.util.ConcurrentLinkedQueueAtomicSize;
-import org.apache.qpid.util.MessageQueue;
-import org.apache.qpid.util.ConcurrentLinkedMessageQueueAtomicSize;
-
-/**
- * Encapsulation of a supscription to a queue. <p/> Ties together the protocol session of a subscriber, the consumer tag
- * that was given out by the broker and the channel id. <p/>
- */
-public class SubscriptionImpl implements Subscription
-{
-
- private static final Logger _suspensionlogger = Logger.getLogger("Suspension");
- private static final Logger _logger = Logger.getLogger(SubscriptionImpl.class);
-
- public final AMQChannel channel;
-
- public final AMQProtocolSession protocolSession;
-
- public final AMQShortString consumerTag;
-
- private final Object _sessionKey;
-
- private MessageQueue<QueueEntry> _messages;
-
- private Queue<QueueEntry> _resendQueue;
-
- private final boolean _noLocal;
-
- /** True if messages need to be acknowledged */
- private final boolean _acks;
- private FilterManager _filters;
- private final boolean _isBrowser;
- private final Boolean _autoClose;
- private boolean _sentClose = false;
-
- private static final String CLIENT_PROPERTIES_INSTANCE = ClientProperties.instance.toString();
-
- private AMQQueue _queue;
- private final AtomicBoolean _sendLock = new AtomicBoolean(false);
-
-
- public static class Factory implements SubscriptionFactory
- {
- public Subscription createSubscription(int channel, AMQProtocolSession protocolSession,
- AMQShortString consumerTag, boolean acks, FieldTable filters,
- boolean noLocal, AMQQueue queue) throws AMQException
- {
- return new SubscriptionImpl(channel, protocolSession, consumerTag, acks, filters, noLocal, queue);
- }
-
- public SubscriptionImpl createSubscription(int channel, AMQProtocolSession protocolSession, AMQShortString consumerTag)
- throws AMQException
- {
- return new SubscriptionImpl(channel, protocolSession, consumerTag, false, null, false, null);
- }
- }
-
- public SubscriptionImpl(int channelId, AMQProtocolSession protocolSession,
- AMQShortString consumerTag, boolean acks)
- throws AMQException
- {
- this(channelId, protocolSession, consumerTag, acks, null, false, null);
- }
-
- public SubscriptionImpl(int channelId, AMQProtocolSession protocolSession,
- AMQShortString consumerTag, boolean acks, FieldTable filters,
- boolean noLocal, AMQQueue queue)
- throws AMQException
- {
- AMQChannel channel = protocolSession.getChannel(channelId);
- if (channel == null)
- {
- throw new AMQException(AMQConstant.NOT_FOUND, "channel :" + channelId + " not found in protocol session");
- }
-
- this.channel = channel;
- this.protocolSession = protocolSession;
- this.consumerTag = consumerTag;
- _sessionKey = protocolSession.getKey();
- _acks = acks;
- _noLocal = noLocal;
- _queue = queue;
-
- _filters = FilterManagerFactory.createManager(filters);
-
-
- if (_filters != null)
- {
- Object isBrowser = filters.get(AMQPFilterTypes.NO_CONSUME.getValue());
- if (isBrowser != null)
- {
- _isBrowser = (Boolean) isBrowser;
- }
- else
- {
- _isBrowser = false;
- }
- }
- else
- {
- _isBrowser = false;
- }
-
-
- if (_filters != null)
- {
- Object autoClose = filters.get(AMQPFilterTypes.AUTO_CLOSE.getValue());
- if (autoClose != null)
- {
- _autoClose = (Boolean) autoClose;
- }
- else
- {
- _autoClose = false;
- }
- }
- else
- {
- _autoClose = false;
- }
-
-
- if (filtersMessages())
- {
- _messages = new ConcurrentLinkedMessageQueueAtomicSize<QueueEntry>();
- }
- else
- {
- // Reference the DeliveryManager
- _messages = null;
- }
- }
-
-
- public SubscriptionImpl(int channel, AMQProtocolSession protocolSession,
- AMQShortString consumerTag)
- throws AMQException
- {
- this(channel, protocolSession, consumerTag, false);
- }
-
- public boolean equals(Object o)
- {
- return (o instanceof SubscriptionImpl) && equals((SubscriptionImpl) o);
- }
-
- /**
- * Equality holds if the session matches and the channel and consumer tag are the same.
- *
- * @param psc The subscriptionImpl to compare
- *
- * @return equality
- */
- private boolean equals(SubscriptionImpl psc)
- {
- return _sessionKey.equals(psc._sessionKey)
- && psc.channel == channel
- && psc.consumerTag.equals(consumerTag);
- }
-
- public int hashCode()
- {
- return _sessionKey.hashCode();
- }
-
- public String toString()
- {
- String subscriber = "[channel=" + channel +
- ", consumerTag=" + consumerTag +
- ", session=" + protocolSession.getKey() +
- ", resendQueue=" + (_resendQueue != null);
-
- if (_resendQueue != null)
- {
- subscriber += ", resendSize=" + _resendQueue.size();
- }
-
-
- return subscriber + "]";
- }
-
- /**
- * This method can be called by each of the publisher threads. As a result all changes to the channel object must be
- * thread safe.
- *
- * @param msg The message to send
- * @param queue the Queue it has been sent from
- *
- * @throws AMQException
- */
- public void send(QueueEntry msg, AMQQueue queue) throws AMQException
- {
- if (msg != null)
- {
- if (_isBrowser)
- {
- sendToBrowser(msg, queue);
- }
- else
- {
- sendToConsumer(channel.getStoreContext(), msg, queue);
- }
- }
- else
- {
- _logger.error("Attempt to send Null message", new NullPointerException());
- }
- }
-
- private void sendToBrowser(QueueEntry msg, AMQQueue queue) throws AMQException
- {
- // We don't decrement the reference here as we don't want to consume the message
- // but we do want to send it to the client.
-
- synchronized (channel)
- {
- long deliveryTag = channel.getNextDeliveryTag();
-
- // We don't need to add the message to the unacknowledgedMap as we don't need to know if the client
- // received the message. If it is lost in transit that is not important.
-// if (_acks)
-// {
-// channel.addUnacknowledgedBrowsedMessage(msg, deliveryTag, consumerTag, queue);
-// }
-
- if (_sendLock.get())
- {
- _logger.error("Sending " + msg + " when subscriber(" + this + ") is closed!");
- }
-
- protocolSession.getProtocolOutputConverter().writeDeliver(msg.getMessage(), channel.getChannelId(), deliveryTag, consumerTag);
- }
- }
-
- private void sendToConsumer(StoreContext storeContext, QueueEntry entry, AMQQueue queue)
- throws AMQException
- {
- 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 (_logger.isDebugEnabled())
- {
- _logger.debug("No ack mode so dequeuing message immediately: " + entry.getMessage().getMessageId());
- }
- queue.dequeue(storeContext, entry);
- }
-
-/*
- if (_sendLock.get())
- {
- _logger.error("Sending " + entry + " when subscriber(" + this + ") is closed!");
- }
-*/
-
- synchronized (channel)
- {
- long deliveryTag = channel.getNextDeliveryTag();
-
-
- if (_acks)
- {
- channel.addUnacknowledgedMessage(entry, deliveryTag, consumerTag);
- }
-
- protocolSession.getProtocolOutputConverter().writeDeliver(entry.getMessage(), channel.getChannelId(), deliveryTag, consumerTag);
-
-
- }
- if (!_acks)
- {
- entry.getMessage().decrementReference(storeContext);
- }
- }
- finally
- {
- //Only set delivered if it actually was writen successfully..
- // using a try->finally would set it even if an error occured.
- // Is this what we want?
-
- entry.setDeliveredToConsumer();
- }
- }
-
- public boolean isSuspended()
- {
-// if (_suspensionlogger.isInfoEnabled())
-// {
-// if (channel.isSuspended())
-// {
-// _suspensionlogger.debug("Subscription(" + debugIdentity() + ") channel's is susupended");
-// }
-// if (_sendLock.get())
-// {
-// _suspensionlogger.debug("Subscription(" + debugIdentity() + ") has sendLock set so closing.");
-// }
-// }
- return channel.isSuspended() || _sendLock.get();
- }
-
- /**
- * Callback indicating that a queue has been deleted.
- *
- * @param queue The queue to delete
- */
- public void queueDeleted(AMQQueue queue) throws AMQException
- {
- channel.queueDeleted(queue);
- }
-
- public boolean filtersMessages()
- {
- return _filters != null || _noLocal;
- }
-
- public boolean hasInterest(QueueEntry entry)
- {
- //check that the message hasn't been rejected
- if (entry.isRejectedBy(this))
- {
- if (_logger.isDebugEnabled())
- {
- _logger.debug("Subscription:" + debugIdentity() + " rejected message:" + entry.debugIdentity());
- }
-// return false;
- }
-
-
-
- //todo - client id should be recoreded and this test removed but handled below
- if (_noLocal)
- {
-
- final AMQProtocolSession publisher = entry.getMessage().getPublisher();
- if(publisher != null)
-
- {
- // We don't want local messages so check to see if message is one we sent
- Object localInstance;
- Object msgInstance;
-
- if ((protocolSession.getClientProperties() != null) &&
- (localInstance = protocolSession.getClientProperties().getObject(CLIENT_PROPERTIES_INSTANCE)) != null)
- {
-
- if ((publisher.getClientProperties() != null) &&
- (msgInstance = publisher.getClientProperties().getObject(CLIENT_PROPERTIES_INSTANCE)) != null)
- {
- if (localInstance == msgInstance || localInstance.equals(msgInstance))
- {
- // if (_logger.isTraceEnabled())
- // {
- // _logger.trace("(" + debugIdentity() + ") has no interest as it is a local message(" +
- // msg.debugIdentity() + ")");
- // }
- return false;
- }
- }
- }
- else
- {
-
- localInstance = protocolSession.getClientIdentifier();
- //todo - client id should be recoreded and this test removed but handled here
-
- msgInstance = publisher.getClientIdentifier();
- if (localInstance == msgInstance || ((localInstance != null) && localInstance.equals(msgInstance)))
- {
- // if (_logger.isTraceEnabled())
- // {
- // _logger.trace("(" + debugIdentity() + ") has no interest as it is a local message(" +
- // msg.debugIdentity() + ")");
- // }
- return false;
- }
- }
-
- }
- }
-
-
- return checkFilters(entry);
-
- }
-
- private String id = String.valueOf(System.identityHashCode(this));
-
- private String debugIdentity()
- {
- return id;
- }
-
- private boolean checkFilters(QueueEntry msg)
- {
- return (_filters == null) || _filters.allAllow(msg.getMessage());
- }
-
- public Queue<QueueEntry> getPreDeliveryQueue()
- {
- return _messages;
- }
-
- public void enqueueForPreDelivery(QueueEntry msg, boolean deliverFirst)
- {
- if (_messages != null)
- {
- if (deliverFirst)
- {
- _messages.pushHead(msg);
- }
- else
- {
- _messages.offer(msg);
- }
- }
- }
-
- private boolean isAutoClose()
- {
- return _autoClose;
- }
-
- public void close()
- {
- boolean closed = false;
- synchronized (_sendLock)
- {
- if (_logger.isDebugEnabled())
- {
- _logger.debug("Setting SendLock true:" + debugIdentity());
- }
-
- closed = _sendLock.getAndSet(true);
- }
-
- if (closed)
- {
- if (_logger.isDebugEnabled())
- {
- _logger.debug("Called close() on a closed subscription");
- }
-
- return;
- }
-
- if (_logger.isInfoEnabled())
- {
- _logger.info("Closing subscription (" + debugIdentity() + "):" + this);
- }
-
- if (_resendQueue != null && !_resendQueue.isEmpty())
- {
- if (_logger.isInfoEnabled())
- {
- _logger.info("Requeuing closing subscription (" + debugIdentity() + "):" + this);
- }
- requeue();
- }
-
- //remove references in PDQ
- if (_messages != null)
- {
- if (_logger.isInfoEnabled())
- {
- _logger.info("Clearing PDQ (" + debugIdentity() + "):" + this);
- }
-
- _messages.clear();
- }
- }
-
- private void autoclose()
- {
- close();
-
- if (_autoClose && !_sentClose)
- {
- _logger.info("Closing autoclose subscription (" + debugIdentity() + "):" + this);
-
- boolean unregisteredOK = false;
- try
- {
- unregisteredOK = channel.unsubscribeConsumer(protocolSession, consumerTag);
- }
- catch (AMQException e)
- {
- // Occurs if we cannot find the subscriber in the channel with protocolSession and consumerTag.
- _logger.info("Unable to UnsubscribeConsumer :" + consumerTag +" so not going to send CancelOK.");
- }
-
- if (unregisteredOK)
- {
- ProtocolOutputConverter converter = protocolSession.getProtocolOutputConverter();
- converter.confirmConsumerAutoClose(channel.getChannelId(), consumerTag);
- _sentClose = true;
- }
-
- }
- }
-
- private void requeue()
- {
- if (_queue != null)
- {
- if (_logger.isDebugEnabled())
- {
- _logger.debug("Requeuing :" + _resendQueue.size() + " messages");
- }
-
- while (!_resendQueue.isEmpty())
- {
- QueueEntry resent = _resendQueue.poll();
-
- if (_logger.isDebugEnabled())
- {
- _logger.debug("Removed for resending:" + resent.debugIdentity());
- }
-
- resent.release();
- _queue.subscriberHasPendingResend(false, this, resent);
-
- try
- {
- channel.getTransactionalContext().deliver(resent, true);
- }
- 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.");
- }
-
- _queue.subscriberHasPendingResend(false, this, null);
- }
- else
- {
- if (!_resendQueue.isEmpty())
- {
- _logger.error("Unable to re-deliver messages as queue is null.");
- }
- }
-
- // Clear the messages
- _resendQueue = null;
- }
-
-
- public boolean isClosed()
- {
- return _sendLock.get(); // This rather than _close is used to signify the subscriber is now closed.
- }
-
- public boolean isBrowser()
- {
- return _isBrowser;
- }
-
- public boolean wouldSuspend(QueueEntry msg)
- {
- return _acks && channel.wouldSuspend(msg.getMessage());
- }
-
- public Queue<QueueEntry> getResendQueue()
- {
- if (_resendQueue == null)
- {
- _resendQueue = new ConcurrentLinkedQueueAtomicSize<QueueEntry>();
- }
- return _resendQueue;
- }
-
-
- public Queue<QueueEntry> getNextQueue(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;
- }
- }
-
- public void addToResendQueue(QueueEntry msg)
- {
- // add to our resend queue
- getResendQueue().add(msg);
-
- // Mark Queue has having content.
- if (_queue == null)
- {
- _logger.error("Queue is null won't be able to resend messages");
- }
- else
- {
- _queue.subscriberHasPendingResend(true, this, msg);
- }
- }
-
- public Object getSendLock()
- {
- return _sendLock;
- }
-
- public AMQChannel getChannel()
- {
- return channel;
- }
-
- 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/SubscriptionManager.java b/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionManager.java
index bc17bcca9c..4c39eb3397 100644
--- 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
@@ -20,6 +20,8 @@
*/
package org.apache.qpid.server.queue;
+import org.apache.qpid.server.subscription.Subscription;
+
import java.util.List;
/**
@@ -30,5 +32,16 @@ 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
index 882efd380d..117799ad87 100644
--- 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
@@ -22,22 +22,27 @@ 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.AMQException;
+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 WeightedSubscriptionManager
+class SubscriptionSet implements SubscriptionManager
{
private static final Logger _log = Logger.getLogger(SubscriptionSet.class);
/** List of registered subscribers */
- private List<Subscription> _subscriptions = new CopyOnWriteArrayList<Subscription>();
+ 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;
@@ -53,6 +58,7 @@ class SubscriptionSet implements WeightedSubscriptionManager
synchronized (_changeLock)
{
_subscriptions.add(subscription);
+ _deliveryAgents.put(subscription, new DeliveryAgent(subscription));
}
}
@@ -66,33 +72,22 @@ class SubscriptionSet implements WeightedSubscriptionManager
public Subscription removeSubscriber(Subscription subscription)
{
// TODO: possibly need O(1) operation here.
-
- Subscription sub = null;
+
synchronized (_changeLock)
{
- int subIndex = _subscriptions.indexOf(subscription);
- if (subIndex != -1)
+ if (_subscriptions.remove(subscription))
{
- //we can't just return the passed in subscription as it is a new object
- // and doesn't contain the stored state we need.
- //NOTE while this may be removed now anyone with an iterator will still have it in the list!!
- sub = _subscriptions.remove(subIndex);
+ _deliveryAgents.remove(subscription);
+ return subscription;
}
else
{
- _log.error("Unable to remove from index(" + subIndex + ")subscription:" + subscription);
+ _log.error("Unable to remove subscription:" + subscription);
+ debugDumpSubscription(subscription);
+ return null;
}
}
- if (sub != null)
- {
- return sub;
- }
- else
- {
- debugDumpSubscription(subscription);
- return null;
- }
}
private void debugDumpSubscription(Subscription subscription)
@@ -148,17 +143,18 @@ class SubscriptionSet implements WeightedSubscriptionManager
Subscription subscription = _subscriptions.get(0);
subscriberScanned();
- if (!(subscription.isSuspended() || subscription.wouldSuspend(msg)))
+ if (!subscription.isSuspended() )
{
if (subscription.hasInterest(msg))
{
- // 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.
- if (!subscription.filtersMessages() || subscription.getPreDeliveryQueue().isEmpty())
+ DeliveryAgent deliverAgent = _deliveryAgents.get(subscription);
+
+ if (deliverAgent.ableToDeliver())
{
- return subscription;
+ if(!subscription.wouldSuspend(msg))
+ {
+ return subscription;
+ }
}
}
}
@@ -177,19 +173,17 @@ class SubscriptionSet implements WeightedSubscriptionManager
final ListIterator<Subscription> iterator = _subscriptions.listIterator(_currentSubscriber);
while (iterator.hasNext())
{
+
Subscription subscription = iterator.next();
+ DeliveryAgent deliverAgent = _deliveryAgents.get(subscription);
++_currentSubscriber;
subscriberScanned();
- if (!(subscription.isSuspended() || subscription.wouldSuspend(msg)))
+ if (!(deliverAgent == null || subscription.isSuspended()))
{
- if (subscription.hasInterest(msg))
+ if (subscription.hasInterest(msg) && deliverAgent.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.
- if (!subscription.filtersMessages() || subscription.getPreDeliveryQueue().isEmpty())
+ if (!subscription.wouldSuspend(msg))
{
return subscription;
}
@@ -228,12 +222,12 @@ class SubscriptionSet implements WeightedSubscriptionManager
return false;
}
- public int getWeight()
+ public int getActiveConsumerCount()
{
int count = 0;
for (Subscription s : _subscriptions)
{
- if (!s.isSuspended())
+ if (s.isActive())
{
count++;
}
@@ -241,13 +235,18 @@ class SubscriptionSet implements WeightedSubscriptionManager
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) throws AMQException
+ public void queueDeleted(AMQQueue queue)
{
for (Subscription s : _subscriptions)
{
@@ -271,4 +270,9 @@ class SubscriptionSet implements WeightedSubscriptionManager
_exclusive = exclusive;
}
+
+ public DeliveryAgent getDeliveryAgent(final Subscription sub)
+ {
+ return _deliveryAgents.get(sub);
+ }
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/WeakReferenceMessageHandle.java b/java/broker/src/main/java/org/apache/qpid/server/queue/WeakReferenceMessageHandle.java
index 373a64e2eb..711497c799 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/queue/WeakReferenceMessageHandle.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/queue/WeakReferenceMessageHandle.java
@@ -48,29 +48,37 @@ public class WeakReferenceMessageHandle implements AMQMessageHandle
private final MessageStore _messageStore;
+ private final Long _messageId;
private long _arrivalTime;
+ private boolean _persistent;
- public WeakReferenceMessageHandle(MessageStore messageStore)
+ public WeakReferenceMessageHandle(final Long messageId, MessageStore messageStore)
{
+ _messageId = messageId;
_messageStore = messageStore;
}
- public ContentHeaderBody getContentHeaderBody(StoreContext context, Long messageId) throws AMQException
+ public ContentHeaderBody getContentHeaderBody(StoreContext context) throws AMQException
{
ContentHeaderBody chb = (_contentHeaderBody != null ? _contentHeaderBody.get() : null);
if (chb == null)
{
- MessageMetaData mmd = loadMessageMetaData(context, messageId);
+ MessageMetaData mmd = loadMessageMetaData(context);
chb = mmd.getContentHeaderBody();
}
return chb;
}
- private MessageMetaData loadMessageMetaData(StoreContext context, Long messageId)
+ public Long getMessageId()
+ {
+ return _messageId;
+ }
+
+ private MessageMetaData loadMessageMetaData(StoreContext context)
throws AMQException
{
- MessageMetaData mmd = _messageStore.getMessageMetaData(context, messageId);
+ MessageMetaData mmd = _messageStore.getMessageMetaData(context, _messageId);
populateFromMessageMetaData(mmd);
return mmd;
}
@@ -82,11 +90,11 @@ public class WeakReferenceMessageHandle implements AMQMessageHandle
_messagePublishInfo = new WeakReference<MessagePublishInfo>(mmd.getMessagePublishInfo());
}
- public int getBodyCount(StoreContext context, Long messageId) throws AMQException
+ public int getBodyCount(StoreContext context) throws AMQException
{
if (_contentBodies == null)
{
- MessageMetaData mmd = _messageStore.getMessageMetaData(context, messageId);
+ MessageMetaData mmd = _messageStore.getMessageMetaData(context, _messageId);
int chunkCount = mmd.getContentChunkCount();
_contentBodies = new ArrayList<WeakReference<ContentChunk>>(chunkCount);
for (int i = 0; i < chunkCount; i++)
@@ -97,12 +105,12 @@ public class WeakReferenceMessageHandle implements AMQMessageHandle
return _contentBodies.size();
}
- public long getBodySize(StoreContext context, Long messageId) throws AMQException
+ public long getBodySize(StoreContext context) throws AMQException
{
- return getContentHeaderBody(context, messageId).bodySize;
+ return getContentHeaderBody(context).bodySize;
}
- public ContentChunk getContentChunk(StoreContext context, Long messageId, int index) throws AMQException, IllegalArgumentException
+ public ContentChunk getContentChunk(StoreContext context, int index) throws AMQException, IllegalArgumentException
{
if (index > _contentBodies.size() - 1)
{
@@ -113,7 +121,7 @@ public class WeakReferenceMessageHandle implements AMQMessageHandle
ContentChunk cb = wr.get();
if (cb == null)
{
- cb = _messageStore.getContentBodyChunk(context, messageId, index);
+ cb = _messageStore.getContentBodyChunk(context, _messageId, index);
_contentBodies.set(index, new WeakReference<ContentChunk>(cb));
}
return cb;
@@ -123,12 +131,11 @@ public class WeakReferenceMessageHandle implements AMQMessageHandle
* Content bodies are set <i>before</i> the publish and header frames
*
* @param storeContext
- * @param messageId
* @param contentChunk
* @param isLastContentBody
* @throws AMQException
*/
- public void addContentBodyFrame(StoreContext storeContext, Long messageId, ContentChunk contentChunk, boolean isLastContentBody) throws AMQException
+ public void addContentBodyFrame(StoreContext storeContext, ContentChunk contentChunk, boolean isLastContentBody) throws AMQException
{
if (_contentBodies == null && isLastContentBody)
{
@@ -142,16 +149,16 @@ public class WeakReferenceMessageHandle implements AMQMessageHandle
}
}
_contentBodies.add(new WeakReference<ContentChunk>(contentChunk));
- _messageStore.storeContentBodyChunk(storeContext, messageId, _contentBodies.size() - 1,
+ _messageStore.storeContentBodyChunk(storeContext, _messageId, _contentBodies.size() - 1,
contentChunk, isLastContentBody);
}
- public MessagePublishInfo getMessagePublishInfo(StoreContext context, Long messageId) throws AMQException
+ public MessagePublishInfo getMessagePublishInfo(StoreContext context) throws AMQException
{
MessagePublishInfo bpb = (_messagePublishInfo != null ? _messagePublishInfo.get() : null);
if (bpb == null)
{
- MessageMetaData mmd = loadMessageMetaData(context, messageId);
+ MessageMetaData mmd = loadMessageMetaData(context);
bpb = mmd.getMessagePublishInfo();
}
@@ -168,12 +175,9 @@ public class WeakReferenceMessageHandle implements AMQMessageHandle
_redelivered = redelivered;
}
- public boolean isPersistent(StoreContext context, Long messageId) throws AMQException
+ public boolean isPersistent(StoreContext context) throws AMQException
{
- //todo remove literal values to a constant file such as AMQConstants in common
- ContentHeaderBody chb = getContentHeaderBody(context, messageId);
- return chb.properties instanceof BasicContentHeaderProperties &&
- ((BasicContentHeaderProperties) chb.properties).getDeliveryMode() == 2;
+ return _persistent;
}
/**
@@ -183,7 +187,7 @@ public class WeakReferenceMessageHandle implements AMQMessageHandle
* @param contentHeaderBody
* @throws AMQException
*/
- public void setPublishAndContentHeaderBody(StoreContext storeContext, Long messageId, MessagePublishInfo publishBody,
+ public void setPublishAndContentHeaderBody(StoreContext storeContext, MessagePublishInfo publishBody,
ContentHeaderBody contentHeaderBody)
throws AMQException
{
@@ -199,24 +203,18 @@ public class WeakReferenceMessageHandle implements AMQMessageHandle
MessageMetaData mmd = new MessageMetaData(publishBody, contentHeaderBody, _contentBodies.size(), arrivalTime);
- _messageStore.storeMessageMetaData(storeContext, messageId, mmd);
+ _messageStore.storeMessageMetaData(storeContext, _messageId, mmd);
- populateFromMessageMetaData(mmd);
- }
- public void removeMessage(StoreContext storeContext, Long messageId) throws AMQException
- {
- _messageStore.removeMessage(storeContext, messageId);
- }
+ _persistent = contentHeaderBody.properties instanceof BasicContentHeaderProperties &&
+ ((BasicContentHeaderProperties) contentHeaderBody.properties).getDeliveryMode() == 2;
- public void enqueue(StoreContext storeContext, Long messageId, AMQQueue queue) throws AMQException
- {
- _messageStore.enqueueMessage(storeContext, queue.getName(), messageId);
+ populateFromMessageMetaData(mmd);
}
- public void dequeue(StoreContext storeContext, Long messageId, AMQQueue queue) throws AMQException
+ public void removeMessage(StoreContext storeContext) throws AMQException
{
- _messageStore.dequeueMessage(storeContext, queue.getName(), messageId);
+ _messageStore.removeMessage(storeContext, _messageId);
}
public long getArrivalTime()
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
new file mode 100644
index 0000000000..180d47001b
--- /dev/null
+++ b/java/broker/src/main/java/org/apache/qpid/server/store/DerbyMessageStore.java
@@ -0,0 +1,1414 @@
+package org.apache.qpid.server.store;
+
+import org.apache.qpid.server.virtualhost.VirtualHost;
+import org.apache.qpid.server.exchange.Exchange;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.queue.MessageMetaData;
+import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.queue.AMQQueueFactory;
+import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.queue.MessageHandleFactory;
+import org.apache.qpid.server.txn.TransactionalContext;
+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;
+import org.apache.commons.configuration.Configuration;
+import org.apache.log4j.Logger;
+import org.apache.mina.common.ByteBuffer;
+
+import java.io.File;
+import java.sql.DriverManager;
+import java.sql.Driver;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.Statement;
+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;
+import java.util.TreeMap;
+
+
+/*
+*
+* 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 DerbyMessageStore implements MessageStore
+{
+
+ private static final Logger _logger = Logger.getLogger(DerbyMessageStore.class);
+
+ private static final String ENVIRONMENT_PATH_PROPERTY = "environment-path";
+
+
+ private static final String DERBY_DRIVER_NAME = "org.apache.derby.jdbc.EmbeddedDriver";
+
+ private static final String DB_VERSION_TABLE_NAME = "QPID_DB_VERSION";
+
+ private static final String EXCHANGE_TABLE_NAME = "QPID_EXCHANGE";
+ private static final String QUEUE_TABLE_NAME = "QPID_QUEUE";
+ private static final String BINDINGS_TABLE_NAME = "QPID_BINDINGS";
+ private static final String QUEUE_ENTRY_TABLE_NAME = "QPID_QUEUE_ENTRY";
+ private static final String MESSAGE_META_DATA_TABLE_NAME = "QPID_MESSAGE_META_DATA";
+ private static final String MESSAGE_CONTENT_TABLE_NAME = "QPID_MESSAGE_CONTENT";
+
+ private static final int DB_VERSION = 1;
+
+
+
+ private VirtualHost _virtualHost;
+ private static Class<Driver> DRIVER_CLASS;
+
+ private final AtomicLong _messageId = new AtomicLong(1);
+ private AtomicBoolean _closed = new AtomicBoolean(false);
+
+ private String _connectionURL;
+
+
+ private enum State
+ {
+ INITIAL,
+ CONFIGURING,
+ RECOVERING,
+ STARTED,
+ CLOSING,
+ CLOSED
+ }
+
+ private State _state = State.INITIAL;
+
+
+ public void configure(VirtualHost virtualHost, String base, Configuration config) throws Exception
+ {
+ stateTransition(State.INITIAL, State.CONFIGURING);
+
+ initialiseDriver();
+
+ _virtualHost = virtualHost;
+
+ _logger.info("Configuring Derby message store for virtual host " + virtualHost.getName());
+ QueueRegistry queueRegistry = virtualHost.getQueueRegistry();
+
+ final String databasePath = config.getString(base + "." + ENVIRONMENT_PATH_PROPERTY, "derbyDB");
+
+ File environmentPath = new File(databasePath);
+ if (!environmentPath.exists())
+ {
+ if (!environmentPath.mkdirs())
+ {
+ throw new IllegalArgumentException("Environment path " + environmentPath + " could not be read or created. "
+ + "Ensure the path is correct and that the permissions are correct.");
+ }
+ }
+
+ createOrOpenDatabase(databasePath);
+
+
+
+
+
+ // this recovers durable queues and persistent messages
+
+ recover();
+
+ stateTransition(State.RECOVERING, State.STARTED);
+
+ }
+
+ private static synchronized void initialiseDriver() throws ClassNotFoundException
+ {
+ if(DRIVER_CLASS == null)
+ {
+ DRIVER_CLASS = (Class<Driver>) Class.forName(DERBY_DRIVER_NAME);
+ }
+ }
+
+ private void createOrOpenDatabase(final String environmentPath) throws SQLException
+ {
+ _connectionURL = "jdbc:derby:" + environmentPath + "/" + _virtualHost.getName() + ";create=true";
+
+ Connection conn = newConnection();
+
+ createVersionTable(conn);
+ createExchangeTable(conn);
+ createQueueTable(conn);
+ createBindingsTable(conn);
+ createQueueEntryTable(conn);
+ createMessageMetaDataTable(conn);
+ createMessageContentTable(conn);
+
+
+ }
+
+
+
+ private void createVersionTable(final Connection conn) throws SQLException
+ {
+ if(!tableExists(DB_VERSION_TABLE_NAME, conn))
+ {
+ Statement stmt = conn.createStatement();
+
+ stmt.execute("CREATE TABLE "+DB_VERSION_TABLE_NAME+" ( version int not null )");
+ stmt.close();
+
+ PreparedStatement pstmt = conn.prepareStatement("INSERT INTO "+DB_VERSION_TABLE_NAME+" ( version ) VALUES ( ? )");
+ pstmt.setInt(1, DB_VERSION);
+ pstmt.execute();
+ pstmt.close();
+ }
+
+ }
+
+
+ private void createExchangeTable(final Connection conn) throws SQLException
+ {
+ if(!tableExists(EXCHANGE_TABLE_NAME, conn))
+ {
+ Statement stmt = conn.createStatement();
+
+ stmt.execute("CREATE TABLE "+EXCHANGE_TABLE_NAME+" ( name varchar(255) not null, type varchar(255) not null, autodelete SMALLINT not null, PRIMARY KEY ( name ) )");
+ stmt.close();
+ }
+ }
+
+ private void createQueueTable(final Connection conn) throws SQLException
+ {
+ if(!tableExists(QUEUE_TABLE_NAME, conn))
+ {
+ Statement stmt = conn.createStatement();
+ stmt.execute("CREATE TABLE "+QUEUE_TABLE_NAME+" ( name varchar(255) not null, owner varchar(255), PRIMARY KEY ( name ) )");
+ stmt.close();
+ }
+ }
+
+ private void createBindingsTable(final Connection conn) throws SQLException
+ {
+ if(!tableExists(BINDINGS_TABLE_NAME, conn))
+ {
+ Statement stmt = conn.createStatement();
+ stmt.execute("CREATE TABLE "+BINDINGS_TABLE_NAME+" ( exchange_name varchar(255) not null, queue_name varchar(255) not null, binding_key varchar(255) not null, arguments blob , PRIMARY KEY ( exchange_name, queue_name, binding_key ) )");
+
+
+ stmt.close();
+ }
+
+ }
+
+ private void createQueueEntryTable(final Connection conn) throws SQLException
+ {
+ if(!tableExists(QUEUE_ENTRY_TABLE_NAME, conn))
+ {
+ Statement stmt = conn.createStatement();
+ stmt.execute("CREATE TABLE "+QUEUE_ENTRY_TABLE_NAME+" ( queue_name varchar(255) not null, message_id bigint not null, PRIMARY KEY (queue_name, message_id) )");
+
+ stmt.close();
+ }
+
+ }
+
+ private void createMessageMetaDataTable(final Connection conn) throws SQLException
+ {
+ if(!tableExists(MESSAGE_META_DATA_TABLE_NAME, conn))
+ {
+ Statement stmt = conn.createStatement();
+ stmt.execute("CREATE TABLE "+MESSAGE_META_DATA_TABLE_NAME+" ( message_id bigint not null, exchange_name varchar(255) not null, routing_key varchar(255), flag_mandatory smallint not null, flag_immediate smallint not null, content_header blob, chunk_count int not null, PRIMARY KEY ( message_id ) )");
+
+ stmt.close();
+ }
+
+ }
+
+
+ private void createMessageContentTable(final Connection conn) throws SQLException
+ {
+ if(!tableExists(MESSAGE_CONTENT_TABLE_NAME, conn))
+ {
+ Statement stmt = conn.createStatement();
+ stmt.execute("CREATE TABLE "+MESSAGE_CONTENT_TABLE_NAME+" ( message_id bigint not null, chunk_id int not null, content_chunk blob , PRIMARY KEY (message_id, chunk_id) )");
+
+ stmt.close();
+ }
+
+ }
+
+
+
+ private boolean tableExists(final String tableName, final Connection conn) throws SQLException
+ {
+ PreparedStatement stmt = conn.prepareStatement("SELECT 1 FROM SYS.SYSTABLES WHERE TABLENAME = ?");
+ stmt.setString(1, tableName);
+ ResultSet rs = stmt.executeQuery();
+ boolean exists = rs.next();
+ rs.close();
+ stmt.close();
+ return exists;
+ }
+
+ public void recover() throws AMQException
+ {
+ stateTransition(State.CONFIGURING, State.RECOVERING);
+
+ _logger.info("Recovering persistent state...");
+ StoreContext context = new StoreContext();
+
+ try
+ {
+ Map<AMQShortString, AMQQueue> queues = loadQueues();
+
+ recoverExchanges();
+
+//
+
+ try
+ {
+
+ beginTran(context);
+
+ deliverMessages(context, queues);
+ _logger.info("Persistent state recovered successfully");
+ commitTran(context);
+
+ }
+ finally
+ {
+ if(inTran(context))
+ {
+ abortTran(context);
+ }
+ }
+ }
+ catch (SQLException e)
+ {
+
+ throw new AMQException("Error recovering persistent state: " + e, e);
+ }
+
+ }
+
+ private Map<AMQShortString, AMQQueue> loadQueues() throws SQLException, AMQException
+ {
+ Connection conn = newConnection();
+
+
+ Statement stmt = conn.createStatement();
+ ResultSet rs = stmt.executeQuery("SELECT name, owner FROM " + QUEUE_TABLE_NAME);
+ Map<AMQShortString, AMQQueue> queueMap = new HashMap<AMQShortString, AMQQueue>();
+ while(rs.next())
+ {
+ 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);
+ _virtualHost.getQueueRegistry().registerQueue(q);
+ queueMap.put(queueNameShortString,q);
+
+ }
+ return queueMap;
+ }
+
+ private void recoverExchanges() throws AMQException, SQLException
+ {
+ for (Exchange exchange : loadExchanges())
+ {
+ recoverExchange(exchange);
+ }
+ }
+
+
+ private List<Exchange> loadExchanges() throws AMQException, SQLException
+ {
+
+ List<Exchange> exchanges = new ArrayList<Exchange>();
+ Connection conn = null;
+ try
+ {
+ conn = newConnection();
+
+
+ Statement stmt = conn.createStatement();
+ ResultSet rs = stmt.executeQuery("SELECT name, type, autodelete FROM " + EXCHANGE_TABLE_NAME);
+
+ Exchange exchange;
+ while(rs.next())
+ {
+ String exchangeName = rs.getString(1);
+ String type = rs.getString(2);
+ boolean autoDelete = rs.getShort(3) != 0;
+
+ exchange = _virtualHost.getExchangeFactory().createExchange(new AMQShortString(exchangeName), new AMQShortString(type), true, autoDelete, 0);
+ _virtualHost.getExchangeRegistry().registerExchange(exchange);
+ exchanges.add(exchange);
+
+ }
+ return exchanges;
+
+ }
+ finally
+ {
+ if(conn != null)
+ {
+ conn.close();
+ }
+ }
+
+ }
+
+ private void recoverExchange(Exchange exchange) throws AMQException, SQLException
+ {
+ _logger.info("Recovering durable exchange " + exchange.getName() + " of type " + exchange.getType() + "...");
+
+ QueueRegistry queueRegistry = _virtualHost.getQueueRegistry();
+
+ Connection conn = null;
+ try
+ {
+ conn = newConnection();
+
+ PreparedStatement stmt = conn.prepareStatement("SELECT queue_name, binding_key, arguments FROM " + BINDINGS_TABLE_NAME + " WHERE exchange_name = ?");
+ stmt.setString(1, exchange.getName().toString());
+
+ ResultSet rs = stmt.executeQuery();
+
+
+ while(rs.next())
+ {
+ String queueName = rs.getString(1);
+ String bindingKey = rs.getString(2);
+ Blob arguments = rs.getBlob(3);
+
+
+ AMQQueue queue = queueRegistry.getQueue(new AMQShortString(queueName));
+ if (queue == null)
+ {
+ _logger.error("Unkown queue: " + queueName + " cannot be bound to exchange: "
+ + exchange.getName());
+ }
+ else
+ {
+ _logger.info("Restoring binding: (Exchange: " + exchange.getName() + ", Queue: " + queueName
+ + ", Routing Key: " + bindingKey + ", Arguments: " + arguments
+ + ")");
+
+ FieldTable argumentsFT = null;
+ if(arguments != null)
+ {
+ byte[] argumentBytes = arguments.getBytes(0, (int) arguments.length());
+ ByteBuffer buf = ByteBuffer.wrap(argumentBytes);
+ argumentsFT = new FieldTable(buf,arguments.length());
+ }
+
+ queue.bind(exchange, bindingKey == null ? null : new AMQShortString(bindingKey), argumentsFT);
+ }
+ }
+ }
+ finally
+ {
+ if(conn != null)
+ {
+ conn.close();
+ }
+ }
+ }
+
+ public void close() throws Exception
+ {
+
+ _closed.getAndSet(true);
+
+ }
+
+ public void removeMessage(StoreContext storeContext, Long messageId) throws AMQException
+ {
+
+ boolean localTx = getOrCreateTransaction(storeContext);
+
+ Connection conn = getConnection(storeContext);
+ ConnectionWrapper wrapper = (ConnectionWrapper) storeContext.getPayload();
+
+
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Message Id: " + messageId + " Removing");
+ }
+
+ // first we need to look up the header to get the chunk count
+ MessageMetaData mmd = getMessageMetaData(storeContext, messageId);
+ try
+ {
+ PreparedStatement stmt = conn.prepareStatement("DELETE FROM " + MESSAGE_META_DATA_TABLE_NAME + " WHERE message_id = ?");
+ stmt.setLong(1,messageId);
+ wrapper.setRequiresCommit();
+ int results = stmt.executeUpdate();
+
+
+ if (results == 0)
+ {
+ if (localTx)
+ {
+ abortTran(storeContext);
+ }
+
+ throw new AMQException("Message metadata not found for message id " + messageId);
+ }
+ stmt.close();
+
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Deleted metadata for message " + messageId);
+ }
+
+
+ stmt = conn.prepareStatement("DELETE FROM " + MESSAGE_CONTENT_TABLE_NAME + " WHERE message_id = ?");
+ stmt.setLong(1,messageId);
+ results = stmt.executeUpdate();
+
+ if(results != mmd.getContentChunkCount())
+ {
+ if (localTx)
+ {
+ abortTran(storeContext);
+ }
+ throw new AMQException("Unexpected number of content chunks when deleting message. Expected " + mmd.getContentChunkCount() + " but found " + results);
+
+ }
+
+ if (localTx)
+ {
+ commitTran(storeContext);
+ }
+ }
+ catch (SQLException e)
+ {
+ if ((conn != null) && localTx)
+ {
+ abortTran(storeContext);
+ }
+
+ throw new AMQException("Error writing AMQMessage with id " + messageId + " to database: " + e, e);
+ }
+
+ }
+
+ public void createExchange(Exchange exchange) throws AMQException
+ {
+ if (_state != State.RECOVERING)
+ {
+ try
+ {
+ Connection conn = null;
+
+ try
+ {
+ conn = newConnection();
+
+ PreparedStatement stmt = conn.prepareStatement("INSERT INTO " + EXCHANGE_TABLE_NAME + " ( name, type, autodelete ) VALUES ( ?, ?, ? )");
+ stmt.setString(1, exchange.getName().toString());
+ stmt.setString(2, exchange.getType().toString());
+ stmt.setShort(3, exchange.isAutoDelete() ? (short) 1 : (short) 0);
+ stmt.execute();
+ stmt.close();
+ conn.commit();
+
+ }
+ finally
+ {
+ if(conn != null)
+ {
+ conn.close();
+
+ }
+ }
+ }
+ catch (SQLException e)
+ {
+ throw new AMQException("Error writing Exchange with name " + exchange.getName() + " to database: " + e, e);
+ }
+ }
+
+ }
+
+ public void removeExchange(Exchange exchange) throws AMQException
+ {
+ Connection conn = null;
+
+ try
+ {
+ conn = newConnection();
+ PreparedStatement stmt = conn.prepareStatement("DELETE FROM " + EXCHANGE_TABLE_NAME + " WHERE name = ?");
+ stmt.setString(1, exchange.getName().toString());
+ int results = stmt.executeUpdate();
+ if(results == 0)
+ {
+ throw new AMQException("Exchange " + exchange.getName() + " not found");
+ }
+ else
+ {
+ conn.commit();
+ stmt.close();
+ }
+ }
+ catch (SQLException e)
+ {
+ throw new AMQException("Error writing deleting with name " + exchange.getName() + " from database: " + e, e);
+ }
+ finally
+ {
+ if(conn != null)
+ {
+ try
+ {
+ conn.close();
+ }
+ catch (SQLException e)
+ {
+ _logger.error(e);
+ }
+ }
+
+ }
+ }
+
+ public void bindQueue(Exchange exchange, AMQShortString routingKey, AMQQueue queue, FieldTable args)
+ throws AMQException
+ {
+ if (_state != State.RECOVERING)
+ {
+ Connection conn = null;
+
+
+ try
+ {
+ conn = newConnection();
+ // exchange_name varchar(255) not null, queue_name varchar(255) not null, binding_key varchar(255), arguments blob
+ PreparedStatement stmt = conn.prepareStatement("INSERT INTO " + BINDINGS_TABLE_NAME + " ( exchange_name, queue_name, binding_key, arguments ) values ( ?, ?, ?, ? )");
+ stmt.setString(1, exchange.getName().toString() );
+ stmt.setString(2, queue.getName().toString());
+ stmt.setString(3, routingKey == null ? null : routingKey.toString());
+ if(args != null)
+ {
+ Blob blobArgs = conn.createBlob();
+ blobArgs.setBytes(0, args.getDataAsBytes());
+ stmt.setBlob(4, blobArgs);
+ }
+ else
+ {
+ stmt.setNull(4, Types.BLOB);
+ }
+
+ stmt.executeUpdate();
+ conn.commit();
+ stmt.close();
+ }
+ catch (SQLException e)
+ {
+ throw new AMQException("Error writing binding for AMQQueue with name " + queue.getName() + " to exchange "
+ + exchange.getName() + " to database: " + e, e);
+ }
+ finally
+ {
+ if(conn != null)
+ {
+ try
+ {
+ conn.close();
+ }
+ catch (SQLException e)
+ {
+ _logger.error(e);
+ }
+ }
+
+ }
+
+ }
+
+
+ }
+
+ public void unbindQueue(Exchange exchange, AMQShortString routingKey, AMQQueue queue, FieldTable args)
+ throws AMQException
+ {
+ Connection conn = null;
+
+
+ try
+ {
+ conn = newConnection();
+ // exchange_name varchar(255) not null, queue_name varchar(255) not null, binding_key varchar(255), arguments blob
+ PreparedStatement stmt = conn.prepareStatement("DELETE FROM " + BINDINGS_TABLE_NAME + " WHERE exchange_name = ? AND queue_name = ? AND binding_key = ?");
+ stmt.setString(1, exchange.getName().toString() );
+ stmt.setString(2, queue.getName().toString());
+ stmt.setString(3, routingKey == null ? null : routingKey.toString());
+
+
+ if(stmt.executeUpdate() != 1)
+ {
+ throw new AMQException("Queue binding for queue with name " + queue.getName() + " to exchange "
+ + exchange.getName() + " not found");
+ }
+ conn.commit();
+ stmt.close();
+ }
+ catch (SQLException e)
+ {
+ throw new AMQException("Error removing binding for AMQQueue with name " + queue.getName() + " to exchange "
+ + exchange.getName() + " in database: " + e, e);
+ }
+ finally
+ {
+ if(conn != null)
+ {
+ try
+ {
+ conn.close();
+ }
+ catch (SQLException e)
+ {
+ _logger.error(e);
+ }
+ }
+
+ }
+
+
+ }
+
+ public void createQueue(AMQQueue queue) throws AMQException
+ {
+ _logger.debug("public void createQueue(AMQQueue queue = " + queue + "): called");
+
+ if (_state != State.RECOVERING)
+ {
+ try
+ {
+ Connection conn = newConnection();
+
+ PreparedStatement stmt =
+ conn.prepareStatement("INSERT INTO " + QUEUE_TABLE_NAME + " (name, owner) VALUES (?, ?)");
+
+ stmt.setString(1, queue.getName().toString());
+ stmt.setString(2, queue.getOwner() == null ? null : queue.getOwner().toString());
+
+ stmt.execute();
+
+ stmt.close();
+
+ conn.commit();
+
+ conn.close();
+ }
+ catch (SQLException e)
+ {
+ throw new AMQException("Error writing AMQQueue with name " + queue.getName() + " to database: " + e, e);
+ }
+ }
+ }
+
+ private Connection newConnection() throws SQLException
+ {
+ return DriverManager.getConnection(_connectionURL);
+ }
+
+ public void removeQueue(AMQShortString name) throws AMQException
+ {
+
+ _logger.debug("public void removeQueue(AMQShortString name = " + name + "): called");
+ Connection conn = null;
+
+
+ try
+ {
+ conn = newConnection();
+ PreparedStatement stmt = conn.prepareStatement("DELETE FROM " + QUEUE_TABLE_NAME + " WHERE name = ?");
+ stmt.setString(1, name.toString());
+ int results = stmt.executeUpdate();
+
+
+ if (results == 0)
+ {
+ throw new AMQException("Queue " + name + " not found");
+ }
+
+ conn.commit();
+ stmt.close();
+ }
+ catch (SQLException e)
+ {
+ throw new AMQException("Error writing deleting with name " + name + " from database: " + e, e);
+ }
+ finally
+ {
+ if(conn != null)
+ {
+ try
+ {
+ conn.close();
+ }
+ catch (SQLException e)
+ {
+ _logger.error(e);
+ }
+ }
+
+ }
+
+
+ }
+
+ public void enqueueMessage(StoreContext context, AMQShortString name, Long messageId) throws AMQException
+ {
+
+ boolean localTx = getOrCreateTransaction(context);
+ Connection conn = getConnection(context);
+ ConnectionWrapper connWrapper = (ConnectionWrapper) context.getPayload();
+
+ try
+ {
+ PreparedStatement stmt = conn.prepareStatement("INSERT INTO " + QUEUE_ENTRY_TABLE_NAME + " (queue_name, message_id) values (?,?)");
+ stmt.setString(1,name.toString());
+ stmt.setLong(2,messageId);
+ stmt.executeUpdate();
+ connWrapper.requiresCommit();
+
+ if(localTx)
+ {
+ commitTran(context);
+ }
+
+
+
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Enqueuing message " + messageId + " on queue " + name + "[Connection" + conn + "]");
+ }
+ }
+ catch (SQLException e)
+ {
+ if(localTx)
+ {
+ abortTran(context);
+ }
+ _logger.error("Failed to enqueue: " + e, e);
+ throw new AMQException("Error writing enqueued message with id " + messageId + " for queue " + name
+ + " to database", e);
+ }
+
+ }
+
+ public void dequeueMessage(StoreContext context, AMQShortString name, Long messageId) throws AMQException
+ {
+
+ boolean localTx = getOrCreateTransaction(context);
+ Connection conn = getConnection(context);
+ ConnectionWrapper connWrapper = (ConnectionWrapper) context.getPayload();
+
+ try
+ {
+ PreparedStatement stmt = conn.prepareStatement("DELETE FROM " + QUEUE_ENTRY_TABLE_NAME + " WHERE queue_name = ? AND message_id =?");
+ stmt.setString(1,name.toString());
+ stmt.setLong(2,messageId);
+ int results = stmt.executeUpdate();
+
+ connWrapper.requiresCommit();
+
+ if(results != 1)
+ {
+ throw new AMQException("Unable to find message with id " + messageId + " on queue " + name);
+ }
+
+ if(localTx)
+ {
+ commitTran(context);
+ }
+
+
+
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Dequeuing message " + messageId + " on queue " + name + "[Connection" + conn + "]");
+ }
+ }
+ catch (SQLException e)
+ {
+ if(localTx)
+ {
+ abortTran(context);
+ }
+ _logger.error("Failed to dequeue: " + e, e);
+ throw new AMQException("Error deleting enqueued message with id " + messageId + " for queue " + name
+ + " from database", e);
+ }
+
+ }
+
+ private static final class ConnectionWrapper
+ {
+ private final Connection _connection;
+ private boolean _requiresCommit;
+
+ public ConnectionWrapper(Connection conn)
+ {
+ _connection = conn;
+ }
+
+ public void setRequiresCommit()
+ {
+ _requiresCommit = true;
+ }
+
+ public boolean requiresCommit()
+ {
+ return _requiresCommit;
+ }
+
+ public Connection getConnection()
+ {
+ return _connection;
+ }
+ }
+
+ public void beginTran(StoreContext context) throws AMQException
+ {
+ if (context.getPayload() != null)
+ {
+ throw new AMQException("Fatal internal error: transactional context is not empty at beginTran: "
+ + context.getPayload());
+ }
+ else
+ {
+ try
+ {
+ Connection conn = newConnection();
+
+
+ context.setPayload(new ConnectionWrapper(conn));
+ }
+ catch (SQLException e)
+ {
+ throw new AMQException("Error starting transaction: " + e, e);
+ }
+ }
+ }
+
+ public void commitTran(StoreContext context) throws AMQException
+ {
+ ConnectionWrapper connWrapper = (ConnectionWrapper) context.getPayload();
+
+ if (connWrapper == null)
+ {
+ throw new AMQException("Fatal internal error: transactional context is empty at commitTran");
+ }
+
+ try
+ {
+ if(connWrapper.requiresCommit())
+ {
+ Connection conn = connWrapper.getConnection();
+ conn.commit();
+
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("commit tran completed");
+ }
+ conn.close();
+ }
+ }
+ catch (SQLException e)
+ {
+ throw new AMQException("Error commit tx: " + e, e);
+ }
+ finally
+ {
+ context.setPayload(null);
+ }
+ }
+
+ public void abortTran(StoreContext context) throws AMQException
+ {
+ ConnectionWrapper connWrapper = (ConnectionWrapper) context.getPayload();
+
+ if (connWrapper == null)
+ {
+ throw new AMQException("Fatal internal error: transactional context is empty at abortTran");
+ }
+
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("abort tran called: " + connWrapper.getConnection());
+ }
+
+ try
+ {
+ Connection conn = connWrapper.getConnection();
+ if(connWrapper.requiresCommit())
+ {
+ conn.rollback();
+ }
+
+ conn.close();
+ }
+ catch (SQLException e)
+ {
+ throw new AMQException("Error aborting transaction: " + e, e);
+ }
+ finally
+ {
+ context.setPayload(null);
+ }
+ }
+
+ public boolean inTran(StoreContext context)
+ {
+ return context.getPayload() != null;
+ }
+
+ public Long getNewMessageId()
+ {
+ return _messageId.getAndIncrement();
+ }
+
+ public void storeContentBodyChunk(StoreContext context,
+ Long messageId,
+ int index,
+ ContentChunk contentBody,
+ boolean lastContentBody) throws AMQException
+ {
+ boolean localTx = getOrCreateTransaction(context);
+ Connection conn = getConnection(context);
+ ConnectionWrapper connWrapper = (ConnectionWrapper) context.getPayload();
+
+ try
+ {
+ PreparedStatement stmt = conn.prepareStatement("INSERT INTO " + MESSAGE_CONTENT_TABLE_NAME + "( message_id, chunk_id, content_chunk ) values (?, ?, ?)");
+ stmt.setLong(1,messageId);
+ stmt.setInt(2, index);
+ byte[] chunkData = new byte[contentBody.getSize()];
+ contentBody.getData().duplicate().get(chunkData);
+ Blob dataAsBlob = conn.createBlob();
+ dataAsBlob.setBytes(1L, chunkData);
+ stmt.setBlob(3, dataAsBlob);
+ stmt.executeUpdate();
+ connWrapper.requiresCommit();
+
+ if(localTx)
+ {
+ commitTran(context);
+ }
+ }
+ catch (SQLException e)
+ {
+ if(localTx)
+ {
+ abortTran(context);
+ }
+
+ throw new AMQException("Error writing AMQMessage with id " + messageId + " to database: " + e, e);
+ }
+
+ }
+
+ public void storeMessageMetaData(StoreContext context, Long messageId, MessageMetaData mmd)
+ throws AMQException
+ {
+
+ boolean localTx = getOrCreateTransaction(context);
+ Connection conn = getConnection(context);
+ ConnectionWrapper connWrapper = (ConnectionWrapper) context.getPayload();
+
+ try
+ {
+
+ PreparedStatement stmt = conn.prepareStatement("INSERT INTO " + MESSAGE_META_DATA_TABLE_NAME + "( message_id , exchange_name , routing_key , flag_mandatory , flag_immediate , content_header , chunk_count ) values (?, ?, ?, ?, ?, ?, ?)");
+ stmt.setLong(1,messageId);
+ stmt.setString(2, mmd.getMessagePublishInfo().getExchange().toString());
+ stmt.setString(3, mmd.getMessagePublishInfo().getRoutingKey().toString());
+ stmt.setShort(4, mmd.getMessagePublishInfo().isMandatory() ? (short) 1 : (short) 0);
+ stmt.setShort(5, mmd.getMessagePublishInfo().isImmediate() ? (short) 1 : (short) 0);
+
+ ContentHeaderBody headerBody = mmd.getContentHeaderBody();
+ final int bodySize = headerBody.getSize();
+ byte[] underlying = new byte[bodySize];
+ ByteBuffer buf = ByteBuffer.wrap(underlying);
+ headerBody.writePayload(buf);
+ Blob dataAsBlob = conn.createBlob();
+ dataAsBlob.setBytes(1L, underlying);
+ stmt.setBlob(6, dataAsBlob);
+
+ stmt.setInt(7, mmd.getContentChunkCount());
+
+ stmt.executeUpdate();
+ connWrapper.requiresCommit();
+
+ if(localTx)
+ {
+ commitTran(context);
+ }
+ }
+ catch (SQLException e)
+ {
+ if(localTx)
+ {
+ abortTran(context);
+ }
+
+ throw new AMQException("Error writing AMQMessage with id " + messageId + " to database: " + e, e);
+ }
+
+
+ }
+
+ public MessageMetaData getMessageMetaData(StoreContext context, Long messageId) throws AMQException
+ {
+ boolean localTx = getOrCreateTransaction(context);
+ Connection conn = getConnection(context);
+
+
+ try
+ {
+
+ PreparedStatement stmt = conn.prepareStatement("SELECT exchange_name , routing_key , flag_mandatory , flag_immediate , content_header , chunk_count FROM " + MESSAGE_META_DATA_TABLE_NAME + " WHERE message_id = ?");
+ stmt.setLong(1,messageId);
+ ResultSet rs = stmt.executeQuery();
+
+ if(rs.next())
+ {
+ final AMQShortString exchange = new AMQShortString(rs.getString(1));
+ final AMQShortString routingKey = rs.getString(2) == null ? null : new AMQShortString(rs.getString(2));
+ final boolean mandatory = (rs.getShort(3) != (short)0);
+ final boolean immediate = (rs.getShort(4) != (short)0);
+ MessagePublishInfo info = new MessagePublishInfo()
+ {
+
+ public AMQShortString getExchange()
+ {
+ return exchange;
+ }
+
+ public void setExchange(AMQShortString exchange)
+ {
+
+ }
+
+ public boolean isImmediate()
+ {
+ return immediate;
+ }
+
+ public boolean isMandatory()
+ {
+ return mandatory;
+ }
+
+ public AMQShortString getRoutingKey()
+ {
+ return routingKey;
+ }
+ } ;
+
+ Blob dataAsBlob = rs.getBlob(5);
+
+ byte[] dataAsBytes = dataAsBlob.getBytes(1,(int) dataAsBlob.length());
+ ByteBuffer buf = ByteBuffer.wrap(dataAsBytes);
+
+ ContentHeaderBody chb = ContentHeaderBody.createFromBuffer(buf, dataAsBytes.length);
+
+ if(localTx)
+ {
+ commitTran(context);
+ }
+
+ return new MessageMetaData(info, chb, rs.getInt(6));
+
+ }
+ else
+ {
+ if(localTx)
+ {
+ abortTran(context);
+ }
+ throw new AMQException("Metadata not found for message with id " + messageId);
+ }
+ }
+ catch (SQLException e)
+ {
+ if(localTx)
+ {
+ abortTran(context);
+ }
+
+ throw new AMQException("Error reading AMQMessage with id " + messageId + " from database: " + e, e);
+ }
+
+
+ }
+
+ public ContentChunk getContentBodyChunk(StoreContext context, Long messageId, int index) throws AMQException
+ {
+ boolean localTx = getOrCreateTransaction(context);
+ Connection conn = getConnection(context);
+
+
+ try
+ {
+
+ PreparedStatement stmt = conn.prepareStatement("SELECT content_chunk FROM " + MESSAGE_CONTENT_TABLE_NAME + " WHERE message_id = ? and chunk_id = ?");
+ stmt.setLong(1,messageId);
+ stmt.setInt(2, index);
+ ResultSet rs = stmt.executeQuery();
+
+ if(rs.next())
+ {
+ Blob dataAsBlob = rs.getBlob(1);
+
+ final int size = (int) dataAsBlob.length();
+ byte[] dataAsBytes = dataAsBlob.getBytes(1, size);
+ final ByteBuffer buf = ByteBuffer.wrap(dataAsBytes);
+
+ ContentChunk cb = new ContentChunk()
+ {
+
+ public int getSize()
+ {
+ return size;
+ }
+
+ public ByteBuffer getData()
+ {
+ return buf;
+ }
+
+ public void reduceToFit()
+ {
+
+ }
+ };
+
+ if(localTx)
+ {
+ commitTran(context);
+ }
+
+ return cb;
+
+ }
+ else
+ {
+ if(localTx)
+ {
+ abortTran(context);
+ }
+ throw new AMQException("Message not found for message with id " + messageId);
+ }
+ }
+ catch (SQLException e)
+ {
+ if(localTx)
+ {
+ abortTran(context);
+ }
+
+ throw new AMQException("Error reading AMQMessage with id " + messageId + " from database: " + e, e);
+ }
+
+
+
+ }
+
+ private void checkNotClosed() throws MessageStoreClosedException
+ {
+ if (_closed.get())
+ {
+ throw new MessageStoreClosedException();
+ }
+ }
+
+
+ private static final class ProcessAction
+ {
+ private final AMQQueue _queue;
+ private final StoreContext _context;
+ private final AMQMessage _message;
+
+ public ProcessAction(AMQQueue queue, StoreContext context, AMQMessage message)
+ {
+ _queue = queue;
+ _context = context;
+ _message = message;
+ }
+
+ public void process() throws AMQException
+ {
+ _queue.enqueue(_context, _message);
+ }
+
+ }
+
+
+ private void deliverMessages(final StoreContext context, Map<AMQShortString, AMQQueue> queues)
+ throws SQLException, AMQException
+ {
+ Map<Long, AMQMessage> msgMap = new HashMap<Long,AMQMessage>();
+ List<ProcessAction> actions = new ArrayList<ProcessAction>();
+
+ Map<AMQShortString, Integer> queueRecoveries = new TreeMap<AMQShortString, Integer>();
+
+ final boolean inLocaltran = inTran(context);
+ Connection conn = null;
+ try
+ {
+
+ if(inLocaltran)
+ {
+ conn = getConnection(context);
+ }
+ else
+ {
+ conn = newConnection();
+ }
+
+
+ MessageHandleFactory messageHandleFactory = new MessageHandleFactory();
+ long maxId = 1;
+
+ TransactionalContext txnContext = new NonTransactionalContext(this, new StoreContext(), null, null);
+
+ Statement stmt = conn.createStatement();
+ ResultSet rs = stmt.executeQuery("SELECT queue_name, message_id FROM " + QUEUE_ENTRY_TABLE_NAME);
+
+
+ while (rs.next())
+ {
+
+
+
+ AMQShortString queueName = new AMQShortString(rs.getString(1));
+
+
+ AMQQueue queue = queues.get(queueName);
+ if (queue == null)
+ {
+ queue = AMQQueueFactory.createAMQQueueImpl(queueName, false, null, false, _virtualHost);
+ _virtualHost.getQueueRegistry().registerQueue(queue);
+ queues.put(queueName, queue);
+ }
+
+ long messageId = rs.getLong(2);
+ maxId = Math.max(maxId, messageId);
+ AMQMessage message = msgMap.get(messageId);
+
+ if(message != null)
+ {
+ message.incrementReference();
+ }
+ else
+ {
+ message = new AMQMessage(messageId, this, messageHandleFactory, txnContext);
+ msgMap.put(messageId,message);
+ }
+
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("On recovery, delivering " + message.getMessageId() + " to " + queue.getName());
+ }
+
+ if (_logger.isInfoEnabled())
+ {
+ Integer count = queueRecoveries.get(queueName);
+ if (count == null)
+ {
+ count = 0;
+ }
+
+ queueRecoveries.put(queueName, ++count);
+
+ }
+
+ actions.add(new ProcessAction(queue, context, message));
+
+ }
+
+ for(ProcessAction action : actions)
+ {
+ action.process();
+ }
+
+ _messageId.set(maxId + 1);
+ }
+ catch (SQLException e)
+ {
+ _logger.error("Error: " + e, e);
+ throw e;
+ }
+ finally
+ {
+ if (inLocaltran && conn != null)
+ {
+ conn.close();
+ }
+ }
+
+ if (_logger.isInfoEnabled())
+ {
+ _logger.info("Recovered message counts: " + queueRecoveries);
+ }
+ }
+
+ private Connection getConnection(final StoreContext context)
+ {
+ return ((ConnectionWrapper)context.getPayload()).getConnection();
+ }
+
+ private boolean getOrCreateTransaction(StoreContext context) throws AMQException
+ {
+
+ ConnectionWrapper tx = (ConnectionWrapper) context.getPayload();
+ if (tx == null)
+ {
+ beginTran(context);
+ return true;
+ }
+
+ return false;
+ }
+
+ private synchronized void stateTransition(State requiredState, State newState) throws AMQException
+ {
+ if (_state != requiredState)
+ {
+ throw new AMQException("Cannot transition to the state: " + newState + "; need to be in state: " + requiredState
+ + "; currently in state: " + _state);
+ }
+
+ _state = newState;
+ }
+}
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 7a6e0b011f..3316302719 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,9 +26,10 @@ 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;
-import org.apache.qpid.server.queue.MessageMetaData;
import org.apache.qpid.server.virtualhost.VirtualHost;
import java.util.ArrayList;
diff --git a/java/broker/src/main/java/org/apache/qpid/server/store/MessageStore.java b/java/broker/src/main/java/org/apache/qpid/server/store/MessageStore.java
index 2a83d9b649..3a0d865876 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/store/MessageStore.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/store/MessageStore.java
@@ -27,8 +27,8 @@ import org.apache.qpid.framing.AMQShortString;
import org.apache.qpid.framing.FieldTable;
import org.apache.qpid.framing.abstraction.ContentChunk;
import org.apache.qpid.server.exchange.Exchange;
-import org.apache.qpid.server.queue.AMQQueue;
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/broker/src/main/java/org/apache/qpid/server/subscription/Subscription.java b/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription.java
new file mode 100644
index 0000000000..3f9bd65008
--- /dev/null
+++ b/java/broker/src/main/java/org/apache/qpid/server/subscription/Subscription.java
@@ -0,0 +1,108 @@
+/*
+ *
+ * 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.AMQException;
+import org.apache.qpid.framing.AMQShortString;
+import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.queue.QueueEntry;
+
+public interface Subscription
+{
+ boolean isActive();
+
+
+ public static enum State
+ {
+ ACTIVE,
+ SUSPENDED,
+ CLOSED
+ }
+
+ public static interface StateListener
+ {
+ public void stateChange(Subscription sub, State oldState, State newState);
+ }
+
+ AMQQueue getQueue();
+
+ void setQueue(AMQQueue queue);
+
+ AMQChannel getChannel();
+
+ AMQShortString getConumerTag();
+
+ boolean isSuspended();
+
+ boolean hasInterest(QueueEntry msg);
+
+ boolean isAutoClose();
+
+ boolean isClosed();
+
+ boolean isBrowser();
+
+ void close();
+
+
+
+ boolean filtersMessages();
+
+ void send(QueueEntry msg) throws AMQException;
+
+ 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();
+
+
+ void resend(final QueueEntry entry) throws AMQException;
+
+ void restoreCredit(final QueueEntry queueEntry);
+
+ void setStateListener(final StateListener listener);
+
+ Object getQueueContext();
+
+ boolean setQueueContext(Object expected, Object newValue);
+
+}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionFactory.java b/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionFactory.java
index 917f7c4e97..3e6db071e3 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/queue/SubscriptionFactory.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionFactory.java
@@ -18,12 +18,14 @@
* under the License.
*
*/
-package org.apache.qpid.server.queue;
+package org.apache.qpid.server.subscription;
import org.apache.qpid.AMQException;
import org.apache.qpid.framing.AMQShortString;
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;
/**
* Allows the customisation of the creation of a subscription. This is typically done within an AMQQueue. This factory
@@ -34,10 +36,12 @@ import org.apache.qpid.server.protocol.AMQProtocolSession;
*/
public interface SubscriptionFactory
{
- Subscription createSubscription(int channel, AMQProtocolSession protocolSession, AMQShortString consumerTag, boolean acks,
- FieldTable filters, boolean noLocal, AMQQueue queue) throws AMQException;
+ Subscription createSubscription(int channel,
+ AMQProtocolSession protocolSession,
+ AMQShortString consumerTag,
+ boolean acks,
+ FieldTable filters,
+ boolean noLocal, FlowCreditManager creditManager) throws AMQException;
- Subscription createSubscription(int channel, AMQProtocolSession protocolSession, AMQShortString consumerTag)
- 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
new file mode 100644
index 0000000000..da419e4ff0
--- /dev/null
+++ b/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionFactoryImpl.java
@@ -0,0 +1,74 @@
+/*
+ *
+ * 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.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.framing.AMQShortString;
+import org.apache.qpid.framing.FieldTable;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.common.AMQPFilterTypes;
+
+public class SubscriptionFactoryImpl implements SubscriptionFactory
+{
+
+ /* private SubscriptionFactoryImpl()
+ {
+
+ }*/
+
+ public Subscription createSubscription(int channel, AMQProtocolSession protocolSession,
+ AMQShortString consumerTag, boolean acks, FieldTable filters,
+ boolean noLocal, FlowCreditManager creditManager) throws AMQException
+ {
+
+ boolean isBrowser;
+
+ if (filters != null)
+ {
+ Boolean isBrowserObj = (Boolean) filters.get(AMQPFilterTypes.NO_CONSUME.getValue());
+ isBrowser = (isBrowserObj != null) && isBrowserObj.booleanValue();
+ }
+ else
+ {
+ isBrowser = false;
+ }
+
+ if(isBrowser)
+ {
+ return new SubscriptionImpl.BrowserSubscription(channel, protocolSession, consumerTag, filters, noLocal, creditManager);
+ }
+ else if(acks)
+ {
+ return new SubscriptionImpl.AckSubscription(channel, protocolSession, consumerTag, filters, noLocal, creditManager);
+ }
+ else
+ {
+ return new SubscriptionImpl.NoAckSubscription(channel, protocolSession, consumerTag, filters, noLocal, creditManager);
+ }
+ }
+
+
+
+ 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
new file mode 100644
index 0000000000..6f28bbba64
--- /dev/null
+++ b/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionImpl.java
@@ -0,0 +1,554 @@
+/*
+ *
+ * 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 java.util.concurrent.atomic.AtomicBoolean;
+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;
+import org.apache.qpid.framing.FieldTable;
+import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.queue.QueueEntry;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.subscription.Subscription;
+import org.apache.qpid.server.flow.FlowCreditManager;
+import org.apache.qpid.server.filter.FilterManager;
+import org.apache.qpid.server.filter.FilterManagerFactory;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.store.StoreContext;
+
+/**
+ * Encapsulation of a supscription to a queue. <p/> Ties together the protocol session of a subscriber, the consumer tag
+ * that was given out by the broker and the channel id. <p/>
+ */
+public abstract class SubscriptionImpl implements Subscription, FlowCreditManager.FlowCreditManagerListener
+{
+ private final AtomicBoolean _suspended = new AtomicBoolean(false);
+
+ private StateListener _stateListener = new StateListener()
+ {
+
+ public void stateChange(Subscription sub, State oldState, State newState)
+ {
+
+ }
+ };
+
+
+ private final AtomicReference<State> _state = new AtomicReference<State>(State.ACTIVE);
+ private final AtomicReference<Object> _queueContext = new AtomicReference<Object>(null);
+
+ static final class BrowserSubscription extends SubscriptionImpl
+ {
+ public BrowserSubscription(int channelId, AMQProtocolSession protocolSession,
+ AMQShortString consumerTag, FieldTable filters,
+ boolean noLocal, FlowCreditManager creditManager)
+ throws AMQException
+ {
+ super(channelId, protocolSession, consumerTag, filters, noLocal, creditManager);
+ }
+
+ public boolean isBrowser()
+ {
+ return true;
+ }
+
+ /**
+ * This method can be called by each of the publisher threads. As a result all changes to the channel object must be
+ * thread safe.
+ *
+ * @param msg The message to send
+ * @throws AMQException
+ */
+ public void send(QueueEntry msg) throws AMQException
+ {
+ // We don't decrement the reference here as we don't want to consume the message
+ // but we do want to send it to the client.
+
+ synchronized (getChannel())
+ {
+ long deliveryTag = getChannel().getNextDeliveryTag();
+
+ getProtocolSession().getProtocolOutputConverter().writeDeliver(msg.getMessage(), getChannel().getChannelId(), deliveryTag, getConumerTag());
+ }
+
+ }
+ }
+
+ static final class NoAckSubscription extends SubscriptionImpl
+ {
+ public NoAckSubscription(int channelId, AMQProtocolSession protocolSession,
+ AMQShortString consumerTag, FieldTable filters,
+ boolean noLocal, FlowCreditManager creditManager)
+ throws AMQException
+ {
+ super(channelId, protocolSession, consumerTag, filters, noLocal, creditManager);
+ }
+
+
+ public boolean isBrowser()
+ {
+ return false;
+ }
+
+ /**
+ * This method can be called by each of the publisher threads. As a result all changes to the channel object must be
+ * thread safe.
+ *
+ * @param entry The message to send
+ * @throws AMQException
+ */
+ 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.
+
+ // 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.
+ entry.dequeue(storeContext);
+
+
+ synchronized (getChannel())
+ {
+ long deliveryTag = getChannel().getNextDeliveryTag();
+
+ getProtocolSession().getProtocolOutputConverter().writeDeliver(entry.getMessage(), getChannel().getChannelId(), deliveryTag, getConumerTag());
+
+ }
+ entry.dispose(storeContext);
+ }
+ finally
+ {
+ //Only set delivered if it actually was writen successfully..
+ // using a try->finally would set it even if an error occured.
+ // Is this what we want?
+
+ entry.setDeliveredToSubscription();
+ }
+ }
+
+ public boolean wouldSuspend(QueueEntry msg)
+ {
+ return false;
+ }
+
+ }
+
+ static final class AckSubscription extends SubscriptionImpl
+ {
+ public AckSubscription(int channelId, AMQProtocolSession protocolSession,
+ AMQShortString consumerTag, FieldTable filters,
+ boolean noLocal, FlowCreditManager creditManager)
+ throws AMQException
+ {
+ super(channelId, protocolSession, consumerTag, filters, noLocal, creditManager);
+ }
+
+ public boolean isBrowser()
+ {
+ return false;
+ }
+
+
+ /**
+ * This method can be called by each of the publisher threads. As a result all changes to the channel object must be
+ * thread safe.
+ *
+ * @param entry The message to send
+ * @throws AMQException
+ */
+ 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.
+
+ // 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.
+
+ synchronized (getChannel())
+ {
+ long deliveryTag = getChannel().getNextDeliveryTag();
+
+
+
+
+ getChannel().addUnacknowledgedMessage(entry, deliveryTag, this);
+
+ getProtocolSession().getProtocolOutputConverter().writeDeliver(entry.getMessage(), getChannel().getChannelId(), deliveryTag, getConumerTag());
+
+ }
+ }
+ finally
+ {
+ //Only set delivered if it actually was writen successfully..
+ // using a try->finally would set it even if an error occured.
+ // Is this what we want?
+
+ entry.setDeliveredToSubscription();
+ }
+ }
+
+
+
+ }
+
+
+ private static final Logger _logger = Logger.getLogger(SubscriptionImpl.class);
+
+ private final AMQChannel _channel;
+
+ private final AMQShortString _consumerTag;
+
+
+ private final boolean _noLocal;
+
+ private final FlowCreditManager _creditManager;
+
+ private FilterManager _filters;
+
+ private final Boolean _autoClose;
+
+
+ private static final String CLIENT_PROPERTIES_INSTANCE = ClientProperties.instance.toString();
+
+ private AMQQueue _queue;
+ private final AtomicBoolean _sendLock = new AtomicBoolean(false);
+
+
+
+ public SubscriptionImpl(int channelId, AMQProtocolSession protocolSession,
+ AMQShortString consumerTag, FieldTable arguments,
+ 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");
+ }
+
+ _channel = channel;
+ _consumerTag = consumerTag;
+
+ _creditManager = creditManager;
+ creditManager.addStateListener(this);
+
+ _noLocal = noLocal;
+
+
+ _filters = FilterManagerFactory.createManager(arguments);
+
+
+
+
+
+ if (_filters != null)
+ {
+ Object autoClose = arguments.get(AMQPFilterTypes.AUTO_CLOSE.getValue());
+ if (autoClose != null)
+ {
+ _autoClose = (Boolean) autoClose;
+ }
+ else
+ {
+ _autoClose = false;
+ }
+ }
+ else
+ {
+ _autoClose = false;
+ }
+
+
+ }
+
+
+
+ public synchronized void setQueue(AMQQueue queue)
+ {
+ if(getQueue() != null)
+ {
+ throw new IllegalStateException("Attempt to set queue for subscription " + this + " to " + queue + "when already set to " + getQueue());
+ }
+ _queue = queue;
+ }
+
+ public String toString()
+ {
+ String subscriber = "[channel=" + _channel +
+ ", consumerTag=" + _consumerTag +
+ ", session=" + getProtocolSession().getKey() ;
+
+ return subscriber + "]";
+ }
+
+ /**
+ * This method can be called by each of the publisher threads. As a result all changes to the channel object must be
+ * thread safe.
+ *
+ * @param msg The message to send
+ * @throws AMQException
+ */
+ abstract public void send(QueueEntry msg) throws AMQException;
+
+
+ public boolean isSuspended()
+ {
+ return !isActive() || _channel.isSuspended() || _sendLock.get();
+ }
+
+ /**
+ * Callback indicating that a queue has been deleted.
+ *
+ * @param queue The queue to delete
+ */
+ public void queueDeleted(AMQQueue queue)
+ {
+ _sendLock.set(true);
+// _channel.queueDeleted(queue);
+ }
+
+ public boolean filtersMessages()
+ {
+ return _filters != null || _noLocal;
+ }
+
+ public boolean hasInterest(QueueEntry entry)
+ {
+ //check that the message hasn't been rejected
+ if (entry.isRejectedBy(this))
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Subscription:" + debugIdentity() + " rejected message:" + entry.debugIdentity());
+ }
+// return false;
+ }
+
+
+
+ //todo - client id should be recoreded and this test removed but handled below
+ if (_noLocal)
+ {
+ final Object publisherId = entry.getMessage().getPublisherClientInstance();
+
+ // We don't want local messages so check to see if message is one we sent
+ Object localInstance;
+
+ if (publisherId != null && (getProtocolSession().getClientProperties() != null) &&
+ (localInstance = getProtocolSession().getClientProperties().getObject(CLIENT_PROPERTIES_INSTANCE)) != null)
+ {
+ if(publisherId.equals(localInstance))
+ {
+ return false;
+ }
+ }
+ else
+ {
+
+ localInstance = getProtocolSession().getClientIdentifier();
+ //todo - client id should be recoreded and this test removed but handled here
+
+
+ if (localInstance != null && localInstance.equals(entry.getMessage().getPublisherIdentifier()))
+ {
+ return false;
+ }
+ }
+
+
+ }
+
+
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("(" + debugIdentity() + ") checking filters for message (" + entry.debugIdentity());
+ }
+ return checkFilters(entry);
+
+ }
+
+ private String id = String.valueOf(System.identityHashCode(this));
+
+ private String debugIdentity()
+ {
+ return id;
+ }
+
+ private boolean checkFilters(QueueEntry msg)
+ {
+ return (_filters == null) || _filters.allAllow(msg.getMessage());
+ }
+
+ public boolean isAutoClose()
+ {
+ return _autoClose;
+ }
+
+ public void close()
+ {
+ boolean closed = false;
+ State state = getState();
+ synchronized (_sendLock)
+ {
+ while(!closed && state != State.CLOSED)
+ {
+ closed = _state.compareAndSet(state, State.CLOSED);
+ if(!closed)
+ {
+ state = getState();
+ }
+ else
+ {
+ _stateListener.stateChange(this,state, State.CLOSED);
+ }
+ }
+ _creditManager.removeListener(this);
+ }
+
+ if (closed)
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Called close() on a closed subscription");
+ }
+
+ return;
+ }
+
+ if (_logger.isInfoEnabled())
+ {
+ _logger.info("Closing subscription (" + debugIdentity() + "):" + this);
+ }
+ }
+
+ public boolean isClosed()
+ {
+ return getState() == State.CLOSED;
+ }
+
+
+ public boolean wouldSuspend(QueueEntry msg)
+ {
+ return !_creditManager.useCreditForMessage(msg.getMessage());//_channel.wouldSuspend(msg.getMessage());
+ }
+
+ public Object getSendLock()
+ {
+ return _sendLock;
+ }
+
+ public void resend(final QueueEntry entry) throws AMQException
+ {
+ _queue.resend(entry, this);
+ }
+
+ public AMQChannel getChannel()
+ {
+ return _channel;
+ }
+
+ public AMQShortString getConumerTag()
+ {
+ return _consumerTag;
+ }
+
+ public AMQProtocolSession getProtocolSession()
+ {
+ return _channel.getProtocolSession();
+ }
+
+ public AMQQueue getQueue()
+ {
+ return _queue;
+ }
+
+ public void restoreCredit(final QueueEntry queueEntry)
+ {
+ _creditManager.addCredit(1, queueEntry.getSize());
+ }
+
+
+ public void creditStateChanged(boolean hasCredit)
+ {
+
+ if(hasCredit)
+ {
+ if(_state.compareAndSet(State.SUSPENDED, State.ACTIVE))
+ {
+ _stateListener.stateChange(this, State.SUSPENDED, State.ACTIVE);
+ }
+ }
+ else
+ {
+ if(_state.compareAndSet(State.ACTIVE, State.SUSPENDED))
+ {
+ _stateListener.stateChange(this, State.ACTIVE, State.SUSPENDED);
+ }
+ }
+ }
+
+ public State getState()
+ {
+ return _state.get();
+ }
+
+
+ public void setStateListener(final StateListener listener)
+ {
+ _stateListener = listener;
+ }
+
+
+ public Object getQueueContext()
+ {
+ return _queueContext.get();
+ }
+
+ public boolean setQueueContext(Object expected, Object newvalue)
+ {
+ return _queueContext.compareAndSet(expected,newvalue);
+ }
+
+
+ public boolean isActive()
+ {
+ return getState() == State.ACTIVE;
+ }
+}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionList.java b/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionList.java
new file mode 100644
index 0000000000..3fbb6bfa4a
--- /dev/null
+++ b/java/broker/src/main/java/org/apache/qpid/server/subscription/SubscriptionList.java
@@ -0,0 +1,247 @@
+/*
+*
+* 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.AMQQueue;
+import org.apache.qpid.server.subscription.Subscription;
+
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.nio.ByteBuffer;
+
+public class SubscriptionList
+{
+
+ private final SubscriptionNode _head = new SubscriptionNode();
+
+ private AtomicReference<SubscriptionNode> _tail = new AtomicReference<SubscriptionNode>(_head);
+ private final AMQQueue _queue;
+ private AtomicInteger _size = new AtomicInteger();
+
+
+ public final class SubscriptionNode
+ {
+ private final AtomicBoolean _deleted = new AtomicBoolean();
+ private final AtomicReference<SubscriptionNode> _next = new AtomicReference<SubscriptionNode>();
+ private final Subscription _sub;
+
+
+ public SubscriptionNode()
+ {
+
+ _sub = null;
+ _deleted.set(true);
+ }
+
+ public SubscriptionNode(final Subscription sub)
+ {
+ _sub = sub;
+ }
+
+
+ public SubscriptionNode getNext()
+ {
+
+ SubscriptionNode next = nextNode();
+ while(next != null && next.isDeleted())
+ {
+
+ final SubscriptionNode newNext = next.nextNode();
+ if(newNext != null)
+ {
+ _next.compareAndSet(next, newNext);
+ next = nextNode();
+ }
+ else
+ {
+ next = null;
+ }
+
+ }
+ return next;
+ }
+
+ private SubscriptionNode nextNode()
+ {
+ return _next.get();
+ }
+
+ public boolean isDeleted()
+ {
+ return _deleted.get();
+ }
+
+
+ public boolean delete()
+ {
+ if(_deleted.compareAndSet(false,true))
+ {
+ _size.decrementAndGet();
+ advanceHead();
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+
+ public Subscription getSubscription()
+ {
+ return _sub;
+ }
+ }
+
+
+ public SubscriptionList(AMQQueue queue)
+ {
+ _queue = queue;
+ }
+
+ private void advanceHead()
+ {
+ SubscriptionNode head = _head.nextNode();
+ while(head._next.get() != null && head.isDeleted())
+ {
+
+ final SubscriptionNode newhead = head.nextNode();
+ if(newhead != null)
+ {
+ _head._next.compareAndSet(head, newhead);
+ }
+ head = _head.nextNode();
+ }
+ }
+
+
+ public SubscriptionNode add(Subscription sub)
+ {
+ SubscriptionNode node = new SubscriptionNode(sub);
+ for (;;)
+ {
+ SubscriptionNode tail = _tail.get();
+ SubscriptionNode next = tail.nextNode();
+ if (tail == _tail.get())
+ {
+ if (next == null)
+ {
+ if (tail._next.compareAndSet(null, node))
+ {
+ _tail.compareAndSet(tail, node);
+ _size.incrementAndGet();
+ return node;
+ }
+ }
+ else
+ {
+ _tail.compareAndSet(tail, next);
+ }
+ }
+ }
+
+ }
+
+ public boolean remove(Subscription sub)
+ {
+ SubscriptionNode node = _head.getNext();
+ while(node != null)
+ {
+ if(sub.equals(node._sub) && node.delete())
+ {
+ return true;
+ }
+ node = node.getNext();
+ }
+ return false;
+ }
+
+
+ public class SubscriptionNodeIterator
+ {
+
+ private SubscriptionNode _lastNode;
+
+ SubscriptionNodeIterator(SubscriptionNode startNode)
+ {
+ _lastNode = startNode;
+ }
+
+
+ public boolean atTail()
+ {
+ return _lastNode.nextNode() == null;
+ }
+
+ public SubscriptionNode getNode()
+ {
+
+ return _lastNode;
+
+ }
+
+ public boolean advance()
+ {
+
+ if(!atTail())
+ {
+ SubscriptionNode nextNode = _lastNode.nextNode();
+ while(nextNode.isDeleted() && nextNode.nextNode() != null)
+ {
+ nextNode = nextNode.nextNode();
+ }
+ _lastNode = nextNode;
+ return true;
+
+ }
+ else
+ {
+ return false;
+ }
+
+ }
+
+ }
+
+
+ public SubscriptionNodeIterator iterator()
+ {
+ return new SubscriptionNodeIterator(_head);
+ }
+
+
+ public SubscriptionNode getHead()
+ {
+ return _head;
+ }
+
+ public int size()
+ {
+ return _size.get();
+ }
+
+
+
+}
+
+
+
diff --git a/java/broker/src/main/java/org/apache/qpid/server/txn/CleanupMessageOperation.java b/java/broker/src/main/java/org/apache/qpid/server/txn/CleanupMessageOperation.java
deleted file mode 100644
index 988f589339..0000000000
--- a/java/broker/src/main/java/org/apache/qpid/server/txn/CleanupMessageOperation.java
+++ /dev/null
@@ -1,77 +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.txn;
-
-import org.apache.log4j.Logger;
-
-import org.apache.qpid.AMQException;
-import org.apache.qpid.server.RequiredDeliveryException;
-import org.apache.qpid.server.queue.AMQMessage;
-import org.apache.qpid.server.queue.NoConsumersException;
-import org.apache.qpid.server.store.StoreContext;
-
-import java.util.List;
-
-/**
- * @author Apache Software Foundation
- */
-public class CleanupMessageOperation implements TxnOp
-{
- private static final Logger _log = Logger.getLogger(CleanupMessageOperation.class);
-
- private final AMQMessage _msg;
-
- private final List<RequiredDeliveryException> _returns;
-
- public CleanupMessageOperation(AMQMessage msg, List<RequiredDeliveryException> returns)
- {
- _msg = msg;
- _returns = returns;
- }
-
- public void prepare(StoreContext context) throws AMQException
- { }
-
- public void undoPrepare()
- {
- // don't need to do anything here, if the store's txn failed
- // when processing prepare then the message was not stored
- // or enqueued on any queues and can be discarded
- }
-
- public void commit(StoreContext context)
- {
- // No-op can't be done here has this is before the message has been attempted to be delivered.
- /*try
- {
- _msg.checkDeliveredToConsumer();
- }
- catch (NoConsumersException e)
- {
- _returns.add(e);
- }*/
- }
-
- public void rollback(StoreContext context)
- {
- // NO OP
- }
-}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/txn/LocalTransactionalContext.java b/java/broker/src/main/java/org/apache/qpid/server/txn/LocalTransactionalContext.java
index b12afd9a41..4234820480 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/txn/LocalTransactionalContext.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/txn/LocalTransactionalContext.java
@@ -24,18 +24,16 @@ import org.apache.log4j.Logger;
import org.apache.qpid.AMQException;
import org.apache.qpid.server.RequiredDeliveryException;
+import org.apache.qpid.server.AMQChannel;
import org.apache.qpid.server.ack.TxAck;
import org.apache.qpid.server.ack.UnacknowledgedMessageMap;
import org.apache.qpid.server.protocol.AMQProtocolSession;
-import org.apache.qpid.server.queue.AMQMessage;
-import org.apache.qpid.server.queue.AMQQueue;
-import org.apache.qpid.server.queue.NoConsumersException;
-import org.apache.qpid.server.queue.QueueEntry;
+import org.apache.qpid.server.queue.*;
import org.apache.qpid.server.store.MessageStore;
import org.apache.qpid.server.store.StoreContext;
-import java.util.LinkedList;
import java.util.List;
+import java.util.ArrayList;
/** A transactional context that only supports local transactions. */
public class LocalTransactionalContext implements TransactionalContext
@@ -44,7 +42,7 @@ public class LocalTransactionalContext implements TransactionalContext
private final TxnBuffer _txnBuffer = new TxnBuffer();
- private final List<DeliveryDetails> _postCommitDeliveryList = new LinkedList<DeliveryDetails>();
+ private final List<DeliveryAction> _postCommitDeliveryList = new ArrayList<DeliveryAction>();
/**
* We keep hold of the ack operation so that we can consolidate acks, i.e. multiple acks within a txn are
@@ -52,81 +50,112 @@ public class LocalTransactionalContext implements TransactionalContext
*/
private TxAck _ackOp;
- private List<RequiredDeliveryException> _returnMessages;
-
- private final MessageStore _messageStore;
-
- private final StoreContext _storeContext;
-
private boolean _inTran = false;
/** Are there messages to deliver. NOT Has the message been delivered */
private boolean _messageDelivered = false;
+ private final AMQChannel _channel;
- private static class DeliveryDetails
+
+ private abstract class DeliveryAction
{
- public QueueEntry entry;
- private boolean deliverFirst;
+ abstract public void process() throws AMQException;
- public DeliveryDetails(QueueEntry entry, boolean deliverFirst)
+ }
+
+ private class RequeueAction extends DeliveryAction
+ {
+ public QueueEntry entry;
+
+ public RequeueAction(QueueEntry entry)
{
this.entry = entry;
- this.deliverFirst = deliverFirst;
+ }
+
+ public void process() throws AMQException
+ {
+ entry.requeue(getStoreContext());
}
}
- public LocalTransactionalContext(MessageStore messageStore, StoreContext storeContext,
- List<RequiredDeliveryException> returnMessages)
+ private class PublishAction extends DeliveryAction
{
- _messageStore = messageStore;
- _storeContext = storeContext;
- _returnMessages = returnMessages;
- // _txnBuffer.enlist(new StoreMessageOperation(messageStore));
+ private final AMQQueue _queue;
+ private final AMQMessage _message;
+
+ public PublishAction(final AMQQueue queue, final AMQMessage message)
+ {
+ _queue = queue;
+ _message = message;
+ }
+
+ public void process() throws AMQException
+ {
+
+ QueueEntry entry = _queue.enqueue(getStoreContext(),_message);
+
+ if(entry.immediateAndNotDelivered())
+ {
+ getReturnMessages().add(new NoConsumersException(_message));
+ }
+ }
+ }
+
+ public LocalTransactionalContext(final AMQChannel channel)
+ {
+ _channel = channel;
}
public StoreContext getStoreContext()
{
- return _storeContext;
+ return _channel.getStoreContext();
+ }
+
+ public List<RequiredDeliveryException> getReturnMessages()
+ {
+ return _channel.getReturnMessages();
+ }
+
+ public MessageStore getMessageStore()
+ {
+ return _channel.getMessageStore();
}
+
public void rollback() throws AMQException
{
- _txnBuffer.rollback(_storeContext);
+ _txnBuffer.rollback(getStoreContext());
// Hack to deal with uncommitted non-transactional writes
- if (_messageStore.inTran(_storeContext))
+ if (getMessageStore().inTran(getStoreContext()))
{
- _messageStore.abortTran(_storeContext);
+ getMessageStore().abortTran(getStoreContext());
_inTran = false;
}
_postCommitDeliveryList.clear();
}
- public void deliver(QueueEntry entry, boolean deliverFirst) throws AMQException
+ public void deliver(final AMQQueue queue, AMQMessage message) throws AMQException
{
// A publication will result in the enlisting of several
// TxnOps. The first is an op that will store the message.
// Following that (and ordering is important), an op will
// be added for every queue onto which the message is
- // enqueued. Finally a cleanup op will be added to decrement
- // the reference associated with the routing.
- // message.incrementReference();
- _postCommitDeliveryList.add(new DeliveryDetails(entry, deliverFirst));
+ // enqueued.
+ _postCommitDeliveryList.add(new PublishAction(queue, message));
_messageDelivered = true;
- _txnBuffer.enlist(new CleanupMessageOperation(entry.getMessage(), _returnMessages));
- /*_txnBuffer.enlist(new DeliverMessageOperation(message, queue));
- if (_log.isDebugEnabled())
- {
- _log.debug("Incrementing ref count on message and enlisting cleanup operation - id " +
- message.getMessageId());
- }
- message.incrementReference();
+
+ }
+
+ public void requeue(QueueEntry entry) throws AMQException
+ {
+ _postCommitDeliveryList.add(new RequeueAction(entry));
_messageDelivered = true;
- */
}
+
private void checkAck(long deliveryTag, UnacknowledgedMessageMap unacknowledgedMessageMap) throws AMQException
{
if (!unacknowledgedMessageMap.contains(deliveryTag))
@@ -184,7 +213,7 @@ public class LocalTransactionalContext implements TransactionalContext
_log.debug("Starting transaction on message store: " + this);
}
- _messageStore.beginTran(_storeContext);
+ getMessageStore().beginTran(getStoreContext());
_inTran = true;
}
}
@@ -207,22 +236,22 @@ public class LocalTransactionalContext implements TransactionalContext
if (_messageDelivered && _inTran)
{
- _txnBuffer.enlist(new StoreMessageOperation(_messageStore));
+ _txnBuffer.enlist(new StoreMessageOperation(getMessageStore()));
}
// fixme fail commit here ... QPID-440
try
{
- _txnBuffer.commit(_storeContext);
+ _txnBuffer.commit(getStoreContext());
}
finally
{
_messageDelivered = false;
- _inTran = _messageStore.inTran(_storeContext);
+ _inTran = getMessageStore().inTran(getStoreContext());
}
try
{
- postCommitDelivery(_returnMessages);
+ postCommitDelivery();
}
catch (AMQException e)
{
@@ -231,7 +260,7 @@ public class LocalTransactionalContext implements TransactionalContext
}
}
- private void postCommitDelivery(List<RequiredDeliveryException> returnMessages) throws AMQException
+ private void postCommitDelivery() throws AMQException
{
if (_log.isDebugEnabled())
{
@@ -240,18 +269,9 @@ public class LocalTransactionalContext implements TransactionalContext
try
{
- for (DeliveryDetails dd : _postCommitDeliveryList)
+ for (DeliveryAction dd : _postCommitDeliveryList)
{
- dd.entry.process(_storeContext, dd.deliverFirst);
-
- try
- {
- dd.entry.checkDeliveredToConsumer();
- }
- catch (NoConsumersException nce)
- {
- returnMessages.add(nce);
- }
+ dd.process();
}
}
finally
diff --git a/java/broker/src/main/java/org/apache/qpid/server/txn/NonTransactionalContext.java b/java/broker/src/main/java/org/apache/qpid/server/txn/NonTransactionalContext.java
index 1e4b69c935..38cf14d772 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/txn/NonTransactionalContext.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/txn/NonTransactionalContext.java
@@ -22,19 +22,14 @@ package org.apache.qpid.server.txn;
import java.util.LinkedList;
import java.util.List;
-import java.util.Set;
import org.apache.log4j.Logger;
import org.apache.qpid.AMQException;
import org.apache.qpid.server.AMQChannel;
import org.apache.qpid.server.RequiredDeliveryException;
-import org.apache.qpid.server.ack.UnacknowledgedMessage;
import org.apache.qpid.server.ack.UnacknowledgedMessageMap;
import org.apache.qpid.server.protocol.AMQProtocolSession;
-import org.apache.qpid.server.queue.AMQMessage;
-import org.apache.qpid.server.queue.AMQQueue;
-import org.apache.qpid.server.queue.NoConsumersException;
-import org.apache.qpid.server.queue.QueueEntry;
+import org.apache.qpid.server.queue.*;
import org.apache.qpid.server.store.MessageStore;
import org.apache.qpid.server.store.StoreContext;
@@ -49,7 +44,7 @@ public class NonTransactionalContext implements TransactionalContext
/** Where to put undeliverable messages */
private final List<RequiredDeliveryException> _returnMessages;
- private final Set<Long> _browsedAcks;
+
private final MessageStore _messageStore;
@@ -59,13 +54,13 @@ public class NonTransactionalContext implements TransactionalContext
private boolean _inTran;
public NonTransactionalContext(MessageStore messageStore, StoreContext storeContext, AMQChannel channel,
- List<RequiredDeliveryException> returnMessages, Set<Long> browsedAcks)
+ List<RequiredDeliveryException> returnMessages)
{
_channel = channel;
_storeContext = storeContext;
_returnMessages = returnMessages;
_messageStore = messageStore;
- _browsedAcks = browsedAcks;
+
}
@@ -93,19 +88,22 @@ public class NonTransactionalContext implements TransactionalContext
// Does not apply to this context
}
- public void deliver(QueueEntry entry, boolean deliverFirst) throws AMQException
+ public void deliver(final AMQQueue queue, AMQMessage message) throws AMQException
{
- try
- {
- entry.process(_storeContext, deliverFirst);
- //following check implements the functionality
- //required by the 'immediate' flag:
- entry.checkDeliveredToConsumer();
- }
- catch (NoConsumersException e)
+ QueueEntry entry = queue.enqueue(_storeContext, message);
+
+ //following check implements the functionality
+ //required by the 'immediate' flag:
+ if(entry.immediateAndNotDelivered())
{
- _returnMessages.add(e);
+ _returnMessages.add(new NoConsumersException(entry.getMessage()));
}
+
+ }
+
+ public void requeue(QueueEntry entry) throws AMQException
+ {
+ entry.requeue(_storeContext);
}
public void acknowledgeMessage(final long deliveryTag, long lastDeliveryTag,
@@ -123,22 +121,17 @@ public class NonTransactionalContext implements TransactionalContext
unacknowledgedMessageMap.size());
unacknowledgedMessageMap.visit(new UnacknowledgedMessageMap.Visitor()
{
- public boolean callback(UnacknowledgedMessage message) throws AMQException
+ public boolean callback(final long deliveryTag, QueueEntry message) throws AMQException
{
- if (!_browsedAcks.contains(deliveryTag))
- {
- if (_log.isDebugEnabled())
- {
- _log.debug("Discarding message: " + message.getMessage().getMessageId());
- }
-
- //Message has been ack so discard it. This will dequeue and decrement the reference.
- message.discard(_storeContext);
- }
- else
+ if (_log.isDebugEnabled())
{
- _browsedAcks.remove(deliveryTag);
+ _log.debug("Discarding message: " + message.getMessage().getMessageId());
}
+
+ message.restoreCredit();
+ //Message has been ack so discard it. This will dequeue and decrement the reference.
+ message.discard(_storeContext);
+
return false;
}
@@ -155,30 +148,24 @@ public class NonTransactionalContext implements TransactionalContext
throw new AMQException("Multiple ack on delivery tag " + deliveryTag + " not known for channel");
}
- LinkedList<UnacknowledgedMessage> acked = new LinkedList<UnacknowledgedMessage>();
+ LinkedList<QueueEntry> acked = new LinkedList<QueueEntry>();
unacknowledgedMessageMap.drainTo(acked, deliveryTag);
- for (UnacknowledgedMessage msg : acked)
+ for (QueueEntry msg : acked)
{
- if (!_browsedAcks.contains(deliveryTag))
- {
if (_log.isDebugEnabled())
{
_log.debug("Discarding message: " + msg.getMessage().getMessageId());
}
+
//Message has been ack so discard it. This will dequeue and decrement the reference.
msg.discard(_storeContext);
- }
- else
- {
- _browsedAcks.remove(deliveryTag);
- }
}
}
}
else
{
- UnacknowledgedMessage msg;
+ QueueEntry msg;
msg = unacknowledgedMessageMap.remove(deliveryTag);
if (msg == null)
@@ -189,21 +176,14 @@ public class NonTransactionalContext implements TransactionalContext
_channel.getChannelId());
}
- if (!_browsedAcks.contains(deliveryTag))
- {
- if (_log.isDebugEnabled())
- {
- _log.debug("Discarding message: " + msg.getMessage().getMessageId());
- }
-
- //Message has been ack so discard it. This will dequeue and decrement the reference.
- msg.discard(_storeContext);
- }
- else
+ if (_log.isDebugEnabled())
{
- _browsedAcks.remove(deliveryTag);
+ _log.debug("Discarding message: " + msg.getMessage().getMessageId());
}
+ //Message has been ack so discard it. This will dequeue and decrement the reference.
+ msg.discard(_storeContext);
+
if (_log.isDebugEnabled())
{
_log.debug("Received non-multiple ack for messaging with delivery tag " + deliveryTag + " msg id " +
@@ -223,6 +203,6 @@ public class NonTransactionalContext implements TransactionalContext
public void messageProcessed(AMQProtocolSession protocolSession) throws AMQException
{
- _channel.processReturns(protocolSession);
+ _channel.processReturns();
}
}
diff --git a/java/broker/src/main/java/org/apache/qpid/server/txn/TransactionalContext.java b/java/broker/src/main/java/org/apache/qpid/server/txn/TransactionalContext.java
index 6016ecc1a5..647ba66fb4 100644
--- a/java/broker/src/main/java/org/apache/qpid/server/txn/TransactionalContext.java
+++ b/java/broker/src/main/java/org/apache/qpid/server/txn/TransactionalContext.java
@@ -25,6 +25,7 @@ import org.apache.qpid.server.ack.UnacknowledgedMessageMap;
import org.apache.qpid.server.protocol.AMQProtocolSession;
import org.apache.qpid.server.queue.AMQMessage;
import org.apache.qpid.server.queue.QueueEntry;
+import org.apache.qpid.server.queue.AMQQueue;
import org.apache.qpid.server.store.StoreContext;
/**
@@ -106,18 +107,26 @@ public interface TransactionalContext
void rollback() throws AMQException;
/**
- * Delivers the specified message to the specified queue. A 'deliverFirst' flag may be set if the message is a
- * redelivery, and should be placed on the front of the queue.
+ * Delivers the specified message to the specified queue.
*
* <p/>This is an 'enqueue' operation.
*
- * @param entry The message to deliver, and the queue to deliver to.
- * @param deliverFirst <tt>true</tt> to place the message on the front of the queue for redelivery, <tt>false</tt>
- * for normal FIFO message ordering.
- *
+ * @param queue
+ * @param message The message to deliver
* @throws AMQException If the message cannot be delivered for any reason.
*/
- void deliver(QueueEntry entry, boolean deliverFirst) throws AMQException;
+ void deliver(final AMQQueue queue, AMQMessage message) throws AMQException;
+
+ /**
+ * Requeues the specified message entry (message queue pair)
+ *
+ *
+ * @param queueEntry The message,queue pair
+ *
+ * @throws AMQException If the message cannot be delivered for any reason.
+ */
+ void requeue(QueueEntry queueEntry) throws AMQException;
+
/**
* Acknowledges a message or many messages as delivered. All messages up to a specified one, may be acknowledged by
diff --git a/java/broker/src/main/java/org/apache/qpid/tools/messagestore/MessageStoreTool.java b/java/broker/src/main/java/org/apache/qpid/tools/messagestore/MessageStoreTool.java
index edc900f401..faa7b85d58 100644
--- a/java/broker/src/main/java/org/apache/qpid/tools/messagestore/MessageStoreTool.java
+++ b/java/broker/src/main/java/org/apache/qpid/tools/messagestore/MessageStoreTool.java
@@ -25,11 +25,11 @@ import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.qpid.configuration.Configuration;
import org.apache.qpid.server.exchange.Exchange;
-import org.apache.qpid.server.queue.AMQQueue;
import org.apache.qpid.server.registry.ApplicationRegistry;
import org.apache.qpid.server.registry.ConfigurationFileApplicationRegistry;
import org.apache.qpid.server.store.MemoryMessageStore;
import org.apache.qpid.server.virtualhost.VirtualHost;
+import org.apache.qpid.server.queue.AMQQueue;
import org.apache.qpid.tools.messagestore.commands.Clear;
import org.apache.qpid.tools.messagestore.commands.Command;
import org.apache.qpid.tools.messagestore.commands.Copy;
diff --git a/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Copy.java b/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Copy.java
index a5b3a87616..0869d9a497 100644
--- a/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Copy.java
+++ b/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Copy.java
@@ -20,8 +20,8 @@
*/
package org.apache.qpid.tools.messagestore.commands;
-import org.apache.qpid.server.queue.AMQQueue;
import org.apache.qpid.tools.messagestore.MessageStoreTool;
+import org.apache.qpid.server.queue.AMQQueue;
public class Copy extends Move
{
diff --git a/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Dump.java b/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Dump.java
index 218d5f04ed..731f6140f9 100644
--- a/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Dump.java
+++ b/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Dump.java
@@ -24,6 +24,7 @@ import org.apache.commons.codec.binary.Hex;
import org.apache.mina.common.ByteBuffer;
import org.apache.qpid.framing.abstraction.ContentChunk;
import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.queue.QueueEntryImpl;
import org.apache.qpid.server.queue.QueueEntry;
import org.apache.qpid.tools.messagestore.MessageStoreTool;
import org.apache.qpid.tools.utils.Console;
@@ -255,7 +256,7 @@ public class Dump extends Show
String title, boolean routing, boolean headers, boolean messageHeaders)
{
List<QueueEntry> single = new LinkedList<QueueEntry>();
- single.add(new QueueEntry(null,msg));
+ single.add(new QueueEntryImpl(null,msg, Long.MIN_VALUE));
List<List> routingData = super.createMessageData(null, single, headers, routing, messageHeaders);
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 df8b59ec19..e6de3ab560 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,6 +22,7 @@ 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/main/java/org/apache/qpid/tools/messagestore/commands/Move.java b/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Move.java
index 7e21253fab..a8dd58ca83 100644
--- a/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Move.java
+++ b/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Move.java
@@ -21,7 +21,7 @@
package org.apache.qpid.tools.messagestore.commands;
import org.apache.qpid.framing.AMQShortString;
-import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.queue.QueueEntryImpl;
import org.apache.qpid.server.queue.AMQQueue;
import org.apache.qpid.server.queue.QueueEntry;
import org.apache.qpid.server.store.StoreContext;
diff --git a/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Purge.java b/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Purge.java
index f187e26593..5e99997863 100644
--- a/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Purge.java
+++ b/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Purge.java
@@ -20,9 +20,8 @@
*/
package org.apache.qpid.tools.messagestore.commands;
-import org.apache.qpid.server.queue.AMQQueue;
-import org.apache.qpid.server.store.StoreContext;
import org.apache.qpid.tools.messagestore.MessageStoreTool;
+import org.apache.qpid.server.queue.AMQQueue;
public class Purge extends Move
{
diff --git a/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Select.java b/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Select.java
index fd7d4c3f13..ff59568374 100644
--- a/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Select.java
+++ b/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Select.java
@@ -22,9 +22,9 @@ 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.AMQQueue;
import org.apache.qpid.server.registry.ApplicationRegistry;
import org.apache.qpid.server.virtualhost.VirtualHost;
+import org.apache.qpid.server.queue.AMQQueue;
import org.apache.qpid.tools.messagestore.MessageStoreTool;
import java.util.LinkedList;
diff --git a/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Show.java b/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Show.java
index a6dccf0f36..2fa017fc64 100644
--- a/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Show.java
+++ b/java/broker/src/main/java/org/apache/qpid/tools/messagestore/commands/Show.java
@@ -26,6 +26,7 @@ import org.apache.qpid.framing.BasicContentHeaderProperties;
import org.apache.qpid.framing.FieldTable;
import org.apache.qpid.framing.abstraction.MessagePublishInfo;
import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.queue.QueueEntryImpl;
import org.apache.qpid.server.queue.AMQQueue;
import org.apache.qpid.server.queue.QueueEntry;
import org.apache.qpid.tools.messagestore.MessageStoreTool;
@@ -33,7 +34,6 @@ import org.apache.qpid.tools.utils.Console;
import java.util.LinkedList;
import java.util.List;
-import java.util.StringTokenizer;
public class Show extends AbstractCommand
{
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 7e2d56b460..22b58b3aa4 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
@@ -22,9 +22,7 @@ package org.apache.qpid.server.exchange;
import junit.framework.TestCase;
import junit.framework.Assert;
-import org.apache.qpid.server.queue.AMQQueue;
-import org.apache.qpid.server.queue.AMQMessage;
-import org.apache.qpid.server.queue.MessageHandleFactory;
+import org.apache.qpid.server.queue.*;
import org.apache.qpid.server.virtualhost.VirtualHost;
import org.apache.qpid.server.registry.ApplicationRegistry;
import org.apache.qpid.server.txn.NonTransactionalContext;
@@ -33,69 +31,62 @@ import org.apache.qpid.server.store.MessageStore;
import org.apache.qpid.server.store.MemoryMessageStore;
import org.apache.qpid.server.store.StoreContext;
import org.apache.qpid.server.RequiredDeliveryException;
+import org.apache.qpid.server.protocol.TestMinaProtocolSession;
import org.apache.qpid.AMQException;
import org.apache.qpid.framing.AMQShortString;
import org.apache.qpid.framing.ContentHeaderBody;
import org.apache.qpid.framing.abstraction.MessagePublishInfo;
-import java.util.HashSet;
-import java.util.List;
import java.util.LinkedList;
public class DestWildExchangeTest extends TestCase
{
- DestWildExchange _exchange;
+ TopicExchange _exchange;
VirtualHost _vhost;
MessageStore _store;
StoreContext _context;
+ TestMinaProtocolSession _protocolSession;
+
public void setUp() throws AMQException
{
- _exchange = new DestWildExchange();
+ _exchange = new TopicExchange();
_vhost = ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHosts().iterator().next();
_store = new MemoryMessageStore();
_context = new StoreContext();
+ _protocolSession = new TestMinaProtocolSession();
}
public void testNoRoute() throws AMQException
{
- AMQQueue queue = new AMQQueue(new AMQShortString("a*#b"), false, null, false, _vhost);
+ AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a*#b"), false, null, false, _vhost);
_exchange.registerQueue(new AMQShortString("a.*.#.b"), queue, null);
MessagePublishInfo info = new PublishInfo(new AMQShortString("a.b"));
- AMQMessage message = new AMQMessage(0L, info, null);
+ IncomingMessage message = new IncomingMessage(0L, info, null, _protocolSession);
- try
- {
- _exchange.route(message);
- fail("Message has no route and shouldn't be routed");
- }
- catch (NoRouteException nre)
- {
- //normal
- }
+ _exchange.route(message);
Assert.assertEquals(0, queue.getMessageCount());
}
public void testDirectMatch() throws AMQException
{
- AMQQueue queue = new AMQQueue(new AMQShortString("ab"), false, null, false, _vhost);
+ AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("ab"), false, null, false, _vhost);
_exchange.registerQueue(new AMQShortString("a.b"), queue, null);
- AMQMessage message = createMessage("a.b");
+ IncomingMessage message = createMessage("a.b");
try
{
- _exchange.route(message);
- message.routingComplete(_store, _context, new MessageHandleFactory());
+ routeMessage(message);
}
catch (AMQException nre)
{
@@ -104,7 +95,7 @@ public class DestWildExchangeTest extends TestCase
Assert.assertEquals(1, queue.getMessageCount());
- Assert.assertEquals("Wrong message recevied", message, queue.getMessagesOnTheQueue().get(0).getMessage());
+ Assert.assertEquals("Wrong message recevied", (Object) message.getMessageId(), queue.getMessagesOnTheQueue().get(0).getMessage().getMessageId());
queue.deleteMessageFromTop(_context);
Assert.assertEquals(0, queue.getMessageCount());
@@ -114,8 +105,7 @@ public class DestWildExchangeTest extends TestCase
try
{
- _exchange.route(message);
- message.routingComplete(_store, _context, new MessageHandleFactory());
+ routeMessage(message);
fail("Message has no route and should fail to be routed");
}
catch (AMQException nre)
@@ -128,16 +118,15 @@ public class DestWildExchangeTest extends TestCase
public void testStarMatch() throws AMQException
{
- AMQQueue queue = new AMQQueue(new AMQShortString("a*"), false, null, false, _vhost);
+ AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a*"), false, null, false, _vhost);
_exchange.registerQueue(new AMQShortString("a.*"), queue, null);
- AMQMessage message = createMessage("a.b");
+ IncomingMessage message = createMessage("a.b");
try
{
- _exchange.route(message);
- message.routingComplete(_store, _context, new MessageHandleFactory());
+ routeMessage(message);
}
catch (AMQException nre)
{
@@ -146,7 +135,7 @@ public class DestWildExchangeTest extends TestCase
Assert.assertEquals(1, queue.getMessageCount());
- Assert.assertEquals("Wrong message recevied", message, queue.getMessagesOnTheQueue().get(0).getMessage());
+ Assert.assertEquals("Wrong message recevied", (Object) message.getMessageId(), queue.getMessagesOnTheQueue().get(0).getMessage().getMessageId());
queue.deleteMessageFromTop(_context);
Assert.assertEquals(0, queue.getMessageCount());
@@ -156,8 +145,7 @@ public class DestWildExchangeTest extends TestCase
try
{
- _exchange.route(message);
- message.routingComplete(_store, _context, new MessageHandleFactory());
+ routeMessage(message);
}
catch (AMQException nre)
{
@@ -166,7 +154,7 @@ public class DestWildExchangeTest extends TestCase
Assert.assertEquals(1, queue.getMessageCount());
- Assert.assertEquals("Wrong message recevied", message, queue.getMessagesOnTheQueue().get(0).getMessage());
+ Assert.assertEquals("Wrong message recevied", (Object) message.getMessageId(), queue.getMessagesOnTheQueue().get(0).getMessage().getMessageId());
queue.deleteMessageFromTop(_context);
Assert.assertEquals(0, queue.getMessageCount());
@@ -176,8 +164,7 @@ public class DestWildExchangeTest extends TestCase
try
{
- _exchange.route(message);
- message.routingComplete(_store, _context, new MessageHandleFactory());
+ routeMessage(message);
fail("Message has no route and should fail to be routed");
}
catch (AMQException nre)
@@ -189,16 +176,15 @@ public class DestWildExchangeTest extends TestCase
public void testHashMatch() throws AMQException
{
- AMQQueue queue = new AMQQueue(new AMQShortString("a#"), false, null, false, _vhost);
+ AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a#"), false, null, false, _vhost);
_exchange.registerQueue(new AMQShortString("a.#"), queue, null);
- AMQMessage message = createMessage("a.b.c");
+ IncomingMessage message = createMessage("a.b.c");
try
{
- _exchange.route(message);
- message.routingComplete(_store, _context, new MessageHandleFactory());
+ routeMessage(message);
}
catch (AMQException nre)
{
@@ -207,7 +193,7 @@ public class DestWildExchangeTest extends TestCase
Assert.assertEquals(1, queue.getMessageCount());
- Assert.assertEquals("Wrong message recevied", message, queue.getMessagesOnTheQueue().get(0).getMessage());
+ Assert.assertEquals("Wrong message recevied", (Object) message.getMessageId(), queue.getMessagesOnTheQueue().get(0).getMessage().getMessageId());
queue.deleteMessageFromTop(_context);
Assert.assertEquals(0, queue.getMessageCount());
@@ -217,8 +203,7 @@ public class DestWildExchangeTest extends TestCase
try
{
- _exchange.route(message);
- message.routingComplete(_store, _context, new MessageHandleFactory());
+ routeMessage(message);
}
catch (AMQException nre)
{
@@ -227,7 +212,7 @@ public class DestWildExchangeTest extends TestCase
Assert.assertEquals(1, queue.getMessageCount());
- Assert.assertEquals("Wrong message recevied", message, queue.getMessagesOnTheQueue().get(0).getMessage());
+ Assert.assertEquals("Wrong message recevied", (Object) message.getMessageId(), queue.getMessagesOnTheQueue().get(0).getMessage().getMessageId());
queue.deleteMessageFromTop(_context);
Assert.assertEquals(0, queue.getMessageCount());
@@ -237,8 +222,7 @@ public class DestWildExchangeTest extends TestCase
try
{
- _exchange.route(message);
- message.routingComplete(_store, _context, new MessageHandleFactory());
+ routeMessage(message);
}
catch (AMQException nre)
{
@@ -247,7 +231,7 @@ public class DestWildExchangeTest extends TestCase
Assert.assertEquals(1, queue.getMessageCount());
- Assert.assertEquals("Wrong message recevied", message, queue.getMessagesOnTheQueue().get(0).getMessage());
+ Assert.assertEquals("Wrong message recevied", (Object) message.getMessageId(), queue.getMessagesOnTheQueue().get(0).getMessage().getMessageId());
queue.deleteMessageFromTop(_context);
Assert.assertEquals(0, queue.getMessageCount());
@@ -256,8 +240,7 @@ public class DestWildExchangeTest extends TestCase
try
{
- _exchange.route(message);
- message.routingComplete(_store, _context, new MessageHandleFactory());
+ routeMessage(message);
}
catch (AMQException nre)
{
@@ -266,7 +249,7 @@ public class DestWildExchangeTest extends TestCase
Assert.assertEquals(1, queue.getMessageCount());
- Assert.assertEquals("Wrong message recevied", message, queue.getMessagesOnTheQueue().get(0).getMessage());
+ Assert.assertEquals("Wrong message recevied", (Object) message.getMessageId(), queue.getMessagesOnTheQueue().get(0).getMessage().getMessageId());
queue.deleteMessageFromTop(_context);
Assert.assertEquals(0, queue.getMessageCount());
@@ -276,8 +259,7 @@ public class DestWildExchangeTest extends TestCase
try
{
- _exchange.route(message);
- message.routingComplete(_store, _context, new MessageHandleFactory());
+ routeMessage(message);
fail("Message has no route and should fail to be routed");
}
catch (AMQException nre)
@@ -290,16 +272,15 @@ public class DestWildExchangeTest extends TestCase
public void testMidHash() throws AMQException
{
- AMQQueue queue = new AMQQueue(new AMQShortString("a"), false, null, false, _vhost);
+ AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a"), false, null, false, _vhost);
_exchange.registerQueue(new AMQShortString("a.*.#.b"), queue, null);
- AMQMessage message = createMessage("a.c.d.b");
+ IncomingMessage message = createMessage("a.c.d.b");
try
{
- _exchange.route(message);
- message.routingComplete(_store, _context, new MessageHandleFactory());
+ routeMessage(message);
}
catch (AMQException nre)
{
@@ -308,7 +289,7 @@ public class DestWildExchangeTest extends TestCase
Assert.assertEquals(1, queue.getMessageCount());
- Assert.assertEquals("Wrong message recevied", message, queue.getMessagesOnTheQueue().get(0).getMessage());
+ Assert.assertEquals("Wrong message recevied", (Object) message.getMessageId(), queue.getMessagesOnTheQueue().get(0).getMessage().getMessageId());
queue.deleteMessageFromTop(_context);
Assert.assertEquals(0, queue.getMessageCount());
@@ -317,8 +298,7 @@ public class DestWildExchangeTest extends TestCase
try
{
- _exchange.route(message);
- message.routingComplete(_store, _context, new MessageHandleFactory());
+ routeMessage(message);
}
catch (AMQException nre)
{
@@ -327,7 +307,7 @@ public class DestWildExchangeTest extends TestCase
Assert.assertEquals(1, queue.getMessageCount());
- Assert.assertEquals("Wrong message recevied", message, queue.getMessagesOnTheQueue().get(0).getMessage());
+ Assert.assertEquals("Wrong message recevied", (Object) message.getMessageId(), queue.getMessagesOnTheQueue().get(0).getMessage().getMessageId());
queue.deleteMessageFromTop(_context);
Assert.assertEquals(0, queue.getMessageCount());
@@ -336,16 +316,15 @@ public class DestWildExchangeTest extends TestCase
public void testMatchafterHash() throws AMQException
{
- AMQQueue queue = new AMQQueue(new AMQShortString("a#"), false, null, false, _vhost);
+ AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a#"), false, null, false, _vhost);
_exchange.registerQueue(new AMQShortString("a.*.#.b.c"), queue, null);
- AMQMessage message = createMessage("a.c.b.b");
+ IncomingMessage message = createMessage("a.c.b.b");
try
{
- _exchange.route(message);
- message.routingComplete(_store, _context, new MessageHandleFactory());
+ routeMessage(message);
fail("Message has route and should not be routed");
}
catch (AMQException nre)
@@ -359,8 +338,7 @@ public class DestWildExchangeTest extends TestCase
try
{
- _exchange.route(message);
- message.routingComplete(_store, _context, new MessageHandleFactory());
+ routeMessage(message);
}
catch (AMQException nre)
{
@@ -369,7 +347,7 @@ public class DestWildExchangeTest extends TestCase
Assert.assertEquals(1, queue.getMessageCount());
- Assert.assertEquals("Wrong message recevied", message, queue.getMessagesOnTheQueue().get(0).getMessage());
+ Assert.assertEquals("Wrong message recevied", (Object) message.getMessageId(), queue.getMessagesOnTheQueue().get(0).getMessage().getMessageId());
queue.deleteMessageFromTop(_context);
Assert.assertEquals(0, queue.getMessageCount());
@@ -378,8 +356,7 @@ public class DestWildExchangeTest extends TestCase
try
{
- _exchange.route(message);
- message.routingComplete(_store, _context, new MessageHandleFactory());
+ routeMessage(message);
fail("Message has route and should not be routed");
}
catch (AMQException nre)
@@ -392,8 +369,7 @@ public class DestWildExchangeTest extends TestCase
try
{
- _exchange.route(message);
- message.routingComplete(_store, _context, new MessageHandleFactory());
+ routeMessage(message);
}
catch (AMQException nre)
{
@@ -403,7 +379,7 @@ public class DestWildExchangeTest extends TestCase
Assert.assertEquals(1, queue.getMessageCount());
- Assert.assertEquals("Wrong message recevied", message, queue.getMessagesOnTheQueue().get(0).getMessage());
+ Assert.assertEquals("Wrong message recevied", (Object) message.getMessageId(), queue.getMessagesOnTheQueue().get(0).getMessage().getMessageId());
queue.deleteMessageFromTop(_context);
Assert.assertEquals(0, queue.getMessageCount());
@@ -413,16 +389,15 @@ public class DestWildExchangeTest extends TestCase
public void testHashAfterHash() throws AMQException
{
- AMQQueue queue = new AMQQueue(new AMQShortString("a#"), false, null, false, _vhost);
+ AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a#"), false, null, false, _vhost);
_exchange.registerQueue(new AMQShortString("a.*.#.b.c.#.d"), queue, null);
- AMQMessage message = createMessage("a.c.b.b.c");
+ IncomingMessage message = createMessage("a.c.b.b.c");
try
{
- _exchange.route(message);
- message.routingComplete(_store, _context, new MessageHandleFactory());
+ routeMessage(message);
fail("Message has route and should not be routed");
}
catch (AMQException nre)
@@ -436,8 +411,7 @@ public class DestWildExchangeTest extends TestCase
try
{
- _exchange.route(message);
- message.routingComplete(_store, _context, new MessageHandleFactory());
+ routeMessage(message);
}
catch (AMQException nre)
{
@@ -446,7 +420,7 @@ public class DestWildExchangeTest extends TestCase
Assert.assertEquals(1, queue.getMessageCount());
- Assert.assertEquals("Wrong message recevied", message, queue.getMessagesOnTheQueue().get(0).getMessage());
+ Assert.assertEquals("Wrong message recevied", (Object) message.getMessageId(), queue.getMessagesOnTheQueue().get(0).getMessage().getMessageId());
queue.deleteMessageFromTop(_context);
Assert.assertEquals(0, queue.getMessageCount());
@@ -455,16 +429,15 @@ public class DestWildExchangeTest extends TestCase
public void testHashHash() throws AMQException
{
- AMQQueue queue = new AMQQueue(new AMQShortString("a#"), false, null, false, _vhost);
+ AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a#"), false, null, false, _vhost);
_exchange.registerQueue(new AMQShortString("a.#.*.#.d"), queue, null);
- AMQMessage message = createMessage("a.c.b.b.c");
+ IncomingMessage message = createMessage("a.c.b.b.c");
try
{
- _exchange.route(message);
- message.routingComplete(_store, _context, new MessageHandleFactory());
+ routeMessage(message);
fail("Message has route and should not be routed");
}
catch (AMQException nre)
@@ -477,8 +450,7 @@ public class DestWildExchangeTest extends TestCase
try
{
- _exchange.route(message);
- message.routingComplete(_store, _context, new MessageHandleFactory());
+ routeMessage(message);
}
catch (AMQException nre)
{
@@ -487,7 +459,7 @@ public class DestWildExchangeTest extends TestCase
Assert.assertEquals(1, queue.getMessageCount());
- Assert.assertEquals("Wrong message recevied", message, queue.getMessagesOnTheQueue().get(0).getMessage());
+ Assert.assertEquals("Wrong message recevied", (Object) message.getMessageId(), queue.getMessagesOnTheQueue().get(0).getMessage().getMessageId());
queue.deleteMessageFromTop(_context);
Assert.assertEquals(0, queue.getMessageCount());
@@ -496,16 +468,15 @@ public class DestWildExchangeTest extends TestCase
public void testSubMatchFails() throws AMQException
{
- AMQQueue queue = new AMQQueue(new AMQShortString("a"), false, null, false, _vhost);
+ AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a"), false, null, false, _vhost);
_exchange.registerQueue(new AMQShortString("a.b.c.d"), queue, null);
- AMQMessage message = createMessage("a.b.c");
+ IncomingMessage message = createMessage("a.b.c");
try
{
- _exchange.route(message);
- message.routingComplete(_store, _context, new MessageHandleFactory());
+ routeMessage(message);
fail("Message has route and should not be routed");
}
catch (AMQException nre)
@@ -516,18 +487,25 @@ public class DestWildExchangeTest extends TestCase
}
+ private void routeMessage(final IncomingMessage message)
+ throws AMQException
+ {
+ _exchange.route(message);
+ message.routingComplete(_store, new MessageHandleFactory());
+ message.deliverToQueues();
+ }
+
public void testMoreRouting() throws AMQException
{
- AMQQueue queue = new AMQQueue(new AMQShortString("a"), false, null, false, _vhost);
+ AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a"), false, null, false, _vhost);
_exchange.registerQueue(new AMQShortString("a.b"), queue, null);
- AMQMessage message = createMessage("a.b.c");
+ IncomingMessage message = createMessage("a.b.c");
try
{
- _exchange.route(message);
- message.routingComplete(_store, _context, new MessageHandleFactory());
+ routeMessage(message);
fail("Message has route and should not be routed");
}
catch (AMQException nre)
@@ -540,16 +518,15 @@ public class DestWildExchangeTest extends TestCase
public void testMoreQueue() throws AMQException
{
- AMQQueue queue = new AMQQueue(new AMQShortString("a"), false, null, false, _vhost);
+ AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("a"), false, null, false, _vhost);
_exchange.registerQueue(new AMQShortString("a.b"), queue, null);
- AMQMessage message = createMessage("a");
+ IncomingMessage message = createMessage("a");
try
{
- _exchange.route(message);
- message.routingComplete(_store, _context, new MessageHandleFactory());
+ routeMessage(message);
fail("Message has route and should not be routed");
}
catch (AMQException nre)
@@ -560,16 +537,17 @@ public class DestWildExchangeTest extends TestCase
}
- private AMQMessage createMessage(String s) throws AMQException
+ private IncomingMessage createMessage(String s) throws AMQException
{
MessagePublishInfo info = new PublishInfo(new AMQShortString(s));
TransactionalContext trancontext = new NonTransactionalContext(_store, _context, null,
- new LinkedList<RequiredDeliveryException>(),
- new HashSet<Long>());
+ new LinkedList<RequiredDeliveryException>()
+ );
+
+ IncomingMessage message = new IncomingMessage(0L, info, trancontext,_protocolSession);
+ message.setContentHeaderBody( new ContentHeaderBody());
- AMQMessage message = new AMQMessage(0L, info, trancontext);
- message.setContentHeaderBody(new ContentHeaderBody());
return message;
}
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 18d8592817..5184bf92e0 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
@@ -21,8 +21,9 @@
package org.apache.qpid.server.exchange;
import junit.framework.TestCase;
-import org.apache.qpid.server.queue.AMQQueue;
import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.queue.AMQQueueFactory;
import org.apache.qpid.server.registry.ApplicationRegistry;
import org.apache.qpid.server.registry.IApplicationRegistry;
import org.apache.qpid.server.management.ManagedObject;
@@ -30,7 +31,6 @@ import org.apache.qpid.server.virtualhost.VirtualHost;
import org.apache.qpid.exchange.ExchangeDefaults;
import org.apache.qpid.framing.AMQShortString;
-import javax.management.openmbean.CompositeData;
import javax.management.openmbean.TabularData;
import java.util.ArrayList;
@@ -50,7 +50,7 @@ public class ExchangeMBeanTest extends TestCase
public void testDirectExchangeMBean() throws Exception
{
- DestNameExchange exchange = new DestNameExchange();
+ DirectExchange exchange = new DirectExchange();
exchange.initialise(_virtualHost, ExchangeDefaults.DIRECT_EXCHANGE_NAME, false, 0, true);
ManagedObject managedObj = exchange.getManagedObject();
ManagedExchange mbean = (ManagedExchange)managedObj;
@@ -77,7 +77,7 @@ public class ExchangeMBeanTest extends TestCase
public void testTopicExchangeMBean() throws Exception
{
- DestWildExchange exchange = new DestWildExchange();
+ TopicExchange exchange = new TopicExchange();
exchange.initialise(_virtualHost,ExchangeDefaults.TOPIC_EXCHANGE_NAME, false, 0, true);
ManagedObject managedObj = exchange.getManagedObject();
ManagedExchange mbean = (ManagedExchange)managedObj;
@@ -132,7 +132,7 @@ public class ExchangeMBeanTest extends TestCase
IApplicationRegistry applicationRegistry = ApplicationRegistry.getInstance();
_virtualHost = applicationRegistry.getVirtualHostRegistry().getVirtualHost("test");
_queueRegistry = _virtualHost.getQueueRegistry();
- _queue = new AMQQueue(new AMQShortString("testQueue"), false, new AMQShortString("ExchangeMBeanTest"), false, _virtualHost);
+ _queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue"), false, new AMQShortString("ExchangeMBeanTest"), false, _virtualHost);
_queueRegistry.registerQueue(_queue);
}
}
diff --git a/java/broker/src/test/java/org/apache/qpid/server/protocol/TestMinaProtocolSession.java b/java/broker/src/test/java/org/apache/qpid/server/protocol/TestMinaProtocolSession.java
index 0c0d8f471e..113944cf7e 100644
--- a/java/broker/src/test/java/org/apache/qpid/server/protocol/TestMinaProtocolSession.java
+++ b/java/broker/src/test/java/org/apache/qpid/server/protocol/TestMinaProtocolSession.java
@@ -30,9 +30,11 @@ public class TestMinaProtocolSession extends AMQMinaProtocolSession
{
public TestMinaProtocolSession() throws AMQException
{
+
super(new TestIoSession(),
ApplicationRegistry.getInstance().getVirtualHostRegistry(),
new AMQCodecFactory(true));
+
}
public ProtocolOutputConverter getProtocolOutputConverter()
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 ed79384d42..fe8e3c8055 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
@@ -32,19 +32,22 @@ import org.apache.qpid.server.txn.TransactionalContext;
import org.apache.qpid.server.txn.NonTransactionalContext;
import org.apache.qpid.server.RequiredDeliveryException;
import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.subscription.Subscription;
+import org.apache.qpid.server.subscription.SubscriptionFactoryImpl;
import org.apache.qpid.server.protocol.TestMinaProtocolSession;
import org.apache.qpid.server.protocol.AMQMinaProtocolSession;
import org.apache.qpid.framing.ContentHeaderBody;
import org.apache.qpid.framing.AMQShortString;
import org.apache.qpid.framing.abstraction.MessagePublishInfo;
+import org.apache.qpid.framing.abstraction.ContentChunk;
+import org.apache.mina.common.ByteBuffer;
import javax.management.Notification;
import java.util.LinkedList;
-import java.util.HashSet;
/** This class tests all the alerts an AMQQueue can throw based on threshold values of different parameters */
public class AMQQueueAlertTest extends TestCase
-{
+{
private final static long MAX_MESSAGE_COUNT = 50;
private final static long MAX_MESSAGE_AGE = 250; // 0.25 sec
private final static long MAX_MESSAGE_SIZE = 2000; // 2 KB
@@ -52,13 +55,14 @@ public class AMQQueueAlertTest extends TestCase
private AMQQueue _queue;
private AMQQueueMBean _queueMBean;
private VirtualHost _virtualHost;
- private AMQMinaProtocolSession protocolSession = null;
+ private AMQMinaProtocolSession _protocolSession;
private MessageStore _messageStore = new MemoryMessageStore();
private StoreContext _storeContext = new StoreContext();
private TransactionalContext _transactionalContext = new NonTransactionalContext(_messageStore, _storeContext,
null,
- new LinkedList<RequiredDeliveryException>(),
- new HashSet<Long>());
+ new LinkedList<RequiredDeliveryException>()
+ );
+ private static final SubscriptionFactoryImpl SUBSCRIPTION_FACTORY = SubscriptionFactoryImpl.INSTANCE;
/**
* Tests if the alert gets thrown when message count increases the threshold limit
@@ -67,7 +71,7 @@ public class AMQQueueAlertTest extends TestCase
*/
public void testMessageCountAlert() throws Exception
{
- _queue = new AMQQueue(new AMQShortString("testQueue1"), false, new AMQShortString("AMQueueAlertTest"),
+ _queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue1"), false, new AMQShortString("AMQueueAlertTest"),
false, _virtualHost);
_queueMBean = (AMQQueueMBean) _queue.getManagedObject();
@@ -90,7 +94,7 @@ public class AMQQueueAlertTest extends TestCase
*/
public void testMessageSizeAlert() throws Exception
{
- _queue = new AMQQueue(new AMQShortString("testQueue2"), false, new AMQShortString("AMQueueAlertTest"),
+ _queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue2"), false, new AMQShortString("AMQueueAlertTest"),
false, _virtualHost);
_queueMBean = (AMQQueueMBean) _queue.getManagedObject();
_queueMBean.setMaximumMessageCount(MAX_MESSAGE_COUNT);
@@ -115,7 +119,7 @@ public class AMQQueueAlertTest extends TestCase
*/
public void testQueueDepthAlertNoSubscriber() throws Exception
{
- _queue = new AMQQueue(new AMQShortString("testQueue3"), false, new AMQShortString("AMQueueAlertTest"),
+ _queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue3"), false, new AMQShortString("AMQueueAlertTest"),
false, _virtualHost);
_queueMBean = (AMQQueueMBean) _queue.getManagedObject();
_queueMBean.setMaximumMessageCount(MAX_MESSAGE_COUNT);
@@ -143,7 +147,7 @@ public class AMQQueueAlertTest extends TestCase
*/
public void testMessageAgeAlert() throws Exception
{
- _queue = new AMQQueue(new AMQShortString("testQueue4"), false, new AMQShortString("AMQueueAlertTest"),
+ _queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue4"), false, new AMQShortString("AMQueueAlertTest"),
false, _virtualHost);
_queueMBean = (AMQQueueMBean) _queue.getManagedObject();
_queueMBean.setMaximumMessageCount(MAX_MESSAGE_COUNT);
@@ -168,18 +172,23 @@ public class AMQQueueAlertTest extends TestCase
This test sends some messages to the queue with subscribers needing message to be acknowledged.
The messages will not be acknowledged and will be required twice. Why we are checking this is because
the bug reported said that the queueDepth keeps increasing when messages are requeued.
+ // TODO - queue depth now includes unacknowledged messages so does not go down when messages are delivered
+
The QueueDepth should decrease when messages are delivered from the queue (QPID-408)
*/
public void testQueueDepthAlertWithSubscribers() throws Exception
{
- protocolSession = new TestMinaProtocolSession();
- AMQChannel channel = new AMQChannel(protocolSession, 2, _messageStore);
- protocolSession.addChannel(channel);
+ _protocolSession = new TestMinaProtocolSession();
+ AMQChannel channel = new AMQChannel(_protocolSession, 2, _messageStore);
+ _protocolSession.addChannel(channel);
// Create queue
_queue = getNewQueue();
- _queue.registerProtocolSession(protocolSession, channel.getChannelId(),
- new AMQShortString("consumer_tag"), true, null, false, false);
+ Subscription subscription =
+ SUBSCRIPTION_FACTORY.createSubscription(channel.getChannelId(), _protocolSession, new AMQShortString("consumer_tag"), true, null, false, channel.getCreditManager());
+
+ _queue.registerSubscription(
+ subscription, false);
_queueMBean = (AMQQueueMBean) _queue.getManagedObject();
_queueMBean.setMaximumMessageCount(9999l); // Set a high value, because this is not being tested
@@ -192,13 +201,13 @@ public class AMQQueueAlertTest extends TestCase
// Check queueDepth. There should be no messages on the queue and as the subscriber is listening
// so there should be no Queue_Deoth alert raised
- assertEquals(new Long(0), new Long(_queueMBean.getQueueDepth()));
+ assertEquals(new Long(totalSize), new Long(_queueMBean.getQueueDepth()));
Notification lastNotification = _queueMBean.getLastNotification();
- assertNull(lastNotification);
+// assertNull(lastNotification);
// Kill the subscriber and check for the queue depth values.
// Messages are unacknowledged, so those should get requeued. All messages should be on the Queue
- _queue.unregisterProtocolSession(protocolSession, channel.getChannelId(), new AMQShortString("consumer_tag"));
+ _queue.unregisterSubscription(subscription);
channel.requeue();
assertEquals(new Long(totalSize), new Long(_queueMBean.getQueueDepth()));
@@ -210,29 +219,32 @@ public class AMQQueueAlertTest extends TestCase
// Connect a consumer again and check QueueDepth values. The queue should get emptied.
// Messages will get delivered but still are unacknowledged.
- _queue.registerProtocolSession(protocolSession, channel.getChannelId(),
- new AMQShortString("consumer_tag"), true, null, false, false);
- _queue.deliverAsync();
- while (_queue.getMessageCount() != 0)
+ Subscription subscription2 =
+ SUBSCRIPTION_FACTORY.createSubscription(channel.getChannelId(), _protocolSession, new AMQShortString("consumer_tag"), true, null, false, channel.getCreditManager());
+
+ _queue.registerSubscription(
+ subscription2, false);
+
+ while (_queue.getUndeliveredMessageCount()!= 0)
{
Thread.sleep(100);
}
- assertEquals(new Long(0), new Long(_queueMBean.getQueueDepth()));
+// assertEquals(new Long(0), new Long(_queueMBean.getQueueDepth()));
// Kill the subscriber again. Now those messages should get requeued again. Check if the queue depth
// value is correct.
- _queue.unregisterProtocolSession(protocolSession, channel.getChannelId(), new AMQShortString("consumer_tag"));
+ _queue.unregisterSubscription(subscription2);
channel.requeue();
assertEquals(new Long(totalSize), new Long(_queueMBean.getQueueDepth()));
- protocolSession.closeSession();
+ _protocolSession.closeSession();
// Check the clear queue
_queueMBean.clearQueue();
assertEquals(new Long(0), new Long(_queueMBean.getQueueDepth()));
}
- protected AMQMessage message(final boolean immediate, long size) throws AMQException
+ protected IncomingMessage message(final boolean immediate, long size) throws AMQException
{
MessagePublishInfo publish = new MessagePublishInfo()
{
@@ -265,9 +277,9 @@ public class AMQQueueAlertTest extends TestCase
ContentHeaderBody contentHeaderBody = new ContentHeaderBody();
contentHeaderBody.bodySize = size; // in bytes
- AMQMessage message = new AMQMessage(_messageStore.getNewMessageId(), publish, _transactionalContext);
+ IncomingMessage message = new IncomingMessage(_messageStore.getNewMessageId(), publish, _transactionalContext, _protocolSession);
message.setContentHeaderBody(contentHeaderBody);
- message.setPublisher(protocolSession);
+
return message;
}
@@ -277,27 +289,49 @@ public class AMQQueueAlertTest extends TestCase
super.setUp();
IApplicationRegistry applicationRegistry = ApplicationRegistry.getInstance();
_virtualHost = applicationRegistry.getVirtualHostRegistry().getVirtualHost("test");
+ _protocolSession = new TestMinaProtocolSession();
+
}
- private void sendMessages(long messageCount, long size) throws AMQException
+ private void sendMessages(long messageCount, final long size) throws AMQException
{
- AMQMessage[] messages = new AMQMessage[(int) messageCount];
+ IncomingMessage[] messages = new IncomingMessage[(int) messageCount];
for (int i = 0; i < messages.length; i++)
{
messages[i] = message(false, size);
messages[i].enqueue(_queue);
- messages[i].routingComplete(_messageStore, _storeContext, new MessageHandleFactory());
+ messages[i].routingComplete(_messageStore, new MessageHandleFactory());
+
}
for (int i = 0; i < messageCount; i++)
{
- _queue.process(_storeContext, new QueueEntry(_queue,messages[i]), false);
+ messages[i].addContentBodyFrame(new ContentChunk(){
+
+ ByteBuffer _data = ByteBuffer.allocate((int)size);
+
+ public int getSize()
+ {
+ return (int) size;
+ }
+
+ public ByteBuffer getData()
+ {
+ return _data;
+ }
+
+ public void reduceToFit()
+ {
+
+ }
+ });
+ messages[i].deliverToQueues();
}
}
private AMQQueue getNewQueue() throws AMQException
{
- return new AMQQueue(new AMQShortString("testQueue" + Math.random()),
+ return AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue" + Math.random()),
false,
new AMQShortString("AMQueueAlertTest"),
false,
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 c02b47e9fd..ef30fb9093 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
@@ -27,8 +27,12 @@ import org.apache.qpid.framing.AMQShortString;
import org.apache.qpid.framing.BasicContentHeaderProperties;
import org.apache.qpid.framing.ContentBody;
import org.apache.qpid.framing.abstraction.MessagePublishInfo;
+import org.apache.qpid.framing.abstraction.ContentChunk;
import org.apache.qpid.server.AMQChannel;
import org.apache.qpid.server.RequiredDeliveryException;
+import org.apache.qpid.server.subscription.Subscription;
+import org.apache.qpid.server.subscription.SubscriptionFactory;
+import org.apache.qpid.server.subscription.SubscriptionFactoryImpl;
import org.apache.qpid.server.protocol.TestMinaProtocolSession;
import org.apache.qpid.server.protocol.AMQProtocolSession;
import org.apache.qpid.server.virtualhost.VirtualHost;
@@ -44,7 +48,6 @@ import org.apache.mina.common.ByteBuffer;
import javax.management.JMException;
import java.util.LinkedList;
-import java.util.HashSet;
/**
* Test class to test AMQQueueMBean attribtues and operations
@@ -59,6 +62,7 @@ public class AMQQueueMBeanTest extends TestCase
private TransactionalContext _transactionalContext;
private VirtualHost _virtualHost;
private AMQProtocolSession _protocolSession;
+ private static final SubscriptionFactoryImpl SUBSCRIPTION_FACTORY = SubscriptionFactoryImpl.INSTANCE;
public void testMessageCountTransient() throws Exception
{
@@ -74,7 +78,7 @@ public class AMQQueueMBeanTest extends TestCase
assertTrue(_queueMBean.getReceivedMessageCount() == messageCount);
_queueMBean.clearQueue();
- assertTrue(_queueMBean.getMessageCount() == 0);
+ assertEquals(0,(int)_queueMBean.getMessageCount());
assertTrue(_queueMBean.getReceivedMessageCount() == messageCount);
//Ensure that the data has been removed from the Store
@@ -117,8 +121,8 @@ public class AMQQueueMBeanTest extends TestCase
public void testConsumerCount() throws AMQException
{
- SubscriptionManager mgr = _queue.getSubscribers();
- assertFalse(mgr.hasActiveSubscribers());
+
+ assertTrue(_queue.getActiveConsumerCount() == 0);
assertTrue(_queueMBean.getActiveConsumerCount() == 0);
@@ -126,18 +130,21 @@ public class AMQQueueMBeanTest extends TestCase
AMQChannel channel = new AMQChannel(protocolSession, 1, _messageStore);
protocolSession.addChannel(channel);
- _queue.registerProtocolSession(protocolSession, 1, new AMQShortString("test"), false, null, false, false);
- assertTrue(_queueMBean.getActiveConsumerCount() == 1);
+ Subscription subscription =
+ SUBSCRIPTION_FACTORY.createSubscription(channel.getChannelId(), protocolSession, new AMQShortString("test"), false, null, false, channel.getCreditManager());
+
+ _queue.registerSubscription(subscription, false);
+ assertEquals(1,(int)_queueMBean.getActiveConsumerCount());
+
- SubscriptionSet _subscribers = (SubscriptionSet) mgr;
- SubscriptionFactory subscriptionFactory = new SubscriptionImpl.Factory();
+ SubscriptionFactory subscriptionFactory = SUBSCRIPTION_FACTORY;
Subscription s1 = subscriptionFactory.createSubscription(channel.getChannelId(),
protocolSession,
new AMQShortString("S1"),
false,
null,
true,
- _queue);
+ channel.getCreditManager());
Subscription s2 = subscriptionFactory.createSubscription(channel.getChannelId(),
protocolSession,
@@ -145,14 +152,14 @@ public class AMQQueueMBeanTest extends TestCase
false,
null,
true,
- _queue);
- _subscribers.addSubscriber(s1);
- _subscribers.addSubscriber(s2);
+ channel.getCreditManager());
+ _queue.registerSubscription(s1,false);
+ _queue.registerSubscription(s2,false);
assertTrue(_queueMBean.getActiveConsumerCount() == 3);
assertTrue(_queueMBean.getConsumerCount() == 3);
s1.close();
- assertTrue(_queueMBean.getActiveConsumerCount() == 2);
+ assertEquals(2, (int) _queueMBean.getActiveConsumerCount());
assertTrue(_queueMBean.getConsumerCount() == 3);
}
@@ -205,13 +212,34 @@ public class AMQQueueMBeanTest extends TestCase
}
- AMQMessage msg = message(false, false);
+ IncomingMessage msg = message(false, false);
long id = msg.getMessageId();
_queue.clearQueue(_storeContext);
msg.enqueue(_queue);
- msg.routingComplete(_messageStore, _storeContext, new MessageHandleFactory());
- _queue.process(_storeContext, new QueueEntry(_queue, msg), false);
+ msg.routingComplete(_messageStore, new MessageHandleFactory());
+
+ msg.addContentBodyFrame(new ContentChunk()
+ {
+ ByteBuffer _data = ByteBuffer.allocate((int)MESSAGE_SIZE);
+
+ public int getSize()
+ {
+ return (int) MESSAGE_SIZE;
+ }
+
+ public ByteBuffer getData()
+ {
+ return _data;
+ }
+
+ public void reduceToFit()
+ {
+
+ }
+ });
+ msg.deliverToQueues();
+// _queue.process(_storeContext, new QueueEntry(_queue, msg), false);
_queueMBean.viewMessageContent(id);
try
{
@@ -224,7 +252,7 @@ public class AMQQueueMBeanTest extends TestCase
}
}
- private AMQMessage message(final boolean immediate, boolean persistent) throws AMQException
+ private IncomingMessage message(final boolean immediate, boolean persistent) throws AMQException
{
MessagePublishInfo publish = new MessagePublishInfo()
{
@@ -259,7 +287,10 @@ public class AMQQueueMBeanTest extends TestCase
contentHeaderBody.bodySize = MESSAGE_SIZE; // in bytes
contentHeaderBody.properties = new BasicContentHeaderProperties();
((BasicContentHeaderProperties) contentHeaderBody.properties).setDeliveryMode((byte) (persistent ? 2 : 1));
- return new AMQMessage(_messageStore.getNewMessageId(), publish, _transactionalContext, contentHeaderBody);
+ IncomingMessage msg = new IncomingMessage(_messageStore.getNewMessageId(), publish, _transactionalContext, _protocolSession);
+ msg.setContentHeaderBody(contentHeaderBody);
+ return msg;
+
}
@Override
@@ -272,10 +303,10 @@ public class AMQQueueMBeanTest extends TestCase
_transactionalContext = new NonTransactionalContext(_messageStore, _storeContext,
null,
- new LinkedList<RequiredDeliveryException>(),
- new HashSet<Long>());
+ new LinkedList<RequiredDeliveryException>()
+ );
- _queue = new AMQQueue(new AMQShortString("testQueue"), false, new AMQShortString("AMQueueMBeanTest"), false, _virtualHost);
+ _queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue"), false, new AMQShortString("AMQueueMBeanTest"), false, _virtualHost);
_queueMBean = new AMQQueueMBean(_queue);
_protocolSession = new TestMinaProtocolSession();
@@ -285,19 +316,20 @@ public class AMQQueueMBeanTest extends TestCase
{
for (int i = 0; i < messageCount; i++)
{
- AMQMessage currentMessage = message(false, persistent);
+ IncomingMessage currentMessage = message(false, persistent);
currentMessage.enqueue(_queue);
// route header
- currentMessage.routingComplete(_messageStore, _storeContext, new MessageHandleFactory());
+ currentMessage.routingComplete(_messageStore, new MessageHandleFactory());
// Add the body so we have somthing to test later
- currentMessage.addContentBodyFrame(_storeContext,
- _protocolSession.getMethodRegistry()
+ currentMessage.addContentBodyFrame(
+ _protocolSession.getMethodRegistry()
.getProtocolVersionMethodConverter()
.convertToContentChunk(
new ContentBody(ByteBuffer.allocate((int) MESSAGE_SIZE),
MESSAGE_SIZE)));
+ currentMessage.deliverToQueues();
}
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 c3219e6564..bba39403a5 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
@@ -182,6 +182,13 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi
_fastAccessConsumers[i] = null;
}
}
+
+
+
+ public String toString()
+ {
+ return "{ Fast: " + Arrays.asList(_fastAccessConsumers) + " ; Slow: " + _slowAccessConsumers + "}";
+ }
}
@@ -299,9 +306,6 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi
*/
private final IdToConsumerMap _consumers = new IdToConsumerMap();
- //Map<AMQShortString, BasicMessageConsumer> _consumers =
- //new ConcurrentHashMap<AMQShortString, BasicMessageConsumer>();
-
/**
* Contains a list of consumers which have been removed but which might still have
* messages to acknowledge, eg in client ack or transacted modes
@@ -1419,7 +1423,9 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi
if (message.isDeliverMessage())
{
_highestDeliveryTag.set(message.getDeliverBody().getDeliveryTag());
+
_queue.add(message);
+
}
else
{
diff --git a/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer.java b/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer.java
index efbce6033b..5b1c1aeeee 100644
--- a/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer.java
+++ b/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer.java
@@ -809,9 +809,9 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer
/** Acknowledge up to last message delivered (if any). Used when commiting. */
void acknowledgeDelivered()
{
- while (!_receivedDeliveryTags.isEmpty())
+ while (!_receivedDeliveryTags.isEmpty())
{
- _session.acknowledgeMessage(_receivedDeliveryTags.poll(), false);
+ _session.acknowledgeMessage(_receivedDeliveryTags.poll(), false);
}
}
@@ -1017,7 +1017,7 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer
{
_logger.warn("Queue was not empty after rejecting all messages Remaining:" + _synchronousQueue.size());
rollback();
- }
+ }
clearReceiveQueue();
}
diff --git a/java/client/src/main/java/org/apache/qpid/client/handler/BasicDeliverMethodHandler.java b/java/client/src/main/java/org/apache/qpid/client/handler/BasicDeliverMethodHandler.java
index d05e99d210..aa7599f355 100644
--- a/java/client/src/main/java/org/apache/qpid/client/handler/BasicDeliverMethodHandler.java
+++ b/java/client/src/main/java/org/apache/qpid/client/handler/BasicDeliverMethodHandler.java
@@ -46,7 +46,7 @@ public class BasicDeliverMethodHandler implements StateAwareMethodListener<Basic
{
final AMQProtocolSession session = stateManager.getProtocolSession();
final UnprocessedMessage msg = new UnprocessedMessage.UnprocessedDeliverMessage(body);
- _logger.debug("New JmsDeliver method received");
+ _logger.debug("New JmsDeliver method received");
session.unprocessedMessageReceived(channelId, msg);
}
}
diff --git a/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolSession.java b/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolSession.java
index 6a5cc62bfc..d19cd7f0d8 100644
--- a/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolSession.java
+++ b/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolSession.java
@@ -297,15 +297,8 @@ public class AMQProtocolSession implements AMQVersionAwareProtocolSession
throw new AMQException("Error: received content body without having received a ContentHeader frame first");
}
- /*try
- {*/
+
msg.receiveBody(contentBody);
- /*}
- catch (UnexpectedBodyReceivedException e)
- {
- _channelId2UnprocessedMsgMap.remove(channelId);
- throw e;
- }*/
if (msg.isAllBodyDataReceived())
{
@@ -324,9 +317,13 @@ public class AMQProtocolSession implements AMQVersionAwareProtocolSession
* @param channelId the channel id the message should be delivered to
* @param msg the message
*/
- private void deliverMessageToAMQSession(int channelId, UnprocessedMessage msg)
+ private void deliverMessageToAMQSession(int channelId, UnprocessedMessage msg) throws AMQException
{
AMQSession session = getSession(channelId);
+ if(session == null)
+ {
+ throw new AMQException("Error: received message on non-existant channel:" + channelId);
+ }
session.messageReceived(msg);
if((channelId & FAST_CHANNEL_ACCESS_MASK) == 0)
{
diff --git a/java/client/src/test/java/org/apache/qpid/client/AMQQueueDeferredOrderingTest.java b/java/client/src/test/java/org/apache/qpid/client/AMQQueueDeferredOrderingTest.java
index fe418535d6..ec45d7e182 100644
--- a/java/client/src/test/java/org/apache/qpid/client/AMQQueueDeferredOrderingTest.java
+++ b/java/client/src/test/java/org/apache/qpid/client/AMQQueueDeferredOrderingTest.java
@@ -126,7 +126,7 @@ public class AMQQueueDeferredOrderingTest extends TestCase
_logger.info("Consuming messages");
for (int i = 0; i < NUM_MESSAGES; i++)
{
- Message msg = consumer.receive(3000);
+ Message msg = consumer.receive(90000);
assertNotNull("Message should not be null", msg);
assertTrue("Message should be a text message", msg instanceof TextMessage);
assertEquals("Message content does not match expected", Integer.toString(i), ((TextMessage) msg).getText());
diff --git a/java/client/src/test/java/org/apache/qpid/client/MessageListenerMultiConsumerTest.java b/java/client/src/test/java/org/apache/qpid/client/MessageListenerMultiConsumerTest.java
index 20632e245f..6e19f53ffe 100644
--- a/java/client/src/test/java/org/apache/qpid/client/MessageListenerMultiConsumerTest.java
+++ b/java/client/src/test/java/org/apache/qpid/client/MessageListenerMultiConsumerTest.java
@@ -128,7 +128,7 @@ public class MessageListenerMultiConsumerTest extends TestCase
{
int msg = 0;
int MAX_LOOPS = MSG_COUNT * 2;
- for (int loops = 0; (msg < MSG_COUNT) || (loops < MAX_LOOPS); loops++)
+ for (int loops = 0; (msg < MSG_COUNT) && (loops < MAX_LOOPS); loops++)
{
if (_consumer1.receive(100) != null)
@@ -220,12 +220,18 @@ public class MessageListenerMultiConsumerTest extends TestCase
for (int msg = 0; msg < (MSG_COUNT / 2); msg++)
{
- assertTrue(_consumer1.receive() != null);
+
+ final Message message = _consumer1.receive(100000);
+ if(message == null)
+ {
+ System.out.println("!!!!!!!! " + msg);
+ }
+ assertTrue(message != null);
}
for (int msg = 0; msg < (MSG_COUNT / 2); msg++)
{
- assertTrue(consumer2.receive() != null);
+ assertTrue(consumer2.receive(10000) != null);
}
}
else
@@ -235,12 +241,12 @@ public class MessageListenerMultiConsumerTest extends TestCase
for (int msg = 0; msg < (MSG_COUNT / 2); msg++)
{
- assertTrue(_consumer1.receive() != null);
+ assertTrue(_consumer1.receive(10000) != null);
}
for (int msg = 0; msg < (MSG_COUNT / 2); msg++)
{
- assertTrue(_consumer2.receive() != null);
+ assertTrue(_consumer2.receive(10000) != null);
}
}
}
diff --git a/java/client/src/test/java/org/apache/qpid/client/ResetMessageListenerTest.java b/java/client/src/test/java/org/apache/qpid/client/ResetMessageListenerTest.java
index 21f3e273aa..81d9a39dd3 100644
--- a/java/client/src/test/java/org/apache/qpid/client/ResetMessageListenerTest.java
+++ b/java/client/src/test/java/org/apache/qpid/client/ResetMessageListenerTest.java
@@ -116,6 +116,7 @@ public class ResetMessageListenerTest extends TestCase
{
_producer.send(_producerSession.createTextMessage("Message " + msg));
}
+// Thread.sleep(120000);
}
@@ -247,6 +248,14 @@ public class ResetMessageListenerTest extends TestCase
{
_producer.send(_producerSession.createTextMessage("Message " + msg));
}
+// try
+// {
+// Thread.sleep(120000);
+// }
+// catch (InterruptedException e)
+// {
+// e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
+// }
}
catch (JMSException e)
{
@@ -257,7 +266,7 @@ public class ResetMessageListenerTest extends TestCase
try
{
- _allSecondMessagesSent.await(5000, TimeUnit.MILLISECONDS);
+ _allSecondMessagesSent.await(500000, TimeUnit.MILLISECONDS);
}
catch (InterruptedException e)
{
diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/ack/RecoverTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/ack/RecoverTest.java
index b6f46b4acc..9b34c36ec6 100644
--- a/java/client/src/test/java/org/apache/qpid/test/unit/ack/RecoverTest.java
+++ b/java/client/src/test/java/org/apache/qpid/test/unit/ack/RecoverTest.java
@@ -115,6 +115,7 @@ public class RecoverTest extends TestCase
consumerSession.recover();
tm = (TextMessage) consumer.receiveNoWait();
+
assertNull(tm);
_logger.info("No messages redelivered as is expected");
diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/basic/SessionStartTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/basic/SessionStartTest.java
index cc18169a5b..56247f9634 100644
--- a/java/client/src/test/java/org/apache/qpid/test/unit/basic/SessionStartTest.java
+++ b/java/client/src/test/java/org/apache/qpid/test/unit/basic/SessionStartTest.java
@@ -28,6 +28,9 @@ import org.apache.qpid.client.AMQQueue;
import org.apache.qpid.client.AMQSession;
import org.apache.qpid.framing.AMQShortString;
import org.apache.qpid.testutil.VMBrokerSetup;
+import org.apache.qpid.AMQException;
+import org.apache.log4j.BasicConfigurator;
+import org.apache.log4j.Level;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -49,6 +52,7 @@ public class SessionStartTest extends TestCase implements MessageListener
protected void setUp() throws Exception
{
super.setUp();
+
init(new AMQConnection(_connectionString, "guest", "guest", randomize("Client"), "test"));
}
diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseOkTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseOkTest.java
index 559e9a4741..3c2e72c07e 100644
--- a/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseOkTest.java
+++ b/java/client/src/test/java/org/apache/qpid/test/unit/client/channelclose/ChannelCloseOkTest.java
@@ -69,6 +69,7 @@ public class ChannelCloseOkTest extends TestCase
private static final Logger _log = LoggerFactory.getLogger(ChannelCloseOkTest.class);
public String _connectionString = "vm://:1";
+ private static final int NUM_MESSAGES = 300;
protected void setUp() throws Exception
{
@@ -170,19 +171,18 @@ public class ChannelCloseOkTest extends TestCase
// Ensure both sessions are still ok.
// Send a bunch of messages as this give time for the sessions to be erroneously closed.
- final int num = 300;
- for (int i = 0; i < num; ++i)
+ for (int i = 0; i < NUM_MESSAGES; ++i)
{
send(_session1, _destination1, "" + i);
send(_session2, _destination2, "" + i);
}
- waitFor(_received1, num + 1);
- waitFor(_received2, num + 1);
+ waitFor(_received1, NUM_MESSAGES + 1);
+ waitFor(_received2, NUM_MESSAGES + 1);
// Note that the third message is never received as it is sent to an incorrect destination.
- assertEquals(num + 1, _received1.size());
- assertEquals(num + 1, _received2.size());
+ assertEquals(NUM_MESSAGES + 1, _received1.size());
+ assertEquals(NUM_MESSAGES + 1, _received2.size());
}
private void sendAndWait(Session session, Destination destination, String message, List<Message> received, int count)
@@ -199,15 +199,17 @@ public class ChannelCloseOkTest extends TestCase
producer1.send(session.createTextMessage(message));
}
- private void waitFor(List<Message> received, int count) throws InterruptedException
+ private void waitFor(List<Message> received, final int count) throws InterruptedException
{
+ int lastSeen = -1;
synchronized (received)
{
- while (received.size() < count)
+ while ((lastSeen != received.size()) && (lastSeen = received.size()) < count)
{
+
try
{
- received.wait();
+ received.wait(2000L);
}
catch (InterruptedException e)
{
@@ -216,6 +218,10 @@ public class ChannelCloseOkTest extends TestCase
}
}
}
+ if(received.size() < count)
+ {
+ throw new RuntimeException("Expected: " + count + " got: " + received.size());
+ }
}
private static String randomize(String in)
diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/Client.java b/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/Client.java
index 19ef612bcc..2ee29e3da4 100644
--- a/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/Client.java
+++ b/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/Client.java
@@ -75,7 +75,9 @@ public class Client implements MessageListener
public synchronized void onMessage(Message response)
{
+
_logger.info("Received " + (++_count) + " of " + _expected + " responses.");
+
if (_count == _expected)
{
@@ -89,10 +91,10 @@ public class Client implements MessageListener
if (_count < _expected)
{
- wait(10000L);
+ wait(1000L);
}
- if (_count < _expected)
+ if (_count != _expected)
{
throw new Exception("Didn't receive all messages... got " + _count + " expected " + _expected);
}
diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/CombinedTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/CombinedTest.java
index 9cde24dd92..81227b9540 100644
--- a/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/CombinedTest.java
+++ b/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/CombinedTest.java
@@ -22,9 +22,13 @@ package org.apache.qpid.test.unit.client.forwardall;
import junit.framework.TestCase;
import org.apache.qpid.testutil.VMBrokerSetup;
+import org.apache.qpid.server.queue.SimpleAMQQueue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.util.Set;
+import java.util.HashSet;
+
/**
* Runs the Service's and Client parts of the test in the same process
* as the broker
@@ -34,6 +38,7 @@ public class CombinedTest extends TestCase
private static final Logger _logger = LoggerFactory.getLogger(CombinedTest.class);
private int run = 0;
+
protected void setUp() throws Exception
{
super.setUp();
@@ -47,16 +52,16 @@ public class CombinedTest extends TestCase
public void testForwardAll() throws Exception
{
- while (run < 10)
+ while (run < 100)
{
int services = 2;
ServiceCreator.start("vm://:1", services);
-
+ Thread.sleep(100);
_logger.info("Starting " + ++run + " client...");
new Client("vm://:1", services).shutdownWhenComplete();
-
+ ServiceCreator.closeAll();
_logger.info("Completed " + run + " successfully!");
}
}
diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/Service.java b/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/Service.java
index 6593f7d86a..bf03ce6899 100644
--- a/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/Service.java
+++ b/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/Service.java
@@ -37,14 +37,16 @@ public class Service implements MessageListener
{
private final AMQConnection _connection;
private final AMQSession _session;
+ private final int _id;
- Service(String broker) throws Exception
+ Service(String broker, int id) throws Exception
{
- this(connect(broker));
+ this(connect(broker), id);
}
- Service(AMQConnection connection) throws Exception
+ Service(AMQConnection connection, int id) throws Exception
{
+ _id = id;
_connection = connection;
AMQQueue queue = new SpecialQueue(connection, "ServiceQueue");
_session = (AMQSession) _connection.createSession(false, AMQSession.NO_ACKNOWLEDGE);
@@ -56,7 +58,7 @@ public class Service implements MessageListener
{
try
{
- Message response = _session.createTextMessage("Response!");
+ Message response = _session.createTextMessage("Response! " + _id);
Destination replyTo = request.getJMSReplyTo();
_session.createProducer(replyTo).send(response);
}
diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/ServiceCreator.java b/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/ServiceCreator.java
index be16f6b7ae..310a0993bc 100644
--- a/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/ServiceCreator.java
+++ b/java/client/src/test/java/org/apache/qpid/test/unit/client/forwardall/ServiceCreator.java
@@ -34,17 +34,19 @@ public class ServiceCreator implements Runnable
private final String broker;
private Service service;
+ private final int id;
- ServiceCreator(String broker)
+ ServiceCreator(String broker, final int id)
{
this.broker = broker;
+ this.id = id;
}
public void run()
{
try
{
- service = new Service(broker);
+ service = new Service(broker, id);
}
catch (Exception e)
{
@@ -76,11 +78,12 @@ public class ServiceCreator implements Runnable
{
threads = new Thread[services];
_services = new ServiceCreator[services];
- ServiceCreator runner = new ServiceCreator(broker);
+ //ServiceCreator runner = new ServiceCreator(broker);
// start services
_logger.info("Starting " + services + " services...");
for (int i = 0; i < services; i++)
{
+ ServiceCreator runner = new ServiceCreator(broker,i);
threads[i] = new Thread(runner);
_services[i] = runner;
threads[i].start();
diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/close/MessageRequeueTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/close/MessageRequeueTest.java
index 8d7645c1fd..56904f20de 100644
--- a/java/client/src/test/java/org/apache/qpid/test/unit/close/MessageRequeueTest.java
+++ b/java/client/src/test/java/org/apache/qpid/test/unit/close/MessageRequeueTest.java
@@ -40,30 +40,40 @@ import javax.jms.Queue;
import javax.jms.Session;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.Random;
+import java.util.UUID;
public class MessageRequeueTest extends TestCase
{
private static final Logger _logger = LoggerFactory.getLogger(MessageRequeueTest.class);
+
+
protected static AtomicInteger consumerIds = new AtomicInteger(0);
protected final Integer numTestMessages = 150;
protected final int consumeTimeout = 3000;
- protected final String queue = "direct://amq.direct//queue";
+ //protected final String queue = "direct://amq.direct//queue";
protected String payload = "Message:";
protected final String BROKER = "vm://:1";
private boolean testReception = true;
private long[] receieved = new long[numTestMessages + 1];
- private boolean passed = false;
+ //private boolean passed = false;
protected void setUp() throws Exception
{
super.setUp();
TransportConnection.createVMBroker(1);
+
+ }
+
+ private void putMessagesOnQueueThenClose(String queue)
+ throws JMSException, InterruptedException
+ {
QpidClientConnection conn = new QpidClientConnection(BROKER);
conn.connect();
@@ -76,20 +86,25 @@ public class MessageRequeueTest extends TestCase
conn.disconnect();
}
- protected void tearDown() throws Exception
+
+
+ private void tearDownQueue(String queue)
+ throws JMSException, InterruptedException
+
{
- super.tearDown();
+ QpidClientConnection conn = new QpidClientConnection(BROKER);
- if (!passed) // clean up
- {
- QpidClientConnection conn = new QpidClientConnection(BROKER);
+ conn.connect();
+ // clear queue
+ conn.consume(queue, consumeTimeout);
- conn.connect();
- // clear queue
- conn.consume(queue, consumeTimeout);
+ conn.disconnect();
+ }
- conn.disconnect();
- }
+
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
TransportConnection.killVMBroker(1);
}
@@ -102,6 +117,11 @@ public class MessageRequeueTest extends TestCase
*/
public void testDrain() throws JMSException, InterruptedException
{
+
+ String queue = "direct://amq.direct//queue" + UUID.randomUUID();
+
+ putMessagesOnQueueThenClose(queue);
+
QpidClientConnection conn = new QpidClientConnection(BROKER);
conn.connect();
@@ -172,18 +192,22 @@ public class MessageRequeueTest extends TestCase
assertEquals(list.toString(), 0, failed);
_logger.info("consumed: " + messagesReceived);
conn.disconnect();
- passed = true;
+ tearDownQueue(queue);
}
/** multiple consumers
* Based on code subbmitted by client FT-304
*/
- public void testCompetingConsumers()
+ public void testCompetingConsumers() throws JMSException, InterruptedException
{
- Consumer c1 = new Consumer();
- Consumer c2 = new Consumer();
- Consumer c3 = new Consumer();
- Consumer c4 = new Consumer();
+ String queue = "direct://amq.direct//queue" + UUID.randomUUID();
+
+ putMessagesOnQueueThenClose(queue);
+
+ Consumer c1 = new Consumer(queue);
+ Consumer c2 = new Consumer(queue);
+ Consumer c3 = new Consumer(queue);
+ Consumer c4 = new Consumer(queue);
Thread t1 = new Thread(c1);
Thread t2 = new Thread(c2);
@@ -237,16 +261,18 @@ public class MessageRequeueTest extends TestCase
assertEquals(list.toString() + "-" + numTestMessages + "-" + totalConsumed, 0, failed);
assertTrue("number of consumed messages does not match initial data: " + totalConsumed, numTestMessages <= totalConsumed);
- passed = true;
+ tearDownQueue(queue);
}
class Consumer implements Runnable
{
private Integer count = 0;
private Integer id;
+ private final String _queue;
- public Consumer()
+ public Consumer(String queue)
{
+ _queue = queue;
id = consumerIds.addAndGet(1);
}
@@ -263,7 +289,7 @@ public class MessageRequeueTest extends TestCase
Message result;
do
{
- result = conn.getNextMessage(queue, consumeTimeout);
+ result = conn.getNextMessage(_queue, consumeTimeout);
if (result != null)
{
@@ -322,8 +348,11 @@ public class MessageRequeueTest extends TestCase
}
}
- public void testRequeue() throws JMSException, AMQException, URLSyntaxException
+ public void testRequeue() throws JMSException, AMQException, URLSyntaxException, InterruptedException
{
+ String queue = "direct://amq.direct//queue" + UUID.randomUUID();
+ putMessagesOnQueueThenClose(queue);
+
int run = 0;
// while (run < 10)
{
diff --git a/java/client/src/test/java/org/apache/qpid/test/unit/topic/DurableSubscriptionTest.java b/java/client/src/test/java/org/apache/qpid/test/unit/topic/DurableSubscriptionTest.java
index 101cba2352..98c0225096 100644
--- a/java/client/src/test/java/org/apache/qpid/test/unit/topic/DurableSubscriptionTest.java
+++ b/java/client/src/test/java/org/apache/qpid/test/unit/topic/DurableSubscriptionTest.java
@@ -88,7 +88,7 @@ public class DurableSubscriptionTest extends TestCase
Message msg;
_logger.info("Receive message on consumer 1:expecting A");
- msg = consumer1.receive();
+ msg = consumer1.receive(1000);
assertEquals("A", ((TextMessage) msg).getText());
_logger.info("Receive message on consumer 1 :expecting null");
msg = consumer1.receive(1000);
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 065b06a87d..39730ef3ac 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
@@ -20,28 +20,33 @@
*/
package org.apache.qpid.test.unit.topic;
-import javax.jms.InvalidDestinationException;
-import javax.jms.JMSException;
+import javax.jms.*;
import javax.jms.MessageConsumer;
import javax.jms.Session;
-import javax.jms.TemporaryTopic;
-import javax.jms.TextMessage;
-import javax.jms.TopicPublisher;
-import javax.jms.TopicSession;
-import javax.jms.TopicSubscriber;
+import javax.jms.Message;
import junit.framework.TestCase;
import org.apache.qpid.client.AMQConnection;
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.transport.TransportConnection;
+import org.apache.qpid.url.URLSyntaxException;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.jms.*;
+
+import java.util.UUID;
/** @author Apache Software Foundation */
public class TopicSessionTest extends TestCase
{
private static final String BROKER = "vm://:1";
+ private static final int THREADS = 20;
+ private static final int MESSAGE_COUNT = 10000;
+ private static final int MESSAGE_SIZE = 128;
protected void setUp() throws Exception
{
@@ -102,6 +107,60 @@ public class TopicSessionTest extends TestCase
subscriptionNameReuseForDifferentTopic(true);
}
+ public void notestSilly() throws Exception
+ {
+
+
+ final ExceptionListener listener = new ExceptionListener()
+ {
+ public void onException(JMSException jmsException)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+ };
+
+
+ Thread[] threads = new Thread[100];
+
+ for(int j = 0; j < 20; j++)
+ {
+ threads[j] = new Thread(new Runnable() {
+ public void run()
+ {
+ try
+ {
+ AMQConnection con = new AMQConnection("tcp://127.0.0.1:5672?retries='0'", "guest", "guest", "test", "test");
+ AMQTopic topic = new AMQTopic(con, "MyTopic1" + UUID.randomUUID());
+
+
+ TopicSession session1 = con.createTopicSession(false, AMQSession.NO_ACKNOWLEDGE);
+
+ con.setExceptionListener(listener);
+
+ TopicPublisher publisher = session1.createPublisher(topic);
+
+ con.start();
+
+ while(true)
+ {
+ publisher.publish(session1.createTextMessage("hello"));
+ Thread.sleep(THREADS);
+ }
+ }
+ catch(Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+ });
+ threads[j].run();
+ }
+
+ threads[0].join();
+
+ }
+
+
private void subscriptionNameReuseForDifferentTopic(boolean shutdown) throws Exception
{
AMQConnection con = new AMQConnection("vm://:1?retries='0'", "guest", "guest", "test", "test");
@@ -368,8 +427,160 @@ public class TopicSessionTest extends TestCase
con2.close();
}
+
+ public void noTestPublishToManyConsumers() throws Exception
+ {
+
+
+ final ExceptionListener exceptionListener = new ExceptionListener()
+ {
+ public void onException(JMSException jmsException)
+ {
+ jmsException.printStackTrace();
+ }
+ };
+
+
+
+ SubscribingThread[] threads = new SubscribingThread[100];
+
+ final String topicName = "MyTopic1" + UUID.randomUUID();
+ for(int j = 0; j < 21; j++)
+ {
+ final int threadId = j;
+ threads[threadId] = new SubscribingThread(threadId, topicName, exceptionListener);
+ threads[j].start();
+ Thread.sleep(100);
+ }
+
+
+ threads[1].join();
+
+ int totalMessages = 0;
+
+ for(int j = 1; j < 21; j++)
+ {
+
+ System.err.println("Thread " + j + ": " + threads[j].msgId);
+ totalMessages += threads[j].msgId;
+ }
+
+ System.err.println("****** Total: " + totalMessages);
+
+
+ }
+
+
+
+
public static junit.framework.Test suite()
{
return new junit.framework.TestSuite(TopicSessionTest.class);
}
+
+ private static class SubscribingThread extends Thread
+ {
+ private final int _threadId;
+ private final String _topicName;
+ private final ExceptionListener _exceptionListener;
+ int msgId = 0;
+
+ public SubscribingThread(final int threadId, final String topicName, final ExceptionListener exceptionListener)
+ {
+ _threadId = threadId;
+ _topicName = topicName;
+ _exceptionListener = exceptionListener;
+ }
+
+ public void run()
+ {
+ try
+ {
+ System.err.println("Thread: " + _threadId);
+
+
+ if(_threadId >0)
+ {
+
+ AMQConnection con2 = new AMQConnection("tcp://127.0.0.1:5672?retries='0'", "guest", "guest", "test", "test");
+ //AMQConnection con2 = new AMQConnection(BROKER + "?retries='0'", "guest", "guest", "test", "test");
+ AMQTopic topic2 = new AMQTopic(con2, _topicName);
+ TopicSession session2 = con2.createTopicSession(false, AMQSession.NO_ACKNOWLEDGE);
+ TopicSubscriber sub = session2.createSubscriber(topic2);
+ con2.setExceptionListener(_exceptionListener);
+
+
+
+ final MessageListener messageListener = new MessageListener()
+ {
+
+ public void onMessage(Message message)
+ {
+ try
+ {
+ msgId = message.getIntProperty("MessageId");
+ if(msgId % 1000 == 0)
+ {
+ System.err.println("Thread: " + _threadId + ": " + msgId + "messages");
+ }
+ }
+ catch (JMSException e)
+ {
+ e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
+ }
+ }
+ };
+
+
+ sub.setMessageListener(messageListener);
+ con2.start();
+
+ Thread.sleep(125000);
+
+
+// Thread.sleep(1200000);
+ }
+ else
+ {
+ int messageId = 0;
+
+ AMQConnection con = new AMQConnection("tcp://127.0.0.1:5672?retries='0'", "guest", "guest", "test", "test");
+ //AMQConnection con = new AMQConnection(BROKER + "?retries='0'", "guest", "guest", "test", "test");
+
+
+ AMQTopic topic = new AMQTopic(con, _topicName);
+
+
+ TopicSession session1 = con.createTopicSession(false, AMQSession.NO_ACKNOWLEDGE);
+
+ con.setExceptionListener(_exceptionListener);
+
+ TopicPublisher publisher = session1.createPublisher(topic);
+ publisher.setDisableMessageID(true);
+ publisher.setDisableMessageTimestamp(true);
+ con.start();
+
+ Thread.sleep(5000);
+
+ publisher.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
+
+ while(messageId <= 240000)
+ //while(messageId <= 10000)
+ {
+ final TextMessage textMessage = session1.createTextMessage("hello");
+ textMessage.setIntProperty("MessageId", messageId++);
+
+
+ publisher.publish(textMessage);
+
+ }
+ }
+
+ }
+ catch(Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+ }
}
diff --git a/java/common/src/main/java/org/apache/mina/common/FixedSizeByteBufferAllocator.java b/java/common/src/main/java/org/apache/mina/common/FixedSizeByteBufferAllocator.java
index bed80d5954..0c311b6645 100644
--- a/java/common/src/main/java/org/apache/mina/common/FixedSizeByteBufferAllocator.java
+++ b/java/common/src/main/java/org/apache/mina/common/FixedSizeByteBufferAllocator.java
@@ -62,7 +62,6 @@ public class FixedSizeByteBufferAllocator implements ByteBufferAllocator
private static final class FixedSizeByteBuffer extends ByteBuffer
{
private java.nio.ByteBuffer buf;
- private int refCount = 1;
private int mark = -1;
@@ -70,36 +69,14 @@ public class FixedSizeByteBufferAllocator implements ByteBufferAllocator
{
this.buf = buf;
buf.order( ByteOrder.BIG_ENDIAN );
- refCount = 1;
}
public synchronized void acquire()
{
- if( refCount <= 0 )
- {
- throw new IllegalStateException( "Already released buffer." );
- }
-
- refCount ++;
}
public void release()
{
- synchronized( this )
- {
- if( refCount <= 0 )
- {
- refCount = 0;
- throw new IllegalStateException(
- "Already released buffer. You released the buffer too many times." );
- }
-
- refCount --;
- if( refCount > 0)
- {
- return;
- }
- }
}
public java.nio.ByteBuffer buf()
@@ -157,50 +134,12 @@ public class FixedSizeByteBufferAllocator implements ByteBufferAllocator
{
if( newCapacity > capacity() )
{
- // Allocate a new buffer and transfer all settings to it.
- int pos = position();
- int limit = limit();
- ByteOrder bo = order();
-
- capacity0( newCapacity );
- buf.limit( limit );
- if( mark >= 0 )
- {
- buf.position( mark );
- buf.mark();
- }
- buf.position( pos );
- buf.order( bo );
+ throw new IllegalArgumentException();
}
return this;
}
- protected void capacity0( int requestedCapacity )
- {
- int newCapacity = MINIMUM_CAPACITY;
- while( newCapacity < requestedCapacity )
- {
- newCapacity <<= 1;
- }
-
- java.nio.ByteBuffer oldBuf = this.buf;
- java.nio.ByteBuffer newBuf;
- if( isDirect() )
- {
- newBuf = java.nio.ByteBuffer.allocateDirect( newCapacity );
- }
- else
- {
- newBuf = java.nio.ByteBuffer.allocate( newCapacity );
- }
-
- newBuf.clear();
- oldBuf.clear();
- newBuf.put( oldBuf );
- this.buf = newBuf;
- }
-
public boolean isAutoExpand()
diff --git a/java/common/src/main/java/org/apache/mina/filter/codec/OurCumulativeProtocolDecoder.java b/java/common/src/main/java/org/apache/mina/filter/codec/OurCumulativeProtocolDecoder.java
index 810d12f472..2ab3ddb50e 100644
--- a/java/common/src/main/java/org/apache/mina/filter/codec/OurCumulativeProtocolDecoder.java
+++ b/java/common/src/main/java/org/apache/mina/filter/codec/OurCumulativeProtocolDecoder.java
@@ -91,7 +91,7 @@ import org.apache.mina.common.IoSession;
* </pre>
*
* @author The Apache Directory Project (mina-dev@directory.apache.org)
- * @version $Rev: 598285 $, $Date: 2007-11-26 14:16:01 +0000 (Mon, 26 Nov 2007) $
+ * @version $Rev$, $Date$
*/
public abstract class OurCumulativeProtocolDecoder extends ProtocolDecoderAdapter {
diff --git a/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketConnector.java b/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketConnector.java
index cb24102edd..30c64f44cd 100644
--- a/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketConnector.java
+++ b/java/common/src/main/java/org/apache/mina/transport/socket/nio/MultiThreadSocketConnector.java
@@ -376,7 +376,8 @@ public class MultiThreadSocketConnector extends SocketConnector
// Set the ConnectFuture of the specified session, which will be
// removed and notified by AbstractIoFilterChain eventually.
- session.setAttribute( AbstractIoFilterChain.CONNECT_FUTURE, connectFuture );
+
+ session.setAttribute( AbstractIoFilterChain.CONNECT_FUTURE, connectFuture );
// Forward the remaining process to the SocketIoProcessor.
session.getIoProcessor().addNew(session);
diff --git a/java/common/src/main/java/org/apache/qpid/common/ClientProperties.java b/java/common/src/main/java/org/apache/qpid/common/ClientProperties.java
index 67f16e6a87..7371c12519 100644
--- a/java/common/src/main/java/org/apache/qpid/common/ClientProperties.java
+++ b/java/common/src/main/java/org/apache/qpid/common/ClientProperties.java
@@ -20,6 +20,8 @@
*/
package org.apache.qpid.common;
+import org.apache.qpid.framing.AMQShortString;
+
/**
* Specifies the available client property types that different clients can use to identify themselves with.
*
@@ -30,8 +32,21 @@ package org.apache.qpid.common;
*/
public enum ClientProperties
{
- instance,
- product,
- version,
- platform
+ instance("instance"),
+ product("product"),
+ version("version"),
+ platform("platform");
+
+ private final AMQShortString _amqShortString;
+
+ private ClientProperties(String name)
+ {
+ _amqShortString = new AMQShortString(name);
+ }
+
+
+ public AMQShortString toAMQShortString()
+ {
+ return _amqShortString;
+ }
}
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 665cbf7a84..ad2ab2ac0b 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
@@ -90,7 +90,7 @@ public final class AMQShortString implements CharSequence, Comparable<AMQShortSt
private AMQShortString substring(final int from, final int to)
{
- return new AMQShortString(_data, from, to);
+ return new AMQShortString(_data, from+_offset, to+_offset);
}
@@ -184,11 +184,22 @@ public final class AMQShortString implements CharSequence, Comparable<AMQShortSt
private AMQShortString(ByteBuffer data, final int length)
{
- byte[] dataBytes = new byte[length];
- data.get(dataBytes);
- _data = dataBytes;
+ if(data.isDirect() || data.isReadOnly())
+ {
+ byte[] dataBytes = new byte[length];
+ data.get(dataBytes);
+ _data = dataBytes;
+ _offset = 0;
+ }
+ else
+ {
+
+ _data = data.array();
+ _offset = data.arrayOffset() + data.position();
+ data.skip(length);
+
+ }
_length = length;
- _offset = 0;
}
@@ -199,6 +210,20 @@ public final class AMQShortString implements CharSequence, Comparable<AMQShortSt
_data = data;
}
+ public AMQShortString shrink()
+ {
+ if(_data.length != _length)
+ {
+ byte[] dataBytes = new byte[_length];
+ System.arraycopy(_data,_offset,dataBytes,0,_length);
+ return new AMQShortString(dataBytes,0,_length);
+ }
+ else
+ {
+ return this;
+ }
+ }
+
/**
* Get the length of the short string
@@ -572,7 +597,7 @@ public final class AMQShortString implements CharSequence, Comparable<AMQShortSt
ref = _globalInternMap.get(this);
if((ref == null) || ((internString = ref.get()) == null))
{
- internString = new AMQShortString(getBytes());
+ internString = shrink();
ref = new WeakReference(internString);
_globalInternMap.put(internString, ref);
}
@@ -651,7 +676,7 @@ public final class AMQShortString implements CharSequence, Comparable<AMQShortSt
public int toIntValue()
{
- int pos = 0;
+ int pos = _offset;
int val = 0;
@@ -660,7 +685,7 @@ public final class AMQShortString implements CharSequence, Comparable<AMQShortSt
{
pos++;
}
- while(pos < _length)
+ while(pos < _length + _offset)
{
int digit = (int) (_data[pos++] - ZERO);
if((digit < 0) || (digit > 9))
@@ -679,7 +704,7 @@ public final class AMQShortString implements CharSequence, Comparable<AMQShortSt
public boolean contains(final byte b)
{
- for(int i = 0; i < _length; i++)
+ for(int i = _offset; i < _length + _offset; i++)
{
if(_data[i] == b)
{
@@ -689,4 +714,17 @@ public final class AMQShortString implements CharSequence, Comparable<AMQShortSt
return false; //To change body of created methods use File | Settings | File Templates.
}
+
+ public static void main(String args[])
+ {
+ AMQShortString s = new AMQShortString("a.b.c.d.e.f.g.h.i.j.k");
+ AMQShortString s2 = s.substring(2, 7);
+
+ AMQShortStringTokenizer t = s2.tokenize((byte) '.');
+ while(t.hasMoreTokens())
+ {
+ System.err.println(t.nextToken());
+ }
+ }
+
}
diff --git a/java/common/src/main/java/org/apache/qpid/framing/AMQTypedValue.java b/java/common/src/main/java/org/apache/qpid/framing/AMQTypedValue.java
index e5b1fad9a8..4a1a2e709d 100644
--- a/java/common/src/main/java/org/apache/qpid/framing/AMQTypedValue.java
+++ b/java/common/src/main/java/org/apache/qpid/framing/AMQTypedValue.java
@@ -93,4 +93,24 @@ public class AMQTypedValue
{
return "[" + getType() + ": " + getValue() + "]";
}
+
+
+ public boolean equals(Object o)
+ {
+ if(o instanceof AMQTypedValue)
+ {
+ AMQTypedValue other = (AMQTypedValue) o;
+ return _type == other._type && (_value == null ? other._value == null : _value.equals(other._value));
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ public int hashCode()
+ {
+ return _type.hashCode() ^ (_value == null ? 0 : _value.hashCode());
+ }
+
}
diff --git a/java/common/src/main/java/org/apache/qpid/framing/FieldTable.java b/java/common/src/main/java/org/apache/qpid/framing/FieldTable.java
index ee6762181d..d87da9cdad 100644
--- a/java/common/src/main/java/org/apache/qpid/framing/FieldTable.java
+++ b/java/common/src/main/java/org/apache/qpid/framing/FieldTable.java
@@ -74,7 +74,7 @@ public class FieldTable
buffer.skip((int) length);
}
- private AMQTypedValue getProperty(AMQShortString string)
+ public AMQTypedValue getProperty(AMQShortString string)
{
checkPropertyName(string);
@@ -891,6 +891,20 @@ public class FieldTable
return keys;
}
+ public Iterator<Map.Entry<AMQShortString, AMQTypedValue>> iterator()
+ {
+ if(_encodedForm != null)
+ {
+ return new FieldTableIterator(_encodedForm.duplicate().rewind(),(int)_encodedSize);
+ }
+ else
+ {
+ initMapIfNecessary();
+ return _properties.entrySet().iterator();
+ }
+ }
+
+
public Object get(AMQShortString key)
{
@@ -1045,6 +1059,95 @@ public class FieldTable
}
}
+ private static final class FieldTableEntry implements Map.Entry<AMQShortString, AMQTypedValue>
+ {
+ private final AMQTypedValue _value;
+ private final AMQShortString _key;
+
+ public FieldTableEntry(final AMQShortString key, final AMQTypedValue value)
+ {
+ _key = key;
+ _value = value;
+ }
+
+ public AMQShortString getKey()
+ {
+ return _key;
+ }
+
+ public AMQTypedValue getValue()
+ {
+ return _value;
+ }
+
+ public AMQTypedValue setValue(final AMQTypedValue value)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean equals(Object o)
+ {
+ if(o instanceof FieldTableEntry)
+ {
+ FieldTableEntry other = (FieldTableEntry) o;
+ return (_key == null ? other._key == null : _key.equals(other._key))
+ && (_value == null ? other._value == null : _value.equals(other._value));
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ public int hashCode()
+ {
+ return (getKey()==null ? 0 : getKey().hashCode())
+ ^ (getValue()==null ? 0 : getValue().hashCode());
+ }
+
+ }
+
+
+ private static final class FieldTableIterator implements Iterator<Map.Entry<AMQShortString, AMQTypedValue>>
+ {
+
+ private final ByteBuffer _buffer;
+ private int _expectedRemaining;
+
+ public FieldTableIterator(ByteBuffer buffer, int length)
+ {
+ _buffer = buffer;
+ _expectedRemaining = buffer.remaining() - length;
+ }
+
+ public boolean hasNext()
+ {
+ return (_buffer.remaining() > _expectedRemaining);
+ }
+
+ public Map.Entry<AMQShortString, AMQTypedValue> next()
+ {
+ if(hasNext())
+ {
+ final AMQShortString key = EncodingUtils.readAMQShortString(_buffer);
+ AMQTypedValue value = AMQTypedValue.readFromBuffer(_buffer);
+ return new FieldTableEntry(key, value);
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ public void remove()
+ {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+
+
+
public int hashCode()
{
initMapIfNecessary();
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 56eadbb3b2..6aec1cb164 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
@@ -23,10 +23,11 @@ package org.apache.qpid.management.ui;
import junit.framework.TestCase;
import org.apache.qpid.exchange.ExchangeDefaults;
import org.apache.qpid.framing.AMQShortString;
-import org.apache.qpid.server.exchange.DestNameExchange;
+import org.apache.qpid.server.exchange.DirectExchange;
import org.apache.qpid.server.management.AMQManagedObject;
-import org.apache.qpid.server.queue.AMQQueue;
import org.apache.qpid.server.queue.AMQQueueMBean;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.queue.AMQQueueFactory;
import org.apache.qpid.server.registry.ApplicationRegistry;
import org.apache.qpid.server.registry.IApplicationRegistry;
import org.apache.qpid.server.virtualhost.VirtualHost;
@@ -62,7 +63,7 @@ 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 = new AMQQueue(new AMQShortString("testQueueForManagement"), false, null, false, _virtualHost);
+ AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueueForManagement"), false, null, false, _virtualHost);
AMQManagedObject mbean = new AMQQueueMBean(queue);
MBeanInfo mbeanInfo = mbean.getMBeanInfo();
@@ -82,7 +83,7 @@ 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
- DestNameExchange exchange = new DestNameExchange();
+ DirectExchange exchange = new DirectExchange();
exchange.initialise(_virtualHost, ExchangeDefaults.DIRECT_EXCHANGE_NAME, false, 0, true);
AMQManagedObject mbean = (AMQManagedObject)exchange.getManagedObject();
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 77cc12df7c..600ae3f7be 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,8 @@ 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.AMQMessage;
+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.junit.extensions.util.SizeOf;
@@ -191,7 +192,7 @@ public class DiagnosticExchange extends AbstractExchange
return false;
}
- public void route(AMQMessage payload) throws AMQException
+ public void route(IncomingMessage payload) throws AMQException
{
Long value = new Long(SizeOf.getUsedMemory());
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 f9904a76b9..7589a7974b 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,8 @@ 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.AMQMessage;
+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;
@@ -81,7 +82,7 @@ public class TestExchange implements Exchange
{
}
- public void route(AMQMessage message) throws AMQException
+ public void route(IncomingMessage message) throws AMQException
{
}
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 42d9cccb4f..144e4be6af 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
@@ -22,14 +22,15 @@ package org.apache.qpid.server.ack;
import junit.framework.TestCase;
import org.apache.qpid.AMQException;
-import org.apache.qpid.framing.BasicPublishBody;
import org.apache.qpid.framing.AMQShortString;
import org.apache.qpid.framing.ContentHeaderBody;
-import org.apache.qpid.framing.AMQFrameDecodingException;
import org.apache.qpid.framing.abstraction.MessagePublishInfo;
import org.apache.qpid.server.RequiredDeliveryException;
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.store.TestableMemoryMessageStore;
import org.apache.qpid.server.store.StoreContext;
import org.apache.qpid.server.txn.NonTransactionalContext;
@@ -99,12 +100,12 @@ public class TxAckTest extends TestCase
private final List<Long> _unacked;
private StoreContext _storeContext = new StoreContext();
- Scenario(int messageCount, List<Long> acked, List<Long> unacked)
+ Scenario(int messageCount, List<Long> acked, List<Long> unacked) throws AMQException
{
TransactionalContext txnContext = new NonTransactionalContext(new TestableMemoryMessageStore(),
_storeContext, null,
- new LinkedList<RequiredDeliveryException>(),
- new HashSet<Long>());
+ new LinkedList<RequiredDeliveryException>()
+ );
for (int i = 0; i < messageCount; i++)
{
long deliveryTag = i + 1;
@@ -138,8 +139,8 @@ public class TxAckTest extends TestCase
}
};
- TestMessage message = new TestMessage(deliveryTag, i, info, txnContext);
- _map.add(deliveryTag, new UnacknowledgedMessage(new QueueEntry(null,message), null, deliveryTag));
+ TestMessage message = new TestMessage(deliveryTag, i, info, txnContext.getStoreContext());
+ _map.add(deliveryTag, new QueueEntryImpl(null,message, Long.MIN_VALUE));
}
_acked = acked;
_unacked = unacked;
@@ -154,7 +155,7 @@ public class TxAckTest extends TestCase
{
for (long tag : tags)
{
- UnacknowledgedMessage u = _map.get(tag);
+ QueueEntry u = _map.get(tag);
assertTrue("Message not found for tag " + tag, u != null);
((TestMessage) u.getMessage()).assertCountEquals(expected);
}
@@ -195,31 +196,46 @@ public class TxAckTest extends TestCase
}
}
+ private static AMQMessageHandle createMessageHandle(final long messageId, final MessagePublishInfo publishBody)
+ {
+ final AMQMessageHandle amqMessageHandle = (new MessageHandleFactory()).createMessageHandle(messageId,
+ null,
+ false);
+ try
+ {
+ amqMessageHandle.setPublishAndContentHeaderBody(new StoreContext(),
+ publishBody,
+ new ContentHeaderBody()
+ {
+ public int getSize()
+ {
+ return 1;
+ }
+ });
+ }
+ catch (AMQException e)
+ {
+ // won't happen
+ }
+
+
+ return amqMessageHandle;
+ }
+
+
private class TestMessage extends AMQMessage
{
private final long _tag;
private int _count;
- TestMessage(long tag, long messageId, MessagePublishInfo publishBody, TransactionalContext txnContext)
+ TestMessage(long tag, long messageId, MessagePublishInfo publishBody, StoreContext storeContext)
+ throws AMQException
{
- super(messageId, publishBody, txnContext);
- try
- {
- setContentHeaderBody(new ContentHeaderBody()
- {
- public int getSize()
- {
- return 1;
- }
- });
- }
- catch (AMQException e)
- {
- // won't happen
- }
+ super(createMessageHandle(messageId, publishBody), storeContext, publishBody);
_tag = tag;
}
+
public void incrementReference()
{
_count++;
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 58323086b5..80470d44b3 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
@@ -24,10 +24,7 @@ import junit.framework.TestCase;
import org.apache.qpid.AMQException;
import org.apache.qpid.framing.*;
import org.apache.qpid.framing.abstraction.MessagePublishInfo;
-import org.apache.qpid.server.queue.AMQMessage;
-import org.apache.qpid.server.queue.AMQQueue;
-import org.apache.qpid.server.queue.MessageHandleFactory;
-import org.apache.qpid.server.queue.QueueEntry;
+import org.apache.qpid.server.queue.*;
import org.apache.qpid.server.registry.ApplicationRegistry;
import org.apache.qpid.server.store.MessageStore;
import org.apache.qpid.server.store.SkeletonMessageStore;
@@ -36,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.protocol.AMQProtocolSession;
import org.apache.log4j.Logger;
import java.util.*;
@@ -94,7 +92,11 @@ public class AbstractHeadersExchangeTestBase extends TestCase
protected void route(Message m) throws AMQException
{
m.route(exchange);
- m.routingComplete(_store, _storeContext, _handleFactory);
+ m.getIncomingMessage().routingComplete(_store, _handleFactory);
+ if(m.getIncomingMessage().allContentReceived())
+ {
+ m.getIncomingMessage().deliverToQueues();
+ }
}
protected void routeAndTest(Message m, TestQueue... expected) throws AMQException
@@ -122,12 +124,12 @@ public class AbstractHeadersExchangeTestBase extends TestCase
{
if (expected.contains(q))
{
- assertTrue("Expected " + m + " to be delivered to " + q, m.isInQueue(q));
+ assertTrue("Expected " + m + " to be delivered to " + q, q.isInQueue(m));
//assert m.isInQueue(q) : "Expected " + m + " to be delivered to " + q;
}
else
{
- assertFalse("Did not expect " + m + " to be delivered to " + q, m.isInQueue(q));
+ assertFalse("Did not expect " + m + " to be delivered to " + q, q.isInQueue(m));
//assert !m.isInQueue(q) : "Did not expect " + m + " to be delivered to " + q;
}
}
@@ -234,7 +236,7 @@ public class AbstractHeadersExchangeTestBase extends TestCase
return properties;
}
- static class TestQueue extends AMQQueue
+ static class TestQueue extends AMQQueueImpl
{
final List<HeadersExchangeTest.Message> messages = new ArrayList<HeadersExchangeTest.Message>();
@@ -253,8 +255,14 @@ public class AbstractHeadersExchangeTestBase extends TestCase
*/
public void process(StoreContext context, QueueEntry msg, boolean deliverFirst) throws AMQException
{
- messages.add(new HeadersExchangeTest.Message(msg.getMessage()));
+ messages.add( new HeadersExchangeTest.Message(msg.getMessage()));
+ }
+
+ boolean isInQueue(Message msg)
+ {
+ return messages.contains(msg);
}
+
}
/**
@@ -262,14 +270,48 @@ public class AbstractHeadersExchangeTestBase extends TestCase
*/
static class Message extends AMQMessage
{
+ private class TestIncomingMessage extends IncomingMessage
+ {
+
+ public TestIncomingMessage(final long messageId,
+ final MessagePublishInfo info,
+ final TransactionalContext txnContext,
+ final AMQProtocolSession publisher)
+ {
+ super(messageId, info, txnContext, publisher);
+ }
+
+
+ public AMQMessage getUnderlyingMessage()
+ {
+ return Message.this;
+ }
+
+
+ public ContentHeaderBody getContentHeaderBody()
+ {
+ try
+ {
+ return Message.this.getContentHeaderBody();
+ }
+ catch (AMQException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ private IncomingMessage _incoming;
+
private static MessageStore _messageStore = new SkeletonMessageStore();
private static StoreContext _storeContext = new StoreContext();
+
private static TransactionalContext _txnContext = new NonTransactionalContext(_messageStore, _storeContext,
null,
- new LinkedList<RequiredDeliveryException>(),
- new HashSet<Long>());
+ new LinkedList<RequiredDeliveryException>()
+ );
Message(String id, String... headers) throws AMQException
{
@@ -278,12 +320,47 @@ public class AbstractHeadersExchangeTestBase extends TestCase
Message(String id, FieldTable headers) throws AMQException
{
- this(getPublishRequest(id), getContentHeader(headers), null);
+ this(_messageStore.getNewMessageId(),getPublishRequest(id), getContentHeader(headers), null);
+ }
+
+ public IncomingMessage getIncomingMessage()
+ {
+ return _incoming;
+ }
+
+ private Message(long messageId,
+ MessagePublishInfo publish,
+ ContentHeaderBody header,
+ List<ContentBody> bodies) throws AMQException
+ {
+ super(createMessageHandle(messageId, publish, header), _txnContext.getStoreContext(), publish);
+
+
+
+ _incoming = new TestIncomingMessage(getMessageId(),publish,_txnContext,new MockProtocolSession(_messageStore));
+ _incoming.setContentHeaderBody(header);
+
+
}
- private Message(MessagePublishInfo publish, ContentHeaderBody header, List<ContentBody> bodies) throws AMQException
+ private static AMQMessageHandle createMessageHandle(final long messageId,
+ final MessagePublishInfo publish,
+ final ContentHeaderBody header)
{
- super(_messageStore.getNewMessageId(), publish, _txnContext, header);
+
+ final AMQMessageHandle amqMessageHandle = (new MessageHandleFactory()).createMessageHandle(messageId,
+ _messageStore,
+ true);
+
+ try
+ {
+ amqMessageHandle.setPublishAndContentHeaderBody(new StoreContext(),publish,header);
+ }
+ catch (AMQException e)
+ {
+
+ }
+ return amqMessageHandle;
}
private Message(AMQMessage msg) throws AMQException
@@ -291,15 +368,13 @@ public class AbstractHeadersExchangeTestBase extends TestCase
super(msg);
}
+
+
void route(Exchange exchange) throws AMQException
{
- exchange.route(this);
+ exchange.route(_incoming);
}
- boolean isInQueue(TestQueue queue)
- {
- return queue.messages.contains(this);
- }
public int hashCode()
{
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 5fbea5e14f..6f8963131b 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
@@ -28,6 +28,7 @@ import org.apache.qpid.AMQException;
import org.apache.qpid.codec.AMQCodecFactory;
import org.apache.qpid.framing.AMQShortString;
import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.queue.AMQQueueFactory;
import org.apache.qpid.server.queue.AMQQueue;
import org.apache.qpid.server.registry.ApplicationRegistry;
import org.apache.qpid.server.registry.IApplicationRegistry;
@@ -54,10 +55,12 @@ public class AMQProtocolSessionMBeanTest extends TestCase
// check the channel count is correct
int channelCount = _mbean.channels().size();
assertTrue(channelCount == 1);
- AMQQueue queue =
- new org.apache.qpid.server.queue.AMQQueue(new AMQShortString("testQueue_" + System.currentTimeMillis()), false,
- new AMQShortString("test"), true, _protocolSession.getVirtualHost());
- AMQChannel channel = new AMQChannel(_protocolSession, 2, _messageStore);
+ AMQQueue queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("testQueue_" + System.currentTimeMillis()),
+ false,
+ new AMQShortString("test"),
+ true,
+ _protocolSession.getVirtualHost());
+ AMQChannel channel = new AMQChannel(_protocolSession,2, _messageStore);
channel.setDefaultQueue(queue);
_protocolSession.addChannel(channel);
channelCount = _mbean.channels().size();
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 95ffb505fb..30240217a2 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
@@ -29,7 +29,9 @@ import org.apache.qpid.framing.AMQShortString;
import org.apache.qpid.framing.abstraction.MessagePublishInfo;
import org.apache.qpid.server.AMQChannel;
import org.apache.qpid.server.RequiredDeliveryException;
-import org.apache.qpid.server.ack.UnacknowledgedMessage;
+import org.apache.qpid.server.subscription.Subscription;
+import org.apache.qpid.server.subscription.SubscriptionFactoryImpl;
+import org.apache.qpid.server.flow.LimitlessCreditManager;
import org.apache.qpid.server.ack.UnacknowledgedMessageMap;
import org.apache.qpid.server.registry.ApplicationRegistry;
import org.apache.qpid.server.store.TestableMemoryMessageStore;
@@ -40,7 +42,6 @@ import org.apache.qpid.server.util.NullApplicationRegistry;
import java.util.LinkedList;
import java.util.Set;
-import java.util.HashSet;
/**
* Tests that acknowledgements are handled correctly.
@@ -49,7 +50,7 @@ public class AckTest extends TestCase
{
private static final Logger _log = Logger.getLogger(AckTest.class);
- private SubscriptionImpl _subscription;
+ private Subscription _subscription;
private MockProtocolSession _protocolSession;
@@ -57,9 +58,7 @@ public class AckTest extends TestCase
private StoreContext _storeContext = new StoreContext();
- private AMQChannel _channel;
-
- private SubscriptionSet _subscriptionManager;
+ private AMQChannel _channel;
private AMQQueue _queue;
@@ -78,8 +77,9 @@ public class AckTest extends TestCase
_channel = new AMQChannel(_protocolSession,5, _messageStore /*dont need exchange registry*/);
_protocolSession.addChannel(_channel);
- _subscriptionManager = new SubscriptionSet();
- _queue = new AMQQueue(new AMQShortString("myQ"), false, new AMQShortString("guest"), true, ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHost("test"), _subscriptionManager);
+
+ _queue = AMQQueueFactory.createAMQQueueImpl(new AMQShortString("myQ"), false, new AMQShortString("guest"), true, ApplicationRegistry.getInstance().getVirtualHostRegistry().getVirtualHost("test"));
+
}
private void publishMessages(int count) throws AMQException
@@ -90,8 +90,9 @@ public class AckTest extends TestCase
private void publishMessages(int count, boolean persistent) throws AMQException
{
TransactionalContext txnContext = new NonTransactionalContext(_messageStore, _storeContext, null,
- new LinkedList<RequiredDeliveryException>(),
- new HashSet<Long>());
+ new LinkedList<RequiredDeliveryException>()
+ );
+ _queue.registerSubscription(_subscription,false);
MessageHandleFactory factory = new MessageHandleFactory();
for (int i = 1; i <= count; i++)
{
@@ -125,7 +126,8 @@ public class AckTest extends TestCase
return new AMQShortString("rk");
}
};
- AMQMessage msg = new AMQMessage(_messageStore.getNewMessageId(), publishBody, txnContext);
+ IncomingMessage msg = new IncomingMessage(_messageStore.getNewMessageId(), publishBody, txnContext,_protocolSession);
+ //IncomingMessage msg2 = null;
if (persistent)
{
BasicContentHeaderProperties b = new BasicContentHeaderProperties();
@@ -142,10 +144,14 @@ public class AckTest extends TestCase
// we increment the reference here since we are not delivering the messaging to any queues, which is where
// the reference is normally incremented. The test is easier to construct if we have direct access to the
// subscription
- msg.incrementReference();
- msg.routingComplete(_messageStore, _storeContext, factory);
+ msg.enqueue(_queue);
+ msg.routingComplete(_messageStore, factory);
+ if(msg.allContentReceived())
+ {
+ msg.deliverToQueues();
+ }
// we manually send the message to the subscription
- _subscription.send(new QueueEntry(_queue,msg), _queue);
+ //_subscription.send(new QueueEntry(_queue,msg), _queue);
}
}
@@ -155,7 +161,7 @@ public class AckTest extends TestCase
*/
public void testAckChannelAssociationTest() throws AMQException
{
- _subscription = new SubscriptionImpl(5, _protocolSession, DEFAULT_CONSUMER_TAG, true);
+ _subscription = SubscriptionFactoryImpl.INSTANCE.createSubscription(5, _protocolSession, DEFAULT_CONSUMER_TAG, true, null, false, new LimitlessCreditManager());
final int msgCount = 10;
publishMessages(msgCount, true);
@@ -169,7 +175,7 @@ public class AckTest extends TestCase
{
assertTrue(deliveryTag == i);
i++;
- UnacknowledgedMessage unackedMsg = map.get(deliveryTag);
+ QueueEntry unackedMsg = map.get(deliveryTag);
assertTrue(unackedMsg.getQueue() == _queue);
}
@@ -183,7 +189,7 @@ public class AckTest extends TestCase
public void testNoAckMode() throws AMQException
{
// false arg means no acks expected
- _subscription = new SubscriptionImpl(5, _protocolSession, DEFAULT_CONSUMER_TAG, false);
+ _subscription = SubscriptionFactoryImpl.INSTANCE.createSubscription(5, _protocolSession, DEFAULT_CONSUMER_TAG, false, null, false, new LimitlessCreditManager());
final int msgCount = 10;
publishMessages(msgCount);
@@ -200,7 +206,7 @@ public class AckTest extends TestCase
public void testPersistentNoAckMode() throws AMQException
{
// false arg means no acks expected
- _subscription = new SubscriptionImpl(5, _protocolSession, DEFAULT_CONSUMER_TAG, false);
+ _subscription = SubscriptionFactoryImpl.INSTANCE.createSubscription(5, _protocolSession, DEFAULT_CONSUMER_TAG, false,null,false, new LimitlessCreditManager());
final int msgCount = 10;
publishMessages(msgCount, true);
@@ -217,7 +223,7 @@ public class AckTest extends TestCase
*/
public void testSingleAckReceivedTest() throws AMQException
{
- _subscription = new SubscriptionImpl(5, _protocolSession, DEFAULT_CONSUMER_TAG, true);
+ _subscription = SubscriptionFactoryImpl.INSTANCE.createSubscription(5, _protocolSession, DEFAULT_CONSUMER_TAG, true,null,false, new LimitlessCreditManager());
final int msgCount = 10;
publishMessages(msgCount);
@@ -230,7 +236,7 @@ public class AckTest extends TestCase
for (long deliveryTag : deliveryTagSet)
{
assertTrue(deliveryTag == i);
- UnacknowledgedMessage unackedMsg = map.get(deliveryTag);
+ QueueEntry unackedMsg = map.get(deliveryTag);
assertTrue(unackedMsg.getQueue() == _queue);
// 5 is the delivery tag of the message that *should* be removed
if (++i == 5)
@@ -246,7 +252,7 @@ public class AckTest extends TestCase
*/
public void testMultiAckReceivedTest() throws AMQException
{
- _subscription = new SubscriptionImpl(5, _protocolSession, DEFAULT_CONSUMER_TAG, true);
+ _subscription = SubscriptionFactoryImpl.INSTANCE.createSubscription(5, _protocolSession, DEFAULT_CONSUMER_TAG, true,null,false, new LimitlessCreditManager());
final int msgCount = 10;
publishMessages(msgCount);
@@ -259,7 +265,7 @@ public class AckTest extends TestCase
for (long deliveryTag : deliveryTagSet)
{
assertTrue(deliveryTag == i + 5);
- UnacknowledgedMessage unackedMsg = map.get(deliveryTag);
+ QueueEntry unackedMsg = map.get(deliveryTag);
assertTrue(unackedMsg.getQueue() == _queue);
++i;
}
@@ -270,7 +276,7 @@ public class AckTest extends TestCase
*/
public void testMultiAckAllReceivedTest() throws AMQException
{
- _subscription = new SubscriptionImpl(5, _protocolSession, DEFAULT_CONSUMER_TAG, true);
+ _subscription = SubscriptionFactoryImpl.INSTANCE.createSubscription(5, _protocolSession, DEFAULT_CONSUMER_TAG, true,null,false, new LimitlessCreditManager());
final int msgCount = 10;
publishMessages(msgCount);
@@ -283,18 +289,19 @@ public class AckTest extends TestCase
for (long deliveryTag : deliveryTagSet)
{
assertTrue(deliveryTag == i + 5);
- UnacknowledgedMessage unackedMsg = map.get(deliveryTag);
+ QueueEntry unackedMsg = map.get(deliveryTag);
assertTrue(unackedMsg.getQueue() == _queue);
++i;
}
}
+/*
public void testPrefetchHighLow() throws AMQException
{
int lowMark = 5;
int highMark = 10;
- _subscription = new SubscriptionImpl(5, _protocolSession, DEFAULT_CONSUMER_TAG, true);
+ _subscription = SubscriptionFactoryImpl.INSTANCE.createSubscription(5, _protocolSession, DEFAULT_CONSUMER_TAG, true,null,false, new LimitlessCreditManager());
_channel.setPrefetchLowMarkCount(lowMark);
_channel.setPrefetchHighMarkCount(highMark);
@@ -343,10 +350,12 @@ public class AckTest extends TestCase
assertTrue(map.size() == 0);
}
+*/
+/*
public void testPrefetch() throws AMQException
{
- _subscription = new SubscriptionImpl(5, _protocolSession, DEFAULT_CONSUMER_TAG, true);
- _channel.setPrefetchCount(5);
+ _subscription = SubscriptionFactoryImpl.INSTANCE.createSubscription(5, _protocolSession, DEFAULT_CONSUMER_TAG, true,null,false, new LimitlessCreditManager());
+ _channel.setMessageCredit(5);
assertTrue(_channel.getPrefetchCount() == 5);
@@ -371,6 +380,7 @@ public class AckTest extends TestCase
assertTrue(map.size() == 0);
}
+*/
public static junit.framework.Test suite()
{
return new junit.framework.TestSuite(AckTest.class);
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
index 282ad3ed5e..3ff691c792 100644
--- 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
@@ -26,6 +26,7 @@ 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;
@@ -48,8 +49,8 @@ public class ConcurrencyTestDisabled extends MessageTestHelper
private final Executor _executor = new OnCurrentThreadExecutor();
private final List<Thread> _threads = new ArrayList<Thread>();
- private final SubscriptionSet _subscriptionMgr = new SubscriptionSet();
- private final DeliveryManager _deliveryMgr;
+ private final SubscriptionSet _subscriptionMgr;
+ private final ConcurrentSelectorDeliveryManager _deliveryMgr;
private boolean isComplete;
private boolean failed;
@@ -60,8 +61,9 @@ public class ConcurrencyTestDisabled extends MessageTestHelper
IApplicationRegistry applicationRegistry = ApplicationRegistry.getInstance();
_virtualHost = applicationRegistry.getVirtualHostRegistry().getVirtualHost("test");
- _deliveryMgr = new ConcurrentSelectorDeliveryManager(_subscriptionMgr, new AMQQueue(new AMQShortString("myQ"), false, new AMQShortString("guest"), false,
+ _deliveryMgr = new ConcurrentSelectorDeliveryManager( new AMQQueueImpl(new AMQShortString("myQ"), false, new AMQShortString("guest"), false,
_virtualHost));
+ _subscriptionMgr = _deliveryMgr.getSubscribers();
}
public void testConcurrent1() throws InterruptedException, AMQException
diff --git a/java/systests/src/main/java/org/apache/qpid/server/queue/DeliveryManagerTest.java b/java/systests/src/main/java/org/apache/qpid/server/queue/DeliveryManagerTest.java
deleted file mode 100644
index b33259cfba..0000000000
--- a/java/systests/src/main/java/org/apache/qpid/server/queue/DeliveryManagerTest.java
+++ /dev/null
@@ -1,177 +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.handler.OnCurrentThreadExecutor;
-import org.apache.qpid.server.store.StoreContext;
-import org.apache.qpid.AMQException;
-import org.apache.qpid.framing.AMQShortString;
-
-import junit.framework.TestSuite;
-
-abstract public class DeliveryManagerTest extends MessageTestHelper
-{
- protected final SubscriptionSet _subscriptions = new SubscriptionSet();
- protected DeliveryManager _mgr;
- protected StoreContext _storeContext = new StoreContext();
- private static final AMQShortString DEFAULT_QUEUE_NAME = new AMQShortString("Me");
-
- public DeliveryManagerTest() throws Exception
- {
- }
-
- public void testStartInQueueingMode() throws AMQException
- {
- QueueEntry[] messages = new QueueEntry[10];
- for (int i = 0; i < messages.length; i++)
- {
- messages[i] = message();
- }
- int batch = messages.length / 2;
-
- for (int i = 0; i < batch; i++)
- {
- _mgr.deliver(_storeContext, DEFAULT_QUEUE_NAME, messages[i], false);
- }
-
- SubscriptionTestHelper s1 = new SubscriptionTestHelper("1");
- SubscriptionTestHelper s2 = new SubscriptionTestHelper("2");
- _subscriptions.addSubscriber(s1);
- _subscriptions.addSubscriber(s2);
-
- for (int i = batch; i < messages.length; i++)
- {
- _mgr.deliver(_storeContext, DEFAULT_QUEUE_NAME, messages[i], false);
- }
-
- assertTrue(s1.getMessages().isEmpty());
- assertTrue(s2.getMessages().isEmpty());
-
- _mgr.processAsync(new OnCurrentThreadExecutor());
-
- assertEquals(messages.length / 2, s1.getMessages().size());
- assertEquals(messages.length / 2, s2.getMessages().size());
-
- for (int i = 0; i < messages.length; i++)
- {
- if (i % 2 == 0)
- {
- assertTrue(s1.getMessages().get(i / 2) == messages[i]);
- }
- else
- {
- assertTrue(s2.getMessages().get(i / 2) == messages[i]);
- }
- }
- }
-
- public void testStartInDirectMode() throws AMQException
- {
- QueueEntry[] messages = new QueueEntry[10];
- for (int i = 0; i < messages.length; i++)
- {
- messages[i] = message();
- }
- int batch = messages.length / 2;
-
- SubscriptionTestHelper s1 = new SubscriptionTestHelper("1");
- _subscriptions.addSubscriber(s1);
-
- for (int i = 0; i < batch; i++)
- {
- _mgr.deliver(_storeContext, DEFAULT_QUEUE_NAME, messages[i], false);
- }
-
- assertEquals(batch, s1.getMessages().size());
- for (int i = 0; i < batch; i++)
- {
- assertTrue(messages[i] == s1.getMessages().get(i));
- }
- s1.getMessages().clear();
- assertEquals(0, s1.getMessages().size());
-
- s1.setSuspended(true);
- for (int i = batch; i < messages.length; i++)
- {
- _mgr.deliver(_storeContext, DEFAULT_QUEUE_NAME, messages[i], false);
- }
-
- _mgr.processAsync(new OnCurrentThreadExecutor());
- assertEquals(0, s1.getMessages().size());
- s1.setSuspended(false);
-
- _mgr.processAsync(new OnCurrentThreadExecutor());
- assertEquals(messages.length - batch, s1.getMessages().size());
-
- for (int i = batch; i < messages.length; i++)
- {
- assertTrue(messages[i] == s1.getMessages().get(i - batch));
- }
-
- }
-
- public void testNoConsumers() throws AMQException
- {
- try
- {
- QueueEntry msg = message(true);
- _mgr.deliver(_storeContext, DEFAULT_QUEUE_NAME, msg, false);
- msg.checkDeliveredToConsumer();
- fail("expected exception did not occur");
- }
- catch (NoConsumersException m)
- {
- // ok
- }
- catch (Exception e)
- {
- fail("expected NoConsumersException, got " + e);
- }
- }
-
- public void testNoActiveConsumers() throws AMQException
- {
- try
- {
- SubscriptionTestHelper s = new SubscriptionTestHelper("A");
- _subscriptions.addSubscriber(s);
- s.setSuspended(true);
- QueueEntry msg = message(true);
- _mgr.deliver(_storeContext, DEFAULT_QUEUE_NAME, msg, false);
- msg.checkDeliveredToConsumer();
- fail("expected exception did not occur");
- }
- catch (NoConsumersException m)
- {
- // ok
- }
- catch (Exception e)
- {
- fail("expected NoConsumersException, got " + e);
- }
- }
-
- public static junit.framework.Test suite()
- {
- TestSuite suite = new TestSuite();
- return suite;
- }
-}
diff --git a/java/systests/src/main/java/org/apache/qpid/server/queue/MessageTestHelper.java b/java/systests/src/main/java/org/apache/qpid/server/queue/MessageTestHelper.java
index 521bedeccd..b2a4216f8d 100644
--- a/java/systests/src/main/java/org/apache/qpid/server/queue/MessageTestHelper.java
+++ b/java/systests/src/main/java/org/apache/qpid/server/queue/MessageTestHelper.java
@@ -20,7 +20,6 @@
*/
package org.apache.qpid.server.queue;
-import org.apache.qpid.framing.BasicPublishBody;
import org.apache.qpid.framing.ContentHeaderBody;
import org.apache.qpid.framing.AMQShortString;
import org.apache.qpid.framing.abstraction.MessagePublishInfo;
@@ -28,7 +27,6 @@ import org.apache.qpid.server.store.MessageStore;
import org.apache.qpid.server.store.SkeletonMessageStore;
import org.apache.qpid.server.store.StoreContext;
import org.apache.qpid.server.registry.ApplicationRegistry;
-import org.apache.qpid.server.util.TestApplicationRegistry;
import org.apache.qpid.server.util.NullApplicationRegistry;
import org.apache.qpid.server.txn.TransactionalContext;
import org.apache.qpid.server.txn.NonTransactionalContext;
@@ -38,7 +36,6 @@ import org.apache.qpid.AMQException;
import junit.framework.TestCase;
import java.util.LinkedList;
-import java.util.HashSet;
class MessageTestHelper extends TestCase
{
@@ -47,20 +44,20 @@ class MessageTestHelper extends TestCase
private final StoreContext _storeContext = new StoreContext();
private final TransactionalContext _txnContext = new NonTransactionalContext(_messageStore, _storeContext, null,
- new LinkedList<RequiredDeliveryException>(),
- new HashSet<Long>());
+ new LinkedList<RequiredDeliveryException>()
+ );
MessageTestHelper() throws Exception
{
ApplicationRegistry.initialise(new NullApplicationRegistry());
}
- QueueEntry message() throws AMQException
+ QueueEntryImpl message() throws AMQException
{
return message(false);
}
- QueueEntry message(final boolean immediate) throws AMQException
+ QueueEntryImpl message(final boolean immediate) throws AMQException
{
MessagePublishInfo publish = new MessagePublishInfo()
{
@@ -90,9 +87,16 @@ class MessageTestHelper extends TestCase
return null;
}
};
-
- return new QueueEntry(null,new AMQMessage(_messageStore.getNewMessageId(), publish, _txnContext,
- new ContentHeaderBody()));
+
+ //public AMQMessage(Long messageId, AMQMessageHandle messageHandle , TransactionalContext txnConext, MessagePublishInfo info)
+ long messageId = _messageStore.getNewMessageId();
+ final AMQMessageHandle messageHandle =
+ (new MessageHandleFactory()).createMessageHandle(messageId, _messageStore, false);
+ messageHandle.setPublishAndContentHeaderBody(new StoreContext(),publish,new ContentHeaderBody());
+ AMQMessage msg = new AMQMessage(messageHandle, _txnContext.getStoreContext(), publish);
+
+
+ return new QueueEntryImpl(null,msg, Long.MIN_VALUE);
}
}
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
index d3ec3c11d4..1200d7fc2b 100644
--- 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
@@ -21,6 +21,7 @@
package org.apache.qpid.server.queue;
import junit.framework.TestCase;
+import org.apache.qpid.server.subscription.Subscription;
public class SubscriptionManagerTest extends TestCase
{
@@ -47,9 +48,9 @@ public class SubscriptionManagerTest extends TestCase
s1.setSuspended(true);
assertFalse(mgr.hasActiveSubscribers());
- mgr.removeSubscriber(new SubscriptionTestHelper("S1"));
+ mgr.removeSubscriber(s1);
assertFalse(mgr.isEmpty());
- mgr.removeSubscriber(new SubscriptionTestHelper("S2"));
+ mgr.removeSubscriber(s2);
assertTrue(mgr.isEmpty());
}
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 458b510ef5..624a368b1f 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
@@ -21,6 +21,8 @@
package org.apache.qpid.server.queue;
import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.subscription.Subscription;
+import org.apache.qpid.framing.AMQShortString;
import java.util.ArrayList;
import java.util.List;
@@ -54,7 +56,12 @@ public class SubscriptionTestHelper implements Subscription
return messages;
}
- public void send(QueueEntry msg, AMQQueue queue)
+ public void setQueue(AMQQueue queue)
+ {
+
+ }
+
+ public void send(QueueEntry msg)
{
messages.add(msg);
}
@@ -84,16 +91,56 @@ public class SubscriptionTestHelper implements Subscription
return new Object();
}
+ public void resend(final QueueEntry entry)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void restoreCredit(final QueueEntry queueEntry)
+ {
+
+ }
+
+ public void setStateListener(final StateListener listener)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public Object getQueueContext()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean setQueueContext(Object expected, Object newValue)
+ {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
public AMQChannel getChannel()
{
return null;
}
-
+
public void start()
{
//no-op
}
+ public AMQShortString getConumerTag()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean isActive()
+ {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public AMQQueue getQueue()
+ {
+ return null;
+ }
+
public void queueDeleted(AMQQueue queue)
{
}
@@ -108,6 +155,11 @@ public class SubscriptionTestHelper implements Subscription
return true;
}
+ public boolean isAutoClose()
+ {
+ return false;
+ }
+
public Queue<QueueEntry> getPreDeliveryQueue()
{
return null;
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 6ffa3e0e02..57bc173812 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
@@ -22,12 +22,12 @@ package org.apache.qpid.server.store;
import org.apache.commons.configuration.Configuration;
import org.apache.qpid.AMQException;
-import org.apache.qpid.framing.ContentBody;
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.AMQQueue;
+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;
import org.apache.qpid.server.exchange.Exchange;
diff --git a/java/systests/src/main/java/org/apache/qpid/server/store/TestReferenceCounting.java b/java/systests/src/main/java/org/apache/qpid/server/store/TestReferenceCounting.java
index 374c69fa00..f36e924890 100644
--- a/java/systests/src/main/java/org/apache/qpid/server/store/TestReferenceCounting.java
+++ b/java/systests/src/main/java/org/apache/qpid/server/store/TestReferenceCounting.java
@@ -23,12 +23,12 @@ package org.apache.qpid.server.store;
import junit.framework.TestCase;
import org.apache.qpid.AMQException;
import org.apache.qpid.framing.BasicContentHeaderProperties;
-import org.apache.qpid.framing.BasicPublishBody;
import org.apache.qpid.framing.ContentHeaderBody;
import org.apache.qpid.framing.AMQShortString;
import org.apache.qpid.framing.abstraction.MessagePublishInfo;
import org.apache.qpid.server.queue.AMQMessage;
import org.apache.qpid.server.queue.MessageHandleFactory;
+import org.apache.qpid.server.queue.AMQMessageHandle;
import org.apache.qpid.server.txn.NonTransactionalContext;
/**
@@ -40,6 +40,7 @@ public class TestReferenceCounting extends TestCase
private StoreContext _storeContext = new StoreContext();
+
protected void setUp() throws Exception
{
super.setUp();
@@ -51,7 +52,7 @@ public class TestReferenceCounting extends TestCase
*/
public void testMessageGetsRemoved() throws AMQException
{
- createPersistentContentHeader();
+ ContentHeaderBody chb = createPersistentContentHeader();
MessagePublishInfo info = new MessagePublishInfo()
{
@@ -82,16 +83,22 @@ public class TestReferenceCounting extends TestCase
}
};
- AMQMessage message = new AMQMessage(_store.getNewMessageId(), info,
- new NonTransactionalContext(_store, _storeContext, null, null, null),
- createPersistentContentHeader());
+
+ final long messageId = _store.getNewMessageId();
+ AMQMessageHandle messageHandle = (new MessageHandleFactory()).createMessageHandle(messageId, _store, true);
+ messageHandle.setPublishAndContentHeaderBody(_storeContext,info, chb);
+ AMQMessage message = new AMQMessage(messageHandle,
+ _storeContext,info);
+
message = message.takeReference();
// we call routing complete to set up the handle
- message.routingComplete(_store, _storeContext, new MessageHandleFactory());
- assertTrue(_store.getMessageMetaDataMap().size() == 1);
+ // message.routingComplete(_store, _storeContext, new MessageHandleFactory());
+
+
+ assertEquals(1, _store.getMessageMetaDataMap().size());
message.decrementReference(_storeContext);
- assertTrue(_store.getMessageMetaDataMap().size() == 0);
+ assertEquals(1, _store.getMessageMetaDataMap().size());
}
private ContentHeaderBody createPersistentContentHeader()
@@ -135,18 +142,25 @@ public class TestReferenceCounting extends TestCase
}
};
- AMQMessage message = new AMQMessage(_store.getNewMessageId(),
- info,
- new NonTransactionalContext(_store, _storeContext, null, null, null),
- createPersistentContentHeader());
+ final Long messageId = _store.getNewMessageId();
+ final ContentHeaderBody chb = createPersistentContentHeader();
+ AMQMessageHandle messageHandle = (new MessageHandleFactory()).createMessageHandle(messageId, _store, true);
+ messageHandle.setPublishAndContentHeaderBody(_storeContext,info,chb);
+ AMQMessage message = new AMQMessage(messageHandle,
+ _storeContext,
+ info);
+
message = message.takeReference();
// we call routing complete to set up the handle
- message.routingComplete(_store, _storeContext, new MessageHandleFactory());
- assertTrue(_store.getMessageMetaDataMap().size() == 1);
+ // message.routingComplete(_store, _storeContext, new MessageHandleFactory());
+
+
+
+ assertEquals(1, _store.getMessageMetaDataMap().size());
message = message.takeReference();
message.decrementReference(_storeContext);
- assertTrue(_store.getMessageMetaDataMap().size() == 1);
+ assertEquals(1, _store.getMessageMetaDataMap().size());
}
public static junit.framework.Test suite()
diff --git a/java/systests/src/main/java/org/apache/qpid/test/client/DupsOkTest.java b/java/systests/src/main/java/org/apache/qpid/test/client/DupsOkTest.java
index 463946e14a..4a2de976da 100644
--- a/java/systests/src/main/java/org/apache/qpid/test/client/DupsOkTest.java
+++ b/java/systests/src/main/java/org/apache/qpid/test/client/DupsOkTest.java
@@ -108,11 +108,16 @@ public class DupsOkTest extends VMTestCase
{
try
{
+ System.err.println("Got last message!");
long remainingMessages = ((AMQSession) clientSession).getQueueDepth((AMQDestination) _queue);
- if(remainingMessages != 0)
+ if(_msgCount != MSG_COUNT)
{
- assertEquals("The queue should have 0 msgs left, seen " + _msgCount + " messages.", 0, getMessageCount(_queue.getQueueName()));
+ assertEquals("Wrong number of messages seen.", MSG_COUNT, _msgCount);
+ }
+ else
+ {
+ System.err.println("0 remaining on queue");
}
}
catch (AMQException e)
@@ -151,6 +156,10 @@ public class DupsOkTest extends VMTestCase
// consumer.close();
+ // wait for the ack to get back
+ Thread.sleep(1000);
+
+
assertEquals("The queue should have 0 msgs left", 0, ((AMQSession) clientSession).getQueueDepth((AMQDestination) _queue));
clientConnection.close();